直接上题:
好大的栈溢出……
还有 backdoor。
先溢出到 backdoor 观察一下先。
payload = b'a'*0x68 + p64(backdoor)
p.send(payload)
注意是 si 单步进入。
可以看见跳转到了 0x7df595569d30。
x/40i:
vmmap 查看,鉴定为来自 ld,gdb 暂时无法解析。查看 0x401000:
注意到它们 jmp 的相同,但 push 的不同;所有的函数最后都 push [rip + 0x2302],然后 jmp [rip + 0x2303] <0x7df595569d30>;
在经过这个神必函数后,@got.plt被填上相应libc的地址,下次调用直接跳转:
所以可以得知,我们的 payload 在 padding 后面加上 p64(0x401020) + p64(0x2),就会执行 exit。
试试:
payload = b'a'*0x68 + p64(wtf) + p64(0x2)
p.send(payload)
确实如此:
试试把 0x2 换成 0x3:
程序崩了:
查询资料可知,之前的神必函数叫做 _dl_runtime_resolve,然后这个报错是因为
_dl_runtime_resolve 调用了 _dl_fixup,里面有一句:
_dl_fixup(struct link_map *l,ElfW(Word) reloc_arg)
/*......*/
assert(ELF(R_TYPE)(reloc->info) == ELF_MACHINE_JMP_SLOT);
//就是这句话导致了报错。
Elf64_Word 就是整数,就是我们传入的 0x3。
那么前面的 struct link_map 就很有意思了,这就是需要我们构造的贵物,查看资料得知在调用 dl_runtime_resolve 前就已经传入。
众所周知第一个参数在 rdi 上,我们跟进神必函数。
从 rbx 的偏移中读取数值,其中就有我们传入的 0x2。
这也意味着有个关键的东西 (struct link_map) 也被 push 了进去,重开再观察一次。
[rip + 0x2302] 就是我们要找的 link_map;
dl_runtime_resolve 的反汇编中,rsp 传给了 rbx:
因此,我们的 payload 可以变为:
fake_link_map_addr = 0x0 # 因为不知道在 stack 上怎么伪造,所以需要 stack pivot 把输入搞到地址确定的 .bss 上.
enter = 0x401026
payload = b'a'*0x68 + p64(enter) + p64(fake_link_map_addr) + p64(0x2) # 0x2 的“意思”就是 exit,当然要是真伪造成功,肯定就不是 exit 而是 system 了。
p.send(payload)
先学习栈迁移吧。