owned this note
owned this note
Published
Linked with GitHub
{%hackmd yf0ZwG0ERVmC3ZRUSC6CBw %}
[TOC]
# NTU Computer Security Fall 2019
## Binary Exploitatiion
### Binary exploitation
- What is PWN
- 透過利用程式(Binary)的漏洞(Vulnerability),在執行期間控制執行流程(Control flow),進而使程式執行特定行為
### Basic concepts
- ELF(Executable and Linkable Format)
- 執行檔
- Example
- exe
- Section
- 執行時會mapping到RAM上(Virtual memory)
- .text, .bss, .data, .rodata, .got, .plt, .fini, etc.
- Workflow(Static)
- 
- Workflow(dynamic linking)
- 
- Section
- .bss
- 存放未初始化值的全域變數(global variable)
- .data
- 存放具初始化值的全域變數
- .rodata
- 存放唯讀(read-only)資料
- .text
- 存放編譯後的code
- 
- Protections
- PIE
- Position-Independent Executable
- NX
- No-eXecute
- Canary
- Stack protector
- RELRO
- Relocation Read-Only
- x64
- 8 bytes alignment
- Stack 0x10 bytes alignment
- Assembly
- Registers
- rax, rbx, rcx, rdx, rdi, rsi
- 64 bits
- eax, ebx, ecx, edx, edi, esi
- 32 bits
- ax, bx, cx, dx, di, si
- 16 bits
- ax -> ah, al
- 8 bits
- RSP
- Stack pointer register
- 指向stack頂端(頭)
- RBP
- Base pointer register
- 指向stack底端(尾)
- RIP
- Program counter register
- 指向當前執行指令instruction位置
- jmp
- 跳至程式某一地址address執行
- call
- 將call完後回來緊接著要執行的下一行指令位置push到stack上儲存起來,再跳過去執行
- leave
- 還原至caller的stack frame
- mov rsp, rbp
- pop rbp
- ret(return)
- Calling convention
- Pass parameters
- rdi, rsi, rdx, rcx, r8, r9, (stack)
- rdi, rsi, rdx, r10, r8, r9, (stack)
- rax: store return value
- x64: register傳參數
- x86: stack傳參數
- Stack frame
- Function prologue
- Function epilogue
- Stack frame
- local variables
- \[rbp\] = old rbp(caller rbp)
- \[rbp + 0x8\] = return address
### Overflow
- Buffer overflow
- Stack overflow
- Heap overflow
- 覆蓋到理論上不應該被修改到的資料
- Important data, Secret
- Return address
### BOF - Buffer Overflow
- Local variables
- Data on stack
- gets(buf)
- 不會檢查輸入長度
- 可以覆蓋stack上的資料
- 改變return address、control flow等
### Canary
- Function prologue時在stack上放置程式執行時隨機生成的8 bytes在saved rbp前
- 第一個byte為null byte
- Function epilogue時會拿儲存在另一segment的值檢查canary值是否相同來檢測是否發生overflow
- 若相同才正常return
- 否則直接終止程式(abort)
- 每次執行canary不同,同一次的canary固定
### Shellcode
- Write assembly code and use assembler to turn to machine code
#### Linux Syscall
- System call
- 跟kernel做溝通的interface
- instruction
- syscall
- rax
- syscall number
- Arguments
- rdi, rsi, rdx, r10, r8, r9
- Return value
- rax
Shellcode: execve
- execve
- int execve(const char \*pathname, char \*const argv\[\], char \*const envp\[\])
- Spawn a shell
- execve("/bin/sh", NULL, NULL)
- 
### NX
- No-execute
- Data segment不應具有執行權限
- stack, heap
- rw-
- Code segment具執行權限,但不具寫入權限
- r-x
- 
- seccomp
### ASLR
- Address Space Layout Randomizaion
- Kernel的保護機制
- 每次動態載入時,base都是隨機的
- library
- stack
- heap
### PIE
- Position-Independent Executable
- 可以看成是ELF code & data section map到virtual address時的ASLR
- PIE開啟時,每次執行程式code base都會不同,否則固定0x400000
- 紀錄在ELF file中
### Lazy Binding
- Dynamic linking的程式,有些使用到的library function可能因執行流程到結束都不會被執行到
- Lazy binding機制為當程式第一次呼叫library function時,才會去尋找libc function的位置(function address)進行binding,並填入GOT表中,後續呼叫此function則直接從GOT表中獲取位置
#### GOT
- Global Offset Table
- Library的位置在載入時才決定,compiler在編譯時期亦無法得知執行時期的library function address
- GOT為儲存library function位置的指標陣列,而lazy binding的機制,一開始不會得知真實位置,而是先填入位於plt的code
### GOT Hijacking
- 因為Lazy binding的機制,GOT為可寫區域
- 假設程式有漏洞可以造成對GOT做寫入覆蓋其值,下一次呼叫對應的library function時則可以從中劫持,任意控制將要執行的function pointer
### ROP(Return Oriented Programming Attack)
#### ROP gadgets
- 片段可執行的code
- 結尾是ret instruction
- call, jmp等任何可以繼續控制流程的方式
- How to find the gadgets
- https://github.com/JonathanSalwan/ROPgadget
- https://github.com/sashs/Ropper
- 手動找
ROP:
- NX: 見招拆招
- 在既有的執行區域(code segment)尋找gadgets,運用這些gadgets疊成一長串的return address chain(ROP chain)
- 透過許多片段執行行為的gadget,來串出任意代碼執行,藉此繞過NX保護機制的限制
- Function return時,會拿走第一個return address,此時rsp指在第二個return address,並跳至第一個return address執行,接著會執行到ret instruction
- 而第二個return address也在開始時放置好,做到繼續control flow,如此反覆來達到Return Oriented Porgramming形式的攻擊
#### Control Register
- Gadget
- pop (reg);
- ret
- 因為byte的數量,因此找到的機會比較搭
ROP:
- 常常不會那麼剛好有想要的gadget且是ret結尾
- 想辦法組合gadget,達成同樣的目的
- 存在overflow,或任何成功control rip的前提下
- NX關的情況,可以撰寫執行`execve("/bin/sh", 0, 0)`的shellcode並嘗試控制rip跳至shellcode
- NX開的情況,可以透過ROP的方式,堆疊出執行`execve("/bin/sh", 0, 0)`行為的ROP chain
### ret2plt(return to .plt)
- 如果想要串的行為本身就有function在binary中,可以直接用ROP放好參數使用它
- 不用用ROP去堆全部的行為
- 在PIE關的情況下,即使不知道library function address(因ASLR),也可以透過return到.plt上使用此function
### ret2libc(return to libc)
- 若能得知library被map到的隨機起始地址(base address),則可以計算出libc中function的位置,便能調用library中的函式
- 關鍵為bypass ASLR,找出libc的隨機base
- 透過information leak漏洞,洩漏memory上的內容,獲取屬於libc segment的address
- 此address會是隨機的base address加上一固定位移值offset
- 不同版本的libc offset不同
- leaked_address - offset = base_address
- 有base後繼可以透過加上offset的方式得知其他function的address來call它,結合ROP等等
#### Information leak
- Compiler開空間時,Stack上面的值不會清乾淨
- 可以leak出來
- 透過剛好input到沒乾淨的值的前面,可以將殘留的值leak出來
- libc的memory
### Stack pivoting(Stack migration)
- ROP需要很多空間放置長串的ROP chain,有時並不會有那麼多空間,可能很短甚至只能控第一次rip
- 若找到其他地方有足夠空間放置ROP payload,此時可以透過運用較少的gadget數,將stack搬移到ROP payload的位置,再進行ROP
- 很多做法
- leave; ret
- Overflow時將rbp填成ROP Chain的address - 8
- Return address填leave; ret gadget
- pop rsp; ret
- 手動找針對各種當下情況的gadget
### ret2csu(return to _\_libc\_csu_init)
- 位於`__libc_csu_init`函式中,為compiler編進去的function
- 尾部有一片段code,很適合拿來控制register放置參數,以及control flow
- 
- 透過控制rbp, rbx, r12, r13, r14, r15 registers的值,跳至Gadget開頭,r13, r14, r15分別放置前三個參數rdi, rsi, rdx
- 此部分解決很少找到pop rdx gadget的問題
- ROP很難控制第三個參數
- 控制r12, rbx來指定任意記憶體位置call \[r12+rbx\*8\]
- 將rbx設為0,將rbp設為1,在call完後使rbx == rbp == 1,jne不會take,而繼續執行後面的連續pop register,如此可重複使用,達到任意ROP
## Heap Exploitation
### Heap intro
- 在打heap時,需要先知道library的版本
- `ldd --version`
- ptmalloc2
- glibc
- tcmalloc: google
- jemalloc
- etc.
- malloc
- 要用多少分配多少
- 提升記憶體分配效率及避免記憶體空間的浪費
- `void *ptr = malloc(size)`
- Workflow
- 
- main arena
- 一開始malloc < 128 KB,透過brk,kernel會給132KB的heap segment(rw)
- main arena
- 對kernel來說已經被使用,對binary來說還沒拿到
- 會交給glibc管理
- 
- ASLR: heap base
- Chunk
- glibc實作記憶體管理機制的資料結構(data structure)
- malloc拿到的一塊記憶體
- Allocated chunk
- Free chunk
- Top chunk
- Size alignment
- 0x10的倍數,malloc 0x18會拿到0x20的大小
- 整個chunk占記憶體大小為header(0x10) + data
- 
#### Chunk header
- Allocated chunk
- prev_size
- 連續記憶體上一塊若是free chunk,則記錄該size
- 若是allocated chunk則同時為它的data
- size
- Chunk size with 3 flags
- PREV_INUSE(P)
- 上一個chunk是否使用中
- IS_MMAPED(M)
- Chunk是否透過mmap出來的
- NON_MAIN_ARENA(N)
- 該chunk是否不屬於main arena
- 
- Pointer會指在user data
- Free chunk
- prev_size
- 連續記憶體上一塊若是free chunk,則記錄該size
- 若是allocated chunk則為它的data
- size
- 連續記憶體下一塊的P flag為0
- fd
- 指向同一bin中的前一塊chunk(linked list)
- bk
- 指向同一bin中的後一塊chunk(linked list)
- 
- Top chunk
- 第一次malloc後,剩下的空間為top chunk,分配空間時視情況從top chunk切割分配
- free Top chunk連續記憶體上一塊chunk時,若不是fastbin則會與Top chunk merge,top chunk P恆為1
- 
### bins
- 回收free chunk的資料結構
- 主要依據size大小分為
- fast bin
- small bin
- large bin
- unsorted bin
- Fast bin
- Size < 0x90 bytes
- bin中依據size劃分為0x20, 0x30, 0x40, ...
- global_max_fast = 0x80
- Singly linked list
- fd指向前一個,bk沒用到
- LIFO(Last in, First out)
- Free時不會將下一塊chunk P flag設成0
- Small bin
- Circular doubly linked list
- 依據size劃分為62個bin
- 0x20, 0x30, ..., 0x3f0
- 0x20 ~ 0x80的大小與fast bin重疊,會根據機置放到fast bin或small bin
- FIFO(First in, First out)
- Free掉時會將下一塊chunk P設為0
- Large bin
- Circular doubly linked list
- 依據大小遞減排序
- size >= 1024 bytes(0x400)
- 63 bins
- 細節可以參考source code
- header
- fd_nextsize
- bk_nextsize
- 
- Unsorted bin
- Circular doubly linked list
- free的chunk size大於fast bin時,不會直接放到對應的bin哩,會先丟到unsorted bin中
- malloc fast bin size大小時會先去fast bin list裡找,若沒有則會至unsorted bin找
- 如找到一樣大小則回傳
- 若無但找到大小大於所需大小的chunk則切割回傳
- 剩下的部分會丟回unsorted bin
- 若都沒有則從top chunk切出來回傳
### Vulnerability
#### Heap overflow
- 在heap segment發生的overflow
- 和stack overflow類似
- Stack overflow目標是掌控stack上可利用資訊
- return address等
- Heap overflow是掌握heap中的利用目標
- 某個chunk分配來是一個object struct,透過heap overflow overwrite進行偽造
- 控制chunk header,並結合glibc malloc free的記憶體管理機制,做到進一步利用
#### UAF(Use after free)
- free(ptr)
- free完pointer後未將ptr清空(ptr=NULL),稱為dangling pointer
- 有一個pointer指著一塊已經被釋放的記憶體
- 根據不同的存取方式,有各種利用方法,可以進一步去做後續exploit的利用
- 用來information leak,存取殘留的data
- Struct Type Confusion
- Double free就是因為存在dangling pointer所以造成free一塊已經釋放的chunk,一樣可以透過一些技巧達到進一步的利用
- Information leak
- free兩個同size的fastbin,fd指向heap,如果存在UAF,將此chunk user data印出來或任何方法得知其值,透過印出fd來leak出heap address
- malloc一塊非fast bin size的chunk,free掉它使它被放入unsorted bin中,或任何製造出unsorted bin的方法,unsorted bin的fd與bk會是一個libc address
- 直接UAF印出fd來leak出libc address,或是malloc拿回這塊chunk,印出殘留的fd等
### Heap exploitation
#### Fastbin attack
- fastbin在檢查double free時,只檢查現在linked list第一個chunk是否等於現在即將要free掉的chunk
- 若存在double free,則可以透過`free(A) free(B) free(A)`的方式繞過此檢查
- 下次再malloc此fastbin size時,會拿到chunk A,而同時chunk A依舊存在於free chunk linked list中,藉此寫入data時修改掉fd,接下來連續malloc兩次後,第三次則會回傳拿到我們偽造的address
- Constraints
- malloc時會檢查chunk size正不正確
- 在目標地址+0x8的地方偽造size
- 尋找附近存在正確size之目標位置
- 增加可行性的利用技巧
- address alignment weakness
- libc address hardcode
- 檢查size時是抓取4 bytes int
#### Hooks
- glibc中存在許多function hooks,在攻擊時如果能達到arbitrary write或任意寫,hooks會是一個很好的寫入目標,來做到control flow
- `__malloc_hook`
- `__free_hook`
- `__realloc_hook`
- etc.
- 在執行該function時,發現該function hoook有值,則當作function pointer跳上去執行
- 結合fastbin attack拿到位於hooks附近位置的fake chunk,來overwrite hooks的值
#### One gadget
- 跳過去即執行`execve("/bin/sh", argv[], envp[])`(開shell)
- magic gadget
- 有一些前提(constraints)要滿足
- 常搭配hooks使用
- https://github.com/david942j/one_gadget
- 補充
- 如果malloc一塊unsorted bin大小的空間,free之後假如那空間的後面連著top chunk,會被merge
#### Tcache(per-thread cache)
- glibc >= 2.26
- Ubuntu 17.10後
- 新的機制,提升performance
- 第一次malloc後,會先分配一塊記憶體,存放`tcache_perthread_struct`
- 一個thread一個`tcache_perthread_struct`
- 
- 根據size分為不同大小的tcache
- smallbin大小範圍的chunk都會使用tcache
- 以fastbin來說,free時不會直接放到fastbin裡,而是放到對應的size的Tcache中,當滿7個時,再free才會放至fastbin中
- fastbin的fd是指向整個chunk的頭(Header),而tcache fd則是指向user data
- malloc時優先從tcache取出,tcache為空才會從原本的bin中開始找
- 若tcache為空,而原本的bin中有剛好大小的chunk時,會從bin中填補至tcache中直到填滿,再從tcache中取出
- tcache中的順序會與bin中相反
- 對於fastbin來說,會先將bin中第一塊取出,才將後面做填補
- 提升效能,而降低了安全性
- 沒有檢查double free
- malloc時沒有檢查size是否合法
- 不需要偽造chunk、偽造size,就能拿到任意記憶體的位置
- 有許多進階玩法與技巧
- 透過漏洞掌握整個`tcache_perthread_struct`等
### Some Heap Technique
#### Heap Overlap
- 可能一開始漏洞無法直接性做到太多事情
- 透過各式偽造chunk size的方式,以及玩弄malloc free的流程,使得不同的chunk發生重疊(heap overlap)的情形
- 假設chunk A的data與chunk B的不可寫部分重疊
- chunk B可能是一個struct,亦或是function pointer,則可以透過chunk A來偽造,overwrite data pointer達到任意讀寫,overwrite function pointer則可以hijack control flow
- 可以更進一步偽造chunk header,偽造heap chunk,玩弄記憶體管理機制
- off-by-one null byte overflow
- [The poisoned NULL byte - Google Project Zero](https://googleprojectzero.blogspot.com/2014/08/the-poisoned-nul-byte-2014-edition.html)
- 某種程度上算很容易發生的漏洞
- 如宣告size剛剛好的字元陣列,一些libc function操作會自動append null byte或自行邊界操作不當等
#### Unsafe Unlink
- doubly linked list - unlink
- unlink(p)
- p->fd = bk
- p->bk = fd
- 古典作法
- 在有漏洞的前提下,偽造p的fd, bk,透過unlink,可以對memory做寫入
- 
- 後來增加檢查,驗證doubly linked list的完整性
- 指過去要能指回來
- P->bk->fd = P
- P->fd->bk = P
- 後來的利用條件
- 已知address底下存放著p指標(已知&p)
- p為data pointer可以多次寫入,或其他同效果方法
- 
- 之後再對data pointer p做寫入,便可以覆蓋掉p,overwrite為任意地址
- 再次寫入即會對目標地址寫入,達到write everywhere的效果
#### Unsorted bin attack
- 在有漏洞的前提下
- 將unsorted bin的bk填成address - 0x10,再malloc相同大小的size,address的地方會被填上libc的address,指向main_arena中
- 無法直接性做到太多事情,可以用來間接進一步利用
- 將一個地方填上很大的數字
- information leak
- 進階heap exploit技巧搭配
- etc.
### Something good
- [how2heap](https://github.com/shellphish/how2heap)
- [Angelboy](https://www.slideshare.net/AngelBoy1)