AIS3 CTF Final Phddb Write-up

程式概述

程式為一個儲存 phd 的 profile 的 database ,有 dump、add、edit 及 remove 等功能

漏洞

  • Use After Free

    • 有問題的地方在於 edit 這個功能

      我們可發現到,thesis 的 size 是由使用者輸入的,一開始會先判對是否跟 struct 中所存的 length 相同,不相同則去 realloc 重新分配 heap 的空間,如果分配失敗則直接 invalid length 然後 return ,而判斷 realloc 成功與否的方式則是看 realloc 的回傳值,如果是 NULL 就失敗,但有個特殊情況下 realloc 回傳值也會是 NULL,我們可以用 man realloc 去看一下,可發現到 realloc(ptr,0) 會被當成 free(ptr) 來使用,而在後面也未做 phdlist[v3]->thesis= NULL 清空的動作,造成之後的 Use After Free

漏洞利用

因此我們可以利用 fastbin 的 FILO 的特性,先創造兩個 phd's profile 並讓 thesis 的大小等於 struct phd 的大小,大致上的 layout 如下圖

而我們故意用 editphd 故意 realloc(phdlist[0],0) 也就是 free(phdlist[0]) ,但在 phdlist[0]->thesis 並不會清除

接著我們在 add 一比 phd's profile ,此時會應會 chunk 在 fastbin 的關係,而分配到與剛剛 free 掉的 thesis0 會是相同的

然後我們再編輯 phdlist[0] 也就是第一筆的 phd's profile,但因為 thesis0 指到 phdlist[2] 的關係,因此會更改到 struct ,我們可以藉此將 struct phd 中的 char pointer 改成 got.plt 位置

最後我們在 dump 及編輯 phdlist[2] 就可以達成 infomation leak 去 leak libcbase 以及 GOT Hijacking 改成 system 位置,就可以拿到 shell 了

exploit

phddb.py
裡面有使用到自己的 module 不過跟 pwntool 差不多,習慣用 pwntool 的人就自行轉換了

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

0ctf 2015 Write-up

freenote[400]

程式概述

  • freenote 為一個類似筆記功能的程式,這個程式分別有 List , New , Edit , Delete 四個主要的功能及 Exit 結束程式

  • 會用個 note struct 去紀錄每個筆記是否為有效筆記筆記大小指向筆記內容的 pointer
struct note{
    int isValidNote; // 0 = not valid, 1 = valid
 int length;
    char content;
}

程式行為

  • 經過 ltrace 分析之後,可發現到一開始程式會先 malloc(0x1810) 用來存放這些 note struct,並以陣列的形式去儲存,其中的 index 即為筆記的編號,在最前方也紀錄共有多少筆記

    • List
      • 會列出每個筆記的內容(也就是內容 pointer 所指向的地方),這裏使會列出筆記為isValidNote == 1 的內容
    • New
      • 再輸入你要的大小之後,如果小於 128 byte 就會分配 128 byte 給你,但如果大於 128 byte 例如 252 byte ,那麼就會給你 128 + 128 byte 的大小,依此類推
    • Edit
      • 在輸入完要編輯的筆記及大小之後,程式會先判斷這個大小是否與之前的一樣,如果一樣則不會重新分配空間直接編輯內容,如過不一樣則會 realloc 夠你筆記大小的空間給他,不過這部分會先看原先分配空間的後面是否有足夠用的空間給他,如果夠用的話就不會改變起始位置
    • Delete
      • 輸入完要刪除的筆記後,會將 note[i] 中的 isValidNote 改成 0,在 free(note[i]->content),並將筆記總數 - 1

漏洞

  • Double free

    • 在 Delete 時,並不會將筆記從 note[i] 中移除,只是將 isValidNote = 0 ,而 free 是根據 note[i] 去決定要 free 哪邊,並沒有先去檢查 note[i]->content 是否已經被 free 掉,一旦輸入同樣的 i 就會造成 double free 的漏洞
  • Memory leak

    • 因在輸入筆記後,程式並沒有在使用者輸入的內容最後方補上 \0 ,因此在 free(note[i]) 之後,該空間會被加入 free chunk 並有 fdbk 欄位,會指向 heap,當 note[i-1] 使用 edit 加大空間後,可巧妙的接續在 fdbk 之前,而在使用 List 之後便可 leak 出 heap 中上次 free 掉空間的位置,這些位置的 offset 都是固定的,因此可以算出 heap base

