HITCON CTF 2019 Quals — One Punch Man [PWN 292pts]
2019-10-16 19:08:37 Author: medium.com(查看原文) 阅读量:20 收藏

題目是經典類選單題型,可以new/modify/show/delete:

  • debut(new) — 可以分配0x80–0x400(small range),然後是用calloc給的
  • modiry(rename) — 存在UAF
  • show — 沒什麼特別的
  • retire — 存在double free

題目保護為:NX + PIE + RELRO,使用libc-2.29!這已經是個我完全沒碰過的版本了…

另外還有個隱藏功能,可以malloc(0x217) 並read讀入,但會判斷tcache_perthread_struct(TPS)
上對應0x20大小的tcache count必須大於6才行.

但由於new功能限制的大小皆為small bin range,無法直接free掉一個fast chunk放進tache進而增加count,這時我腦中出現幾種思路:

  1. fastbin-dup想辦法要到一塊memory能overwrite TPS count.
  2. 同上,只是改用tcache-dup
  3. 同上,只是改用smallbin-dup (House of Lore)
  4. 用unsorted bin unlink attack把TPS count改成一個大值
  5. tcache stashing unlink attack把TPS count改成一個大值

首先第一個方法由於我們只能calloc(small chunk size),沒辦法要一塊fast chunk.

而第二個方法一開始我感覺可行,因為只要tcache還沒滿,那麼free掉fast/small chunk後都會先進tcache bin,但搞了很久才發現calloc不會從tcache拿…所以也放棄了這方法

第三個方法(貌似是其他多數人的做法?)後來沒有嘗試.

第四個方法也是一開始就想到,但是試了半天才發現unsorted bin unlink在libc 2.29已經新增了完整性檢查WTF(因為用docker架環境所以不知為何error message都沒收到,是後來把libc 2.27跟libc 2.29的malloc.c拿來diff一下才發現的QQ)。

於是我想到了第五種作法,這大概是去年libc 2.27剛出的時候我在啃code時無聊想到的打法XD不過由於限制很多,再加上他效果跟unsorted bin attack有87%像,所以也沒認真當一回事,場景是這樣的:

當要了一塊small chunk後,此時會遍歷跟這塊大小一樣的small bin上看有沒有其他free chunk,如果有就把它擺到tcache bin上(就是最近用到的放在快取上的概念),看似簡單的一句話其實是要符合挺多條件的

  • 『當要了一塊small chunk』,換句話說你起碼要有一塊small chunk在這個bin裡面
  • 『當要了一塊small chunk』,所以tcache bin必須是空的?否則正常來說tcache優先權是最高的,通常tcache有就會先從那邊拿
    (又或是有什麼情況可以讓他跳過tcache來找small bin?)
  • 『有其他free chunk』,換句話說前面先拿了一個small chunk走後,還要有其他free chunk,代表起碼要有2個
  • 『如果有就把它擺到tcache bin』,也就是說tcache bin必須還有空間。

UNLINKs

而這個情況會發生兩種不同的unlink

  1. 要了一塊small chunk — 從small bin unlink出去,會做完整性檢查。

2. 把剩餘在small bin的chunk unlink到tcache bin中,不會做檢查

而上面這個bck->fd = bin就是關鍵,這個bin是一個libc中的位址,所以會是0x7f??????????所以如果能控制這個bck,就能在任意位址寫入這個值。

*(bck+0x18) = 0x7f??????????

其實效果真的跟unsorted bin attack差不多,只是要符合挺多條件的,在本題的情況無非就是要改掉TPS count,調整一下讓count欄位被改為0x7f即可。

然後有發現上面好像有三條很矛盾的狀況嗎?

  • 『當要了一塊small chunk』,換句話說你起碼要有一塊small chunk在這個bin裡面
  • 『當要了一塊small chunk』,所以tcache bin必須是空的?否則正常來說tcache優先權是最高的,通常tcache有就會先從那邊拿
  • 『如果有就把它擺到tcache bin』,也就是說tcache bin必須還有空間。

正常來說free chunk只有在tcache bin滿了之後才會進small bin,第一條情況告訴我們起碼有一個small chunk,那通常代表tcache bin滿了,但第二第三條情況就衝突到了,怎麼辦?

GOOD OLD LAST REMAINDER

這時可以利用unsorted bin中的last remainder,例如以下

[1] A = calloc(1, 0x400); free(A);  *7
[2] A = calloc(1, 0x400); free(A);
[3] A = calloc(1, (0x400-0x100));
[4] calloc(1, 0x400);

[1] 塞滿tcache idx_0x400 bin

[2] 此時會進入到unsorted bin之中

[3] 要一塊(0x400–0x100),由於calloc不會從tcache拿,所以直接從unsorted bin拿走,切剩下的那塊last remainder為0x100

[4]此時要一塊比0x100還大的chunk,由於last remainder大小不夠,就會直接被送進small bin裡面。

就只是利用remainder不會進tcache的特性而已。


文章来源: https://medium.com/@ktecv2000/hitcon-ctf-2019-quals-one-punch-man-pwn-292pts-3e94eb3fd312?source=rss-eb87faee21ca------2
如有侵权请联系:admin#unsafe.sh