Basic Info
附加檔案有兩個 32bit ELF
note
跟 note_client
以及 libc-2.19
環境是一般程度的保護,PIE 沒開以及 Partial RELRO
直接執行
note
會看到一堆 Hex:IDA 打開看起來是正常的選單題:
那為何執行起來看到的是一堆 Hex 呢?
仔細分析後
note
中所有 I/O 都被實作會經過某種 encode/decode先跳過這邊,看看
note_client
會做什麼直接執行:
IDA 分析可以知道他想要開檔
config.txt
,並連線 config.txt
中寫的 host:port因此
config.txt
內容寫 127.0.0.1 31337
,並使用 ncat 將 note
bind 在 127.0.0.1:31337 後執行 note_client
:成功執行了!
note_client
基本上就是把 I/O 做 encode/decode 後跟 note
溝通,因此可以先省去逆向 note
當中的 IO encoding ,直接透過 note_client
來互動。Info of note
- Initialize:
pool = (Pool*)malloc(0x1000)
, 結構為:
struct Pool { int limit; int alive_amount Note note[255]; }; struct Note { int inuse; int sz; char *content; };
其中limit, alive_amount
不重要,用來限制 note 數量不會超過 255 而已。
- list: 印出所有
inuse
是true
的 note - read: 指定
index
- 驗證
index
合法 - 驗證
note[index]->inuse
為true
- 顯示 note 的
date/name/body
- add: 新增一個 note,步驟為:
- 找 pool 裡第一個
inuse
是false
的 note - 輸入一個
size
, 規定32 <= size <= 1024
note->content = malloc(size+48)
- 輸入
name:readn(note->content, 31)
- 輸入
date:readn(note->content+32, 15)
- 輸入
body: readn(note->content+48, size)
note->inuse = true
note->sz = size
- free: 指定
index
- 驗證
index
合法 - 驗證
note[index]->inuse
為true
free(note[index]->content)
note->inuse = false
- edit: 指定
index
- 驗證
index
合法 - 驗證
note[index]->inuse
為true
- 編輯
name:readn(note->content, 31)
- 編輯
date:readn(note->content+32, 15)
- 編輯
body: readn(note->content+48, note->sz)
Vulnerability
note
中有很多邊界檢查有多餘等號或不正確的檢驗,但事實上都不會有直接危害。唯一可以利用的洞是在讀輸入時有 overflow。邏輯大約是這樣(因為原本還有一些 decode 功能,這是簡化後的版本):void readn(char *output, int len) { int i=0, c; while((c = getchar()) != '\n') { output[i++] = c; if(i >= len) break; } output[i] = c; }
只要輸入剛好的長度且最後一個字元不是換行,就會造成 one null-byte overflow。
Exploit
接下來就是正常的 heap exploit 了。
Information Leak
因為只能 overflow 一個 null-byte,肯定想要 overflow 下一塊 chunk 的
add(36); add(36); add(316); add(36); add(36) 之後 heap 長這個樣子:
接著刪除第一塊(free(0)),並且在編輯第二塊 (edit(1)) 時觸發 overflow:
將
因此做出了一塊
接著再 add(36):
此時 read(1) 就能 leak 出被 free 的 small chunk 的
Overwrite GOT
Information leak 完後,目標是能改寫到前面
利用 smallbin chunk 合併時的 unlink 達到目的。
free(0); add(108); add(52)
注意此時 1 號 note 可以亂改 note 2 的
圖文並茂一下:
edit(1): 在 note1 構造假 chunk 並改寫 note2 的
free(2):
此時觸發
即
因此再次 edit(1) 就可以改寫掉
exploit 中將
BTW,因為
BTW2,這題即使開啟 PIE 與 Full RELRO 也一樣作法,最後一步改為改寫 libc 中的
Exploit Script
Flag:
Exploit
接下來就是正常的 heap exploit 了。
Information Leak
因為只能 overflow 一個 null-byte,肯定想要 overflow 下一塊 chunk 的
size
。首先利用 add 功能構造 heap layout:add(36); add(36); add(316); add(36); add(36) 之後 heap 長這個樣子:
+---------------+---------+---------+------------------+---------+---------+-----------+
| pool: 0x1009 | 0: 0x59 | 1: 0x59 | 2: 0x171 | 3: 0x59 | 4: 0x59 | top_chunk |
+---------------+---------+---------+------------------+---------+---------+-----------+
接著刪除第一塊(free(0)),並且在編輯第二塊 (edit(1)) 時觸發 overflow:
|------- 0xb0 ------|
+----------+---------+---------+----------------+-+---------+---------+-----------+
| 0x1009 | 0: 0x59 | 1: 0x58 | 2: 0x100(fake) | | 3: 0x59 | 4: 0x59 | top_chunk |
+----------+---------+---------+----------------+-+---------+---------+-----------+
| freed | | prev_size(0xb0)| |
+---------+ +----------------+-+
將
size
壓上 null-byte 會因為 prev_inuse
bit 為 0 而認為前一塊 chunk 已經被 free 了,此時 free(2) 就會將 0 與 2 號 chunk 合併:+----------+---------+---------+-----------+------+---------+---------+-----------+
| 0x1009 | 0x1b1 | 0x70 | 3: 0x59 | 4: 0x59 | top_chunk |
+----------+---------+---------+-----------+------+---------+---------+-----------+
| 1: 0x58 | <-overlap! ^ no use
+---------+
因此做出了一塊
0x1b0
的大 chunk,並且跟 1 號 chunk 是重疊的。接著再 add(36):
+----------+---------+---------------------+------+---------+---------+-----------+
| 0x1009 | 0: 0x59 | 0x159(freed) | 0x70 | 3: 0x59 | 4: 0x59 | top_chunk |
+----------+---------+---------+-----------+------+---------+---------+-----------+
| 1: 0x58 |
+---------+
此時 read(1) 就能 leak 出被 free 的 small chunk 的
fd
與 bk
。並且只要在 read 之前先 free(3),就能同時 leak libc base 與 heap base (unsorted bin double linked list). +----------+
| v
+----------+---------+-----------------|---|------+---------+---------+-----------+
| 0x1009 | 0: 0x59 | fwd: in libc, bck | 0x70 | freed | 4: 0x58 | top_chunk |
+----------+---------+---------+-----------+------+---------+---------+-----------+
| 1: 0x58 |
+---------+
Overwrite GOT
Information leak 完後,目標是能改寫到前面
pool
那邊的 char *content
,如果能改掉 pool 上的 pointer 就能利用編輯功能改寫 got 從而控制 eip。利用 smallbin chunk 合併時的 unlink 達到目的。
free(0); add(108); add(52)
+----------+-----------------+---------+------+------+---------+---------+-----------+
| 0x1009 | 0: 0xa1 | 2: 0x69 | 0xa8 | 0x70 | freed | 4: 0x58 | top_chunk |
+----------+---------+-------+-+-------+------+------+---------+---------+-----------+
| 1: 0x58 |
+---------+
注意此時 1 號 note 可以亂改 note 2 的
size
。我們在 1 號 note 當中假造一個被 free 的 chunk,並把 fd, bk
指向 pool
中 1 號 note 附近,如此可以使假造的 free chunk 成功被 unlink,並改掉 pool
中的 1 號 content
指標。圖文並茂一下:
edit(1): 在 note1 構造假 chunk 並改寫 note2 的
prev_size
與 size
heap +----------+----------+
0x0000 : | 0x00 | 0x1009 |
0x0008(pool) : | 0xff | 0x04 |
...
0x0020 : | 0x24 | h+0x1068 |
...
0x1008 : | 0x00 | 0xa1 |
0x1010(note0): | | |
...
0x1068(note1): | 0x00 | 0x41 | <- fake chunk
0x1070 : | h+0x18 | h+0x1c | <- fake fd|bk
...
0x10a8 : | 0x40 | 0x68 |
0x10b0(note2): | | |
...
free(2):
heap +----------+----------+
0x0000 : | 0x00 | 0x1009 |
0x0008(pool) : | 0xff | 0x04 |
...
0x0020 : | 0x24 | h+0x1068 |
...
0x1008 : | 0x00 | 0xa1 |
0x1010(note0): | | |
...
0x1068(note1): | 0x00 | 0x41 |<-+
0x1070 : | h+0x18 | h+0x1c | | merge
... |
0x10a8 : | 0x40 | 0x68 +--+
0x10b0(note2): | | |
...
此時觸發
unlink(0x1068)
,unlink 的結果就是 *(h+0x1c+8)=h+0x18
即
h+0x24
從原本 h+0x1068
變為 h+0x18
因此再次 edit(1) 就可以改寫掉
note[1]
自己的 char *content
,再一次 edit(1) 即可改掉 GOT。exploit 中將
got_atoi
改成 libc_system
,這樣下次輸入 index 時輸入 "sh" 即可開啟 shell。BTW,因為
note_client
本身有在檢查輸入長度之類的資訊,所以必須要看懂並實作原本 IO 的加解密。基本上照 decompile 的結果寫一遍就可以了,細節可見 script 。BTW2,這題即使開啟 PIE 與 Full RELRO 也一樣作法,最後一步改為改寫 libc 中的
free_hook
函式即可。Flag:
WhiteHat{24b3e07a4494d4cd3ad973ee7d5fadca390df5bb}
沒有留言:
張貼留言