三個白帽終于出了一個pwn的挑戰。總算是能讓我這種不會web的菜雞拿幾個貓幣了。而且這一道題目出的很有心意,后面利用過程一環扣一環,質量很高。
現在就來分享一些詳細的利用思路。
附上原文件:vms.zip
做pwn題的第一步就是要分析代碼,尋找漏洞。這次的程序代碼流程不是很長,總共的函數數量也只有十幾個,所以在分析代碼方面不存在很多的問題。
這個是經過分析還原出來的Vulnerability結構體。也是程序中儲存數據的主要結構。當然最重要的就是結構體里有一個指針。一旦覆蓋可以用來任意讀寫
00000000 Vulnerability struc ; (sizeof=0x60, mappedto_1)
00000000 rank dq ?
00000008 titleSize dq ?
00000010 detailLen dq ?
00000018 title db 64 dup(?)
00000058 detail dq ? ; offset
00000060 Vulnerability ends
程序存在2個主要的漏洞 第一個是在edit修改Vulnerability結構的時候沒有校驗in_use位。所以存在即使內存已經被釋放,卻仍然能夠修改內存的情況。也就是所謂的use after free漏洞
稍微分析一下,很容易就可以發現這一出的uaf漏洞
第二處漏洞就相對比較隱晦了。在add添加Vulnerability和edit修改Vulnerability結構的時候,如果輸入的rank位負數,則程序會選擇跳過而不是給rank字段賦值。我們可以利用這一點來leak堆上的信息。因為這是整個程序唯一一處不會破壞堆上原先數據的的的地方。Vulnerability結構的其他字段都會強制的修改內存中的數據,無法用來泄露信息
出題人在整個題目里挖了3個大坑等著人來跳。要想拿到shell,需要一個一個的繞過。
1.所有堆指針加密
無論是存在.bss段的Vulnerability的指針還是Vulnerability結構中指向detail的指針,都是用一個隨機數亦或之后再存放的。所以無法直接通過uaf來修改指針實現任意地址讀寫。
所以我們必須要先想辦法搞到亦或用的隨機數,否則無法完成利用。
2.堆指針的check函數
在程序通過指針讀寫detail內存之前都會有一個check函數來校驗堆指針的有效性,如果校驗錯誤就會直接退出程序
好在這個check并沒有這個嚴格。一般來說只要保證Vulnerability的指針小于等于detail的指針的時候,有很大概率通過check。實際的測試下來,整個攻擊一般在20次嘗試以內就能成功
3.FULL RELRO保護
通過checksec就可以發現,程序開啟了FULL RELRO,這意味著我們無法修改got表。即使通過uaf實現的任意讀寫,也無法控制eip。內存里也沒有明顯的函數指針能夠被覆蓋。
這里我通過覆蓋free_hook來實現最后的調用system。這里是相關的代碼。可以發現free函數可以通過hook來使他調用我們自定義的函數。只要我們修改__free_hook的值為system函數的地址,就可以拿到shell
2926 __libc_free (void *mem)
2927 {
2928 mstate ar_ptr;
2929 mchunkptr p; /* chunk corresponding to mem */
2930
2931 void (*hook) (void *, const void *)
2932 = atomic_forced_read (__free_hook);
2933 if (__builtin_expect (hook != NULL, 0))
2934 {
2935 (*hook)(mem, RETURN_ADDRESS (0));
2936 return;
2937 }
...
2962 }
2963 libc_hidden_def (__libc_free)
詳細的利用代碼我和我分析代碼時的idb在這里
利用第一步就是要leak出用于亦或的隨機數。需要用一些猥瑣的思路來構建堆結構。
1.獲得堆地址
首先,我們需要知道堆塊的具體地址。這個我們可以leak堆管理中的鏈表指針來實現。Vulnerability結構的rank字段正好是存放鏈表指針的位置。正如前面所說的,只要我們在add的時候不寫入rank的值就能夠通過rank來leak堆的地址。
2.利用fastbin漏洞控制malloc內存地址
我們知道fastbin的單向鏈表鏈接的。而通過uaf,我們的rank字段正好可以覆蓋這個單項鏈表指針。從而控制下一次malloc時候的地址。而且我們已經知道了堆的地址。所以我們就能控制malloc。使得新分配的Vulnerability結構的rank字段于另外一個Vulnerability的detail指針重合。這樣我們就能夠實現對detail字段的leak和修改了。而且結合剛才獲得的堆地址。我們也可以反向算出用于亦或的隨機數的數值。
到此,我們實現了任意地址的讀寫
3.泄露libc基地址
這個比較簡單。因為normal bin是通過雙向鏈表來管理的。而雙向鏈表的頭是存放在libc里的。所以只要在堆上隨意的釋放一塊大的內存就能產生一個指向libc的指針。在我利用過程中。正好在堆上有這樣一個指針,直接leak出來然后推算出libc基地址。
4.寫入free_hook
用ida查看一下libc就能找到libc中free_hook的地址
最后的實際的地址是0x3C0A10
。把算好的system函數的地址寫入進去就可以了。最后找一塊內存寫入/bin/sh
然后拿去free就可以完成整個利用的過程了
一血留念