PWN简单堆栈溢出漏洞利用(一)
题目需要读取flag文件才能正确显示出flag,也就是下文中显示的 This is flag
创建办法:
在/home/pwn/pwn0目录下创建一个名为flag文件,打开并写入This is flag
1. 运行程序、查看文件类型、保护措施
程序让我们输入一段字符串,并返回我们所输入的内容。
看到是一个 32位 动态链接的ELF文件
2.反汇编分析
将程序拉到IDA分析,发现有getFlag()
、foo()
函数;
打开main()
,按F5
显示对应伪C代码
看见调用了foo()
,那也就顺势看看foo()
里面有什么;注意一点foo
传递了一个参数
从函数体中看:
先输入一个字符串,变量result
存储输入的字符串;
判断a1
值是否等于特定值,如果相等result
存储的值变成getFlag
的返回值
那么能否成功执行getFlag
关键就是将a1
值修改为特定值(1633xxxxx),而非传入的参数值(3054xxxxx)
栈溢出漏洞通常出现在输入的地方,所以重点检查foo()
。
gets()
函数通常不会检查输入字符串的长度,因此可以通过输入的字符串覆盖修改a1
;(a1
作为参数传入函数,比函数中的临时变量s
高位)
3. 调试分析payload
按照上面分析,需要在输入字符串时准确覆盖变量a1
的值,那么问题就转换为:payload长度是多少?payload内容是什么?
-
payload长度是多少?
IDA中打开
foo()
查看变量s
定义处,IDA分析变量s
距离栈底是1c
,注意这里是到达栈底ebp上一个内存地址的距离。也就是s
到达变量a1
中间还有栈底(ebp)、返回地址(旧eip)。
好,那么(结合前面确定是32位程序)现在计算一下一共需要输入几个字节才到达变量a1
顶上的内存地址:
0x1c + 0x4(ebp) + 0x4(eip) = 0x24 = 36
最后确定payload长度为:36+4(覆盖a1)=40
-
payload内容是什么?
已经确定了payload长度为40,其中前36个为无实际意义填充,最后4个应该让
a1
满足if条件语句的内容,也就是a1 == 1633771873
但不是直接填入1633771873。我们先将1633771873转换为十六进制形式为:0x61616161。当然也不是填入十六进制形式,因为输入的这个数在机器里面存储的并不是这个数。我们应该将数字转换为对应字符串然后输入字符串,使用pwn p32转换一下结果为
aaaa
最后确定payload内容为:A*36+a*4
我们尝试测试一下payload的正确性:
可以看到getFlat()
被成功运行了。
4. 编写payload利用脚本
from pwn import *#导入pwn库
pwn = process('./pwn0')#创建一个对象名为pwn;也就是导入程序
print pwn.recvline()#读取一行程序输出后,print打印出来(so,can you find flag?)
payload = 'A' * 36#构造payload无实际意义填充部分
payload += p32(0x61616161)#用p32转换字符,加在payload末尾
pwn.sendline(payload)#向程序输入一行数据,内容为payload
print pwn.recvline()#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaa
print pwn.recvline()#This is flag
5.总结
-
gets()函数是不会检查你输入的字符串长度的
-
大部分简单的溢出漏洞都出现在让我们输入的地方
-
栈写变量从高地址向低地址写,写数据从低地址向高地址写
顺序写入 var a、var b 那么,var a位于高地址,var b位于低地址
顺序写入数组 [10,12] 那么,10位于低地址,12位于高地址
-
内存存储数字可能未必是我们输入的数字,可能需要转换为字符串,然后输入