【ctf復現003】修改TCACHE_MAX_BINS

Ch33_1n'
Apr 8, 2021

--

題目:VNCTF2021-LittleRedFlower

環境:libc2.30,x64,開了沙箱

漏洞:地址任意寫

(該修改的手法對目前有tcache機制的libc版本都可用)

TCACHE_MAX_BINS

tcache_perthread_struct 是全局的tcache數據結構,存在于每個綫程中。用TCACHE_MAX_BINS 確定 bins 數量,每個 bin 上可存放七個 chunk。從下面的代碼中可以看到,TCACHE_MAX_BINS 默認為 64,即 0x40.

但是在free或malloc的時候,并不會依據 TCACHE_MAX_BINS 來遍歷鏈表。

下面給的是__libc_malloc的代碼。在_int_free中也一樣,對idx是否合法都是依據 mp_.tcache_bins 判斷的(libc2.31和2.32中均是如此,未作修改)。

我們回到mp_結構體的定義中,會發現 .tcache_bins 取的正是TCACHE_MAX_BINS 的值。

它的類型是malloc_par,裏面存放的是各種可能用到的參數。跟 tcache 有關的參數成員還有 tcache_count、tcache_max_bytes、tcache_unsorted_limit。

程式分析

程式開啓了沙箱,禁用execve:

也就是只能用orw了。

程式的大致流程是:

  1. 輸出stdout地址(GIFT)。這樣一開始就能直接算出libc基址。
  2. 任意地址寫1字節數據。
  3. 在malloc的0x200的chunk上,任意offset處寫8字節。對offset大小無檢查,相當於任意地址寫8字節。
  4. 能malloc一個指定大小(0x1000≤size≤0x2000)的chunk,並寫入size位内容。
  5. 最後free掉新申請的chunk,指針置0。

不難觀察到該程式的限制相對較少,有兩次任意寫的機會。程式的注入點都在2、3、4步,最後一步調用了free函數。所以只要通過前三步劫持__free_hook為某些gadget,能進而劫持程序執行流、再實現orw就可以了。也就是棧遷移的手法,但這裏開了NX保護,所以不能用棧地址。__free_hook前面很多initial的空間,全是0,可以利用。

2、3、4步都沒有free,應該不能通過劫持fd或next指針,申請到__free_hook 附近改。但是第三步可以實現任意地址寫。第四步malloc 的時候要申請到目標地址處的話,這個地址只能在第三步寫進去了。

此前bin中沒有chunk。按理來説應該是要用brk()進行内存分配,但是這樣就跟第三步寫入的地址無關了。實際上在 __libc_malloc 函數中,是先遍歷bins,當沒有可用chunk時才調用brk()分配。

對 tcache 的遍歷,就是用 mp_.tcache_bins 確定範圍的。如果利用第二步的任意地址寫1字節,修改mp_.tcache_bins為一個大數,調用malloc執行到這裏的時候,就會越過數組範圍往下遍歷,直到 entries[tc_idx] 和 tcache->counts[tc_idx] 均不爲0的位置,返回一個tcache chunk。

因此由於第三步中malloc的chunk被初始化了全1數據,這片區域可以作爲counts[tc_idx]的位置,也就是我們需要控制好size,讓它能落在這片區域。

counts原本占0x80的大小,entries占0x200。所以tc_idx至少為 (0x280+0x10)/2–1=147,也就是size至少為0x1480.

這裏我選的是0x1500:

申請到freehook后,就是orw了,可以用棧遷移的方式來解。官方的wp就是典型的orw,也可以用sigcontext的SROP,這樣就省去了找某些gadget的功夫,降低難度。劫持完__free_hook 后,可以利用setcontext 函數進行SROP,再通過ROP讀出flag。

一般是從setcontext+54開始用,爲了避開fldenv [rcx]这个指令(不然程序容易崩潰)。

能看到+61後面調用的參數都是rdx(libc2.29以後的版本都是,之前的參數的rdi),而freehook的參數是放到rdi中的。所以freehook處需要填充的gadget是能實現參數從rdi轉到rdx上的。

ROPgadget grep一下,找到下面這段。rdi可控,rdx可控,call地址也可控。

exp

後記

這個題目的重點應該是 TCACHE_MAX_BINS 的修改和orw。我一開始不會調offset和size。原來是基礎不牢… 結構體tcache_perthread_struct是在程序運行后申請的,其空間就在堆基址處的0x291的chunk裏。根據原TCACHE_MAX_BINS算了下空間大小,剛剛好就是0x291. 這才弄明白爲什麽之前遇到有tcache機制的程式時,堆基址處的chunk能保存tcache bin的地址。

參考鏈接

--

--