(比賽當天看了一整天都沒看出漏洞,感謝R1nd0師傅賽後分享的解題思路和exp)
環境:libc-2.23,x64
漏洞:double free
程式分析
保護全開:
用strings命令查看GCC版本,程式用的libc應該為2.23.so。
程式是經典的菜單題。傳輸時加上了别的数字,同時使用了base64加密。
我一開始是用base64解密接收到的字符串,減去menu中對應的字符串,以爲這樣得到的就是key。後面每次傳輸,都是以這個key為基礎進行傳輸的。導致每次只能寫入6字節的數據,我百思不得其解。
res=base64.b64decode(ru(‘\n’))
success(res)
key=res[:-6]
success(key)
實際上base64解碼出來是:(原文是對應’1. New.’的接收到的字符串)
接著我們看對應’3.Edit.’(長度與1.new不同)接收到的字符串。
兩字符串的頭部有些許不同,可以看出來傳輸協議并非加上固定的字符串這麽簡單。對比下不同之處,大概能了解傳輸的協議是跟字符串長度有關的,更具體的需要看getnum函數。
總的來説該協議就是:
def transfer(ptr_size,lenn,content):
s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+=p64(lenn+ptr_size+0x20)#用於檢查
s+=p64(ptr_size)
s+=p64(lenn)#content長度
s+='\x41'*ptr_size#前8字節必須為‘AAAAAAAA’
s+=content
p.sendline(s)
測試一下。嘗試執行menu(1),收到’size:’字符串説明接收成功了。
add函數:存在double free漏洞。ptr是一個在bss段上的指針,在程式中用於暫存輸入的字符,起緩衝區的作用。造成該漏洞的主要原因是程式邏輯不正確,修改代碼順序後也可在ptr指針不置0的情況下消除該漏洞。
free函數:free的是src指向的chunk,有將src和size置零,不存在漏洞。
edit函數:用add時保存的size規定輸入長度,不存在漏洞。
show函數:和菜單輸出用的是同一個output函數。函數調用處沒有輸出長度的參數,函數中只實現了加密輸出功能。也就是字符串有多長,都全部輸出。存在内存泄漏漏洞。
利用思路
- 泄露地址:利用unsorted bin。因爲傳輸協議中的ptr_size和len是沒有限制大小的,我們能申請到0x100的空間。爲了讓 src 申請到這些被free到unsorted bin中的空間,需要控制len和size大小,保證src=malloc(size)時申請到的空間,是從前面free ptr到unsorted bin中的空間切割回來的。這樣show的時候,能拿到main_arena+88的地址,從而計算偏移。
- 改寫__malloc_hook:利用double free漏洞,在fastbin中構造chunk 2->chunk1->chunk2。修改fd指針,再在申請回來的時候將malloc_hook覆寫成one_gadget。
exp
反思
這個程式的漏洞利用手法比較基礎,就是fastbin double free。但是double free漏洞不太明顯,需要認真閲讀代碼才能發現。復現完最大的感受就是,找不到漏洞時需要沉下心來逆向,通常出現錯誤的地方都是在程序員自己寫的myread函數,這個地方的代碼需認真看。