Post

PWN

Methodology

  1. Usually need to start with reversing methodology
  2. Check protections
    • checksec [binary]
  3. Test for overflows everywhere
    • cyclic [num_bytes] && cyclic -l [4_byte_val]
  4. Other vulnerabilities
    • Format strings, integer overflow/underflow, command injection, TOCTOU, deserialization, UAF/double free, etc.

ELF specific (kind of)

  1. Check imports, specifically looking for dangerous functions and user input locations
  2. Examine dangerous functions to look for vulns
  3. Work backwards from a dangerous function to an input
  4. Look for OOB accesses (stack, heap, etc…)

Scripts

Tools

Practice

Links

Topics

Shellcode

1
2
3
4
5
6
7
8
9
10
from pwn import *
import sys

context.arch = "amd64"
assembly = shellcraft.echo("Hello world!\n")
asm("mov eax, 0")
disasm(b"\xb8\x0b\x00\x00\x00")
shellcode = asm(assembly)
r = gdb.debug_shellcode(shellcode)
r.recvline()

*nix/ELF

Protections

  • checksec [file]
  • Nothing: stack overflow, point to shellcode on stack with known address
  • NX stack: Ret2LIBC, put shellcode somewhere else to execute, or ROP
  • NX/DEP: ROP
  • ASLR: leak an address, partial overwrite, or spray
    • Changes the position of stack, heap, library (but not text or PLT)
    • ASLR but no NX means you need to find a gadget that points to the stack
    • GDB gets rid of PIE and ASLR, fix with set disable-randomization off
  • RELRO: partial doesn’t do much, full marks the GOT as read-only and moves GOT before Heap
    • Can’t overwrite pointers, but you can leak libc base address for ROP
    • Full RELRO
  • Canaries: leak, guess, or circumvent (longjmp) the canary
  • PIE: the main executable and data are randomized by section, but not the internals of each section

Ret2LIBC

  1. in gdb p system
  2. get address of “/bin/sh”
  3. BUFFER + SYSTEM_ADDR + JUNK + BINSH_ADDR

Ret2DLResolve

Command Injection

  • Bash escapes: “` ;&”

GOT/PLT

  • When a dynamically linked function is first called, it is a pointer to the PLT
  • This address is a jmp instruction to the .got.plt section
  • However, the first time what actually happens is that the jump value is updated by looking at the .so
  • Partial explanation
  • Full explanation

Heap

  • Overwrite heap chunk metadata or other items on heap
  • Look for mismatch in allocations/deallocations
  • How2Heap
  • Figure out available exploitation methods
  • Typical heap challenge bugs:
    • Buffer overflow in alloc
    • Double free
    • Pointers not set to null after free
    • Ability to edit OOB of allocation
    • Ability to leak pointers via show function
    • UAF in edit/show functions
    • Able to print pointers via non null-terminated alloc

ptmalloc2

Heap challenge checks

  • Are heap chunks null-byte filled on allocation? de-allocation?
  • Are pointers to chunks set to NULL once de-allocated?
  • Based on available allocation sizes, what type of chunk are we getting? Which bin? mmap?
  • Are we allowed to create? edit? delete? what restrictions exist on those operations?
  • UAF? Double free? OOB read or write?
  • Are there any memory leaks? (memory does not get freed when reference is lost)
  • Is allocation return value checked for failure?

PwnDbg

  • heap/bins
  • search
  • x/xg $rsp

UAF

  • Still have a pointer to the heap
  • Hopefully get to write new data there that can be accessed

Double Free

  • Linked list of free chunks will get the same pointer twice, which can be allocated to two different malloc calls

Windows

  • Process layout
  • “Syscall numbers tend to change from version to version of Windows and would be hard or unreliable to code into an exploit”
  • TEB -> FS[0] (also the TIB because that is first in the TEB)
  • pykd for windbg scripting (.load pykd;!py script.py)

Protections

  • DEP
  • ASLR
  • CFG
  • SafeSEH
  • SEHOP

SEH

  • There is a default exception handler installed, and new ones are added inside of each “try” block
    • A pointer to the handler is stored on the stack in the _EXCEPTION_REGISTRATION_RECORD structure
  • Exception handlers are stored on stack in singularly-linked list

Interesting C functions

C++

Java

Python

Ideas

  • Run shellcode on stack, or somewhere else
  • ROP through binary, or library, text section, awesome if you can call system()
  • Create a loop to accept input multiple times if needed
  • one_gadget
  • Create RWX memory region with mmap
  • Look in the GOT for LIBC addresses, may be able to print using some printf/puts call, use a gadget to pop a stack address into rdi (x86_64 only) and then return to just before the printf call (Example)
  • Overwrite something in the GOT
  • Overwrite DTORS and C Library hooks
    • __exit_funcs is replacement to __free_hook (Writeup)
  • Use the BSS to write temp data to at known address, if you have write-what-where
  • Time-based <- also helpful for using a filename as an oracle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# heap/__exit_funcs code

def deobfuscate(val):
    mask = 0xfff << 52
    while mask:
        v = val & mask
        val ^= (v >> 12)
        mask >>= 12
    return val

rol = lambda val, r_bits, max_bits: \
    (val << r_bits%max_bits) & (2**max_bits-1) | \
    ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

ror = lambda val, r_bits, max_bits: \
    ((val & (2**max_bits-1)) >> r_bits%max_bits) | \
    (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))


# encrypt a function pointer
def encrypt(v, key):
    return p64(rol(v ^ key, 0x11, 64))

# obtain the key
key = ror(u64(read(orig_onexit_addr, 8)), 0x11, 64) ^ orig_handler

log.info(f'sanity check: {hex(u64(encrypt(orig_handler, key)))}')

# Next, we need to allocate our exit_function_list, we're using the cxa type because it is called as func(void *arg, int status)
# This is nice to obtain system(void *arg) where arg is a pointer to "/bin/sh"
# We already allocated the '/bin/sh' at offset 0x2c0
#############  next | count  | type (cxa) | addr                             | arg               | not used
onexit_fun = p64(0) + p64(1) + p64(4)     + encrypt(libc.sym['system'], key) + p64(heap + 0x2c0) + p64(0)
add(onexit_fun)

ROP

  • Execve example shows setting up registers
  • ropper and ROPgadget
  • Stack pivoting, check ESP gadgets
  • ROP too easy? Try JOP
  • pwntools: elf.got['func'] for overwrite, elf.sym['func'] used to call
  • libc.so and ld.so are consistently spaced in memory, because of mmap relativitey, which means other mmap allocations (or large mallocs) also have that property
  • Setting rax without pop rax, call alarm(rax_val) 2x quickly to do mov rax, rdi
  • Arbitrary write in GCC-compiled binary, look for: add dword [rbp-0x3d], ebx
  • More common gcc gadgets (stolen from nobodyisnobody)
    1
    2
    3
    
    gadget_add = 0x000000000040125c # add dword ptr [rbp - 0x3d], ebx ; nop ; ret
    gadget_csu = 0x000000000040156a # pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
    gadget_csu2 = 0x401550 # mov rdx,r14  /  mov rsi,r13 / mov edi,r12d  / call qword[r15+rbx*8] + 7 pop
    

Format Strings

  1. See if there is anything useful on the stack with "|%p" * x
  2. If there isn’t full relro, try "AAAABBBB" + "|%p" * x to find your input
  3. Count the index of your input and add 1, do p32([GOT_ADDR]) + "%[index+1]$p" to print your input
  4. Get value you want and print that number of characters, do p32([GOT_ADDR]) + "%[num]x" + "%[index+1]$hn"
  5. Fix num because it will be a little off, if you need to do more writes they can be combined ex r.sendline(p32(0x804a010) + p32(0x804a012) + "%33880x" + "%8$hn" + "%33700x" + "%9$hn")
  • May need to call system PLT address instead of call to system in text section
  • %[number]x is the pad
  • %[index]$n means write to that index on the stack (what that points to)
  • PWNTools fmtstr

Integer Overflow/Underflow

Type Confusion

Uninitialized Data

  • If not initialized, variables will just get whatever data happens to be there

Seccomp

So you got a LIBC

  • LIBC Finder
  • Use pwninit to get loader for older libc (https://github.com/io12/pwninit/releases/latest)
    • /pwninit --bin [binary] --no-template
  • If given LIBC, probably need to leak an address to it (lots of good notes in PWN-Tips repo)
  • Another source for ROP gadgets
  • PWNtools examples:
    • p = process(["./ld-2.27.so", "./task2"], env={"LD_LIBRARY_PATH":".", "LD_PRELOAD":"libc-2.27.so"})
    • env = {"LD_PRELOAD": os.path.join(os.getcwd(), "libcrypto.so.1.0.0")}
  • patchelf --set-rpath . [binary] # requires your custom libc.so.6 to be in the same directory: patchelf --set-interpreter [ld.so] [binary]
  • LD_LIBRARY_PATH=. LD_DEBUG=all LD_PRELOAD=./libc.so.6 ./ld-2.29.so ./binary if given .so, the debug thing can be helpful
  • Can also do objdump -D, look for libc_start_main(), look for call rax before exit() (+2 because it pushes ret addr)
  • ASLR will cause the addresses of the functions to change, but not their offsets
  • LIBC Database/Another one
  • Use pwntools to find the base from a symbol
    • libc = ELF("whatever.so"); libcbase = puts_addr - libc.sym['puts']; system = libcbase + libc.sym['system']; binsh = libcbase + libc.search('/bin/sh').next()

Random troubleshooting

  • try popping address of system into register and then calling that register instead of just jumping to system
  • (echo "";cat) | nc [server] [port]
  • cat payload - | nc [server] [port]
  • to get LIBC address with UAF, allocate 9 0x80 (fastbin size) chunks, free 8 and look in 8th chunk for pointer
  • mmap “feature”: POSIX specifies that the system shall always zero fill any partial page at the end of the object and that system will never write any modification of the object beyond its end. On Linux, when you write data to such partial page after the end of the object, the data stays in the page cache even after the file is closed and unmapped and even though the data is never written to the file itself, subsequent mappings may see the modified content.
  • If a chunk’s IS_MMAPED flag is set, calloc will not zero out the chunk’s user data after allocation because it assumes the chunk has been allocated via mmap() which also zeroes out data

Jail escapes

This post is licensed under CC BY 4.0 by the author.