漏洞利用及思路

  • 為了要利用 double free 這個漏洞去改其他位置的值,必須先觸發 unlink() 不過要觸發 unlink() 必須滿足下列三個條件其中一種:
    • 如果下一塊是 top chunk,且上一塊是 free chunk
      • 最後合併到 top chunk
    • 如果下一塊不是 top chunk
      • 上一塊是 free chunk
      • 下一塊是 free chunk
  • 然而紀錄上一塊是不是 free chunk 的及大小資訊( free 是利用這些資訊去尋找上一塊 chunk 位置),會記錄在目前這塊 chunk 的 meta 中,也就是說要確定該快 chunk 是否為已經 free 的狀態是由下一塊的 chunk 所決定的,所以如果使用下一塊是 free chunk 這個條件必須改到下下一塊 chunk 的 meta data 或是利用特殊的方法欺騙 free() 下下一塊的位置,也就是必須動到三塊的 chunk 的 meta data,所以這部分稍微會比較麻煩一點點,故決定採用上一塊是 free chunk 這個條件來達成。
  • leak heap
    • 建立四塊左右的 note,delete 0,2 塊,再利用前面所述的方法,算出 heap 位置
  • 構建 fake chunk

    • 先 new 三塊 note 之後,delete 第二塊的,再利用 edit 加大第一塊的空間,使得可以蓋過第二塊的 meta data 起初大概的改法如下

    • 但使用後缺發現會一直出現 double linked corruption

    • 仔細查看後才發現到原來有 FD->bk != P || BK->fd != P 這項保護的機制在,不能直接改,因此必須找到滿足 P->fd->bk == PP->bk->fd == P 的 pointer,才有機會利用
    • 過了很久才想到在 note[i] 中都有指向 content 的 pointer 只要稍作修改就可偽造不同 size 的 chunk 讓 free 以為 note[i]->content 所指的位置為 chunk 的 head,這一步應該就是最關鍵的地方,也是讓我卡比較多時間的地方,其最後改法如下圖所示(黃框為 fake chunk ):

    • delete note[1] 也就是 free(note[1]->content) 之後便可成功改到 note[0]

      • note[0]->content = &(note[0]->content)-0x10 亦及 FD->bk = BK
      • note[0]->content = &(note[0]->content)-0x18 亦及 BK->fd = FD
    • 因此 note[0]->content 位置就變成了 &(note[0]->content)-0x18 ,這樣就可以利用 edit 任意更改 note[i] 的內容

  • 更改 note[i]

    • 我這邊稍作了修改將 note 變成六塊

      • 第 0-1 塊用來 leak heap 位置用
      • 第 2-3 塊用來更改 note[i] 的內容
      • 因此只要再次用 edit 更改同樣大小的內容,便可改掉整個 note,這部分定要跟之前說 new 的大小相同,否則會重新 realloc 會失敗,示意圖大概如下

      • 第 4-5 塊最後會用來改 atoi 的 got
        • 事實上可以不用這麼多塊,但只是怕亂掉所以每塊都分開
    • 再來將 note[i] 部分內容改成 free_gotatoi_got 位置

  • leak libc 位置

    • 使用 list 後,可利用 got 來算出 libc 的位置
  • 改 got

    • 再用 edit 更改 note[5] 後,便可將 atoi 的 got 內容改為 system
  • 跳轉到 system

    • 直接輸入 /bin/sh 就會去執行 system('/bin/sh'),這樣就拿到 shell 了
  • exploit
    exploit

心得

  • 這次 0ctf 題目算是不會很難,只是不知道為什麼第二天就體力不支了,整整兩天只解了 freenote 這題,不過這次題目出的我個人覺得還算不錯,也挺好玩的,只是實力與經驗還需再加強,也要再多多練一下其他領域的題目,不然每次解 pwn 之外的題目都幾乎不會解,就連最簡單的 SQL injection 都會有點問題,不過我覺得 freenote 這題是很棒的一題,可以拿來練習 heap exploition 的部分,未來有時間再來整理有關 heap exploition 的資料。