Pwnable.tw-Re-alloc
Author: 堇姬Naup
env
他的版本是 Ubuntu GLIBC 2.29-0ubuntu2
glibc all in one載下 libc跟ld
1 | ./download_old 2.29-0ubuntu2_amd64 |
用 patchelf 來patch
1 | patchelf --set-interpreter ld-2.29.so realloc |
IDA分析
main
會讀取choice,並進入到對應的function
1 | int __fastcall __noreturn main(int argc, const char **argv, const char **envp) |
menu
3個功能
alloc
realloc
free
1 | int menu() |
allocate
他會接收index,可以是 1 或 0
並且接收size,不能大於0x78
之後call realloc -> prt = 0 -> malloc(size)
heap[index] = heap pointer
輸入data,並在最後補0
1 | int allocate() |
rfree
輸入index
用realloc heap[ptr],並把size設為0,這是跟free一樣的效果
並把heap[ptr]清空
1 | int rfree() |
realloc
他可以輸入index跟size,並且用realloc分配
1 | int reallocate() |
realloc是啥
1 | void *realloc(void *ptr, size_t size) |
realloc長這樣
如果ptr = 0,那就是malloc(size)
如果size = 0,那就是free(ptr)
如果兩個都不為0,則是重新分配該chunk
分析
首先先來看 realloc,我們可以控heap array 跟size,我如果把size填為 0 ,他會去free掉heap[idx],但因為realloc free回傳的是0,所以不會去清空掉heap[idx],導致UAF
感覺可以打 tcache poinsoning,那要如何leak libc呢
要如何leak libc想了很久,也大概是這題的考點
首先我的想法是 heap arr位於bss上可以去做任意寫蓋成我們想要free的地方,我用任意寫在bss段上寫一個大chunk,去free他,以此來leak,但是沒有show,就算該位置有libc,也leak不出來
在這部分有個off by null,但是沒什麼用,都已經有任意寫了www
1 | heap[v2] = v4; |
那就找GOT下手
1 | DYNAMIC RELOCATION RECORDS |
觀察了一陣子,看到atoll,去看一下他在哪裡被調用
1 | __int64 read_long() |
他會去atoll nptr這個buffer
如果我把atoll改成printf,就存在fmt,應該可以leak libc
在進入排heap之前我們需要知道一個特性,就是realloc一個chunk的時候,如果tcache key存在,就不會清空tcache entry
所以就這樣排
alloc 一塊0x10
realloc free掉
realloc 一塊,改掉fd並蓋掉tcache key
就會變成
1 | tcache -> chunk 0 -> fake |
alloc一塊chunk 0
現在heap arr滿了
我們可以透過realloc 0x30把chunk擴張到0x30,最後再拔掉
再alloc一塊0x10就拿到fake chunk了,這裡填GOT
1 | pwndbg> x/10xg 0x4040B0 |
這樣就任意寫到heap了
1 | from pwn import * |
執行下去拿到fmt
1 | naup@naup-virtual-machine:~/Desktop/pwn/pwnable/realloc$ python3 exploit.py |
libc位於rsp+0x18
用 %9$p
leak成功
1 | from pwn import * |
結果我打到這裡就卡住了
因為我把atoll改成printf,導致我function不能用
後來看到printf回傳值是印出的字元數,所以可以控回傳
https://kknews.cc/zh-tw/tech/gvv9mp9.html
用 %xxxc\x00
的方式
然而當我嘗試排了許多次heap,heap arr[1]卡著一塊GOT address,所以我想利用手上有的fmt,寫掉heap arr,但是輸入長度只有0x10,所以我想到可以用stack上value來寫heap addr做寫入
先寫入4bytes的heap arr再stack上,並且用fmt蓋0去蓋掉低位byte
我一次蓋2bytes,再去修改stack 上 heap arr address(這邊只蓋低位1bytes就行了),address+0x2蓋掉heap arr
以此類推
最後把heap arr 0跟1都蓋成0
1 | %4210872c%46$n |
這樣就成功把整個heap arr清空掉
1 | from pwn import * |
接下來可以跟原本一樣去打UAF,做tcahce poinsoning,來拿到fake chunk
這邊雖然清掉了heap arr,但是heap tcache pthread struct中依然存在一些不乾淨的值,所以這邊打UAF用跟之前不一樣大小的chunk來打
把atoll蓋成libc system address
之後傳入/bin/sh,就可以開一個shell 了
exploit
1 | from pwn import * |
後記
這題寫了好久,主要是卡在heap arr卡著GOT的位置
原本是想要用fmt去寫GOT成libc,但是需要一次寫入,否則再次呼叫function的時候就會crash掉,但一次寫8bytes遠端會不太穩定,所以改成清空掉heap arr,重打UAF+tcache poinsoning
最後補個realloc所有狀況
1 | realloc(ptr,size) |