Plaid CTF 2015 Write-up

PlaidDB [550]

程式行為及概述

如名稱所述是個 database 的 service ,可加入資料進 db,而每筆資料都配合一組 key,並以 binary tree 的方式去儲存,其中每個 node 都有個 row struct 大致上如下

struct row {
char *key
int size
char *content
row *left
row *right
row *parent
bool is_leaf
}

而他有的功能如下

  • GET
    • 分配放 key 的空間,輸入 key , 可獲得 db 的內容,最後再把剛分配的空間 free 掉
  • PUT
    • 一開使會先分配 0x38 byte 放置 row ,再分配 8 byte 放 key,再依據所輸入的 size 大小,分配相對應的空間給你 ,而在輸入 key 時會檢查所輸入 key 的空間夠不夠用,一旦不夠用就會重新 realloc 兩倍的空間給你
  • DUMP
    • 會將所有 key 的資訊 dump 出來
  • DEL
    • 分配放 key 的空間,輸入 key 之後,會先比對是否 key 是否有在 tree 中,若有,則將相對應的 key、content、row 及用來比對 key 依序 free 掉,*若無則返回 menu 選單(並沒有將剛剛用來比對的 key free 掉),這部分在後續排 heap 的階段頗好用 *
  • EXIT
    • 離開程式

漏洞

  • NULL byte overflow
    • 再所有輸入 key 的功能中一旦把 key 輸入完就會在結尾補上零,然而在輸入的 key 剛好是最大分配的空間時,並不會重新 realloc 而卻也會在最後補上 \x00 此時,造成了 overflow ,並蓋到了 malloc_chunk 中的 size 欄位

保護機制

  • CANARY : ENABLED
  • FORTIFY : ENABLED
  • NX : ENABLED
  • PIE : ENABLED
  • RELRO : FULL

漏洞利用及思路

  • 此題一開始想觸發 unlink,但發現難以利用,很難找到一個 pointer 指回自己,也沒有看出可以 leak memory 的地方,後來看到了 google project zeroGlibc Adventures: The Forgotten Chunks 的用法,才理解到這題主要在考的是讓 chunk overlap 的情況,其中大致上的概念如下

    • 一開始先 malloc 三塊相鄰的 chunk

    • 接著 free(B)

    • Null byte overflow ,這時候 libc 會認為這塊 chunk 剩下 0x100 的空間,但 c chunk 的 prev_size 卻是紀錄還有 0x120 byte

    • malloc(D) 會先從剛加入 unsortbin 的 chunk 中切出 chunk

    • malloc(E) 一樣從未分配完的 chunk 在切出空間來給 user

    • free(D) 此時 D 也會被加入 unsortbin

    • free(C) 此時 free 會根據C 這塊 chunk 的 prev_size 去找尋上一塊chunk 因此會認為 D 開始到 C 前都是同一塊 chunk 並認為這塊 chunk 有 0x120 byte 卻不知道中間已經有一塊已經分配出去的 chunk ,並將它們合併成 0x220 byte的 chunk

    • malloc(0x200) 再次 malloc 夠大的空間時,會將之前已經分配出去的 chunk 一併也取進來,但使用者可以任意寫入該區內容,也就是 E 這塊 chunk 可以任意被改動,這題剛好就是讓 plaiddb 的row struct落在這一塊而被任意改動

  • 利用上述的手法改道舊有的 row struct 其實就已經差不多了,可微調 size 大小,讓舊有的 key 也落入 overlap 的 chunk 中,並很巧妙的 free 掉時,讓 free 將 bin 的位置剛好填入舊有的 key 之中,並用 DUMP 就可以 leak 出 libc 的 base ,接著將舊有的 row 夠造成下列的形式

payload = pack64(binsh)
payload += pack64(7)
payload += pack64(0)
payload += pack64(free_hook)
payload += pack64(free_hook-0x30)
payload += pack64(free_hook)
payload += pack64(system)
  • 接著在 DELE 時 , 就會將 system 寫入 free_hook 的欄位中,當下次在 free 時等同於也執行了 system,不過當 DELE 動作快結束時就可以拿到 shell ,這邊其實只要稍微 trace 或是用 binary tree 刪除節點的概念就會知道他如何去寫入了,細節我就不多寫了

心得

  • 很可惜的沒在兩天內解完,最後都排得差不多了,但時間還是差一點點,寫 explit 速度還是太慢,這題的關鍵主要在排 heap 的部分,因為 fastbin 並不會觸發 unlink 不容易達成上述描述的情況,必須想辦法利用 key 來製造 smallbin 的大小,至於排的過程就不詳細描述了,如果有時間的話大家可以去解解看慢慢體會一下 :-)

Exploit

exploit

comments powered by Disqus