water-impossible
Challenge Link: water-impossible
Category: pwn
Writeup: water-impossible
Welcome !! Challenger ~
Here is a simple challenge for you.
Try to find the key to pass.
nc bamboofox.cs.nctu.edu.tw 58799
須具備的知識
Stack
stack 是程式在執行中用來儲存資料的結構,儲存的資料如
- 暫時的值
- return address
- 區域變數
- ...
rsp
rsp
是用來儲存 stack 所在位置的暫存器
區域變數
宣告區域變數時,會將值存在 stack 上
假設宣告兩個變數 a, b
int a = 1;
int b = 2;
宣告 a 時,stack 狀態如下
+----------+ 0x7fffffffe728 low
| a = 1 |
+----------+ 0x7fffffffe730
| ... |
+----------+ 0x7fffffffe738 high
宣告 b 時,stack 狀態如下
+----------+ 0x7fffffffe720 low
| b = 2 |
+----------+ 0x7fffffffe728
| a = 1 |
+----------+ 0x7fffffffe730
| ... |
+----------+ 0x7fffffffe738 high
當 push 值進 stack 時,rsp
的位置會減足夠的大小,讓新的值可以填進來,所以後進來的 b 反而會存在較低位的地方
Buffer overflow
在讀取輸入的時候,輸入的大小超過變數的大小,此時多的部份會超越區域變數的位址,進而蓋到 stack 上的其他位置
假設有一程式如下
int key = 0xdeadbeef;
char buf[16] = {0};
gets(buf);
宣告後 stack 狀態如下
+----------------+ 0x7fffffffe720 low <-----+
| 0x0 | |
+----------------+ 0x7fffffffe728 | buf[16]
| 0x0 | |
+----------------+ 0x7fffffffe730 <-----+-----+
| 0xdeadbeef | | key
+----------------+ 0x7fffffffe738 high <-----------+
因為 gets 沒有限制輸入的長度,因此可以輸入超過 buf 長度,此時多的部份就會蓋到 stack 其他部份
例如輸入 aaaaaaaabbbbbbbbcccccccc
+----------------+ 0x7fffffffe720 low <-----+
| aaaaaaaa | |
+----------------+ 0x7fffffffe728 | buf[16]
| bbbbbbbb | |
+----------------+ 0x7fffffffe730 <-----+-----+
| cccccccc | | key
+----------------+ 0x7fffffffe738 high <-----------+
原本 key
存的位置就被 buf 的輸入蓋掉了
觀察
觀察一下code後可以發現觸發system("/bin/sh")
的條件為(int)token == 6666
而token是一個宣告在main
但從未被使用到的變數
if((int)token == 6666){
printf("wow, That's impossible to touch this token ?!");
system("/bin/sh");
}
另外可以被exploit的部份為read()
的 buffer overflow
char key[16];
read(0, key, 40);
通常這類題目可能的作法有以下兩種
蓋 return
如果stack一直往下蓋的話可以碰到return
,那就直接跳到system("/bin/sh")
就好了
不過這題有限制長度40,所以沒辦法蓋到return
蓋 token
因為token
也是宣告在stack上,應該可以找到token
的位置後直接蓋成想要的值
解法
以下解法是用蓋token的方法
找 paylod 長度
先輸入以下字串來定位
AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFF
到if((int)token == 6666)
時 stack 狀態如下
上圖可以看到token
的位置以及內容是[rbp-0x4] : 0x7fffffffdf1c ("DDDDEEEEEEEE0آ\367\377\177")
由此可知到token
之前,所需要的 payload 長度是 28 ,也就是
AAAAAAAABBBBBBBBCCCCCCCCDDDD
蓋token的值
因為這支程式是在 64位元 的環境編譯的,所以送的時候要給64位元的位址,使用 pwntool 的話只要用p64()
就可以囉
r.sendline(payload + p64(0x1a0a))