栈溢出
目标
学会使用淹没相邻变量或返回地址的方法利用缓冲区溢出漏洞。
测试步骤与结果
本次实验的源码如下所示
1 |
|
验证缓冲区溢出的发生
- 正常情况下,输入正确密码,程序提示 “输入正确”;输入错误密码(少于 8 位),程序提示 “输入错误”。
![]()
- 进入 ollydbg,在函数调用处打断点。开始运行程序,输入一个较短的密码,就以
"444"为例。![]()
strcpy的调用点在0x401055处。![]()
- 输入的密码存储在
0x12fb7c处,要拷贝到0x12fb18处。![from]()
![to]()
0x12fb18处的数据如下,显示为00 34 34 34,恰好是"4"的 ascii 码十六进制表示![]()
淹没相邻变量改变程序流程
拷贝前后,内存的变化如下


拷贝前后,内存变化如下




淹没返回地址改变程序流程
为了方便调试,我们使用文件来输入 “密码”。
1 | if(!fp=fopen("password.txt","rw+")) |
- 找到输入点。这个地址跳转到 “验证成功” 的输出上。
![]()
- 先填满 8 字节的
buffer数组,4 字节的authenticated变量,4 字节的 ebp,接下来的 4 个字节我们就可以放上我们的返回地址。![]()
- 运行程序,显示 “验证成功”,但由于直接跳转地址破坏了栈平衡,程序崩溃。
此时的栈结构如此图所示 ![]()
测试结论
覆盖的过程中发生了什么?我认为可以用以下几张图来表示




思考题
程序的源码如下
1 | /* |
本次的目标是通过栈溢出,想办法使bar函数得到执行。
-
程序通过命令行参数执行了
foo函数,在foo函数中,有一个 10 个字节长的buf字符数组,在第 14 行中发生了未经检查的无界向有界拷贝的行为,很容易引发溢出。分析到这里,思路就很简单了:参数中先用任意字符填满buf
,然后再填入 4 字节的bar函数的地址。 -
先调试,填上一个较短的参数看看发生了什么
![填入参数3344]()
![运行]()
-
发现我们给的这个参数在 DS 段中存储着,而且运行时程序会打印出
foo和bar函数的地址值。所以bar函数地址我们已经有了。 -
我们的命令行参数从 DS 段拷贝到了
0x12ff60的位置。![]()
![]()
![]()
![]()
-
构造 payload 时要注意考虑到内存的对齐因素,缓冲区是 10 个字节长没错,但是填充时要淹没掉
0x40109b的返回地址值,又要使最后 4 个字节为bar函数的地址值,所以要填上 12 个任意字符。综合考虑,构造 payload 如下。
![]()
-
把这个值复制到调试命令行中,重启程序
![]()
-
此时已经可以看到
0x12ff6c中存储的返回地址值变成了0x401060。前面的0x12ff60~0x12ff68也已悉数占满。
![]()
-
按 F8 单步跟下来,可以发现程序紧接着跳到了
0x401060,bar函数的地址,目标达到了。紧接着程序就因为栈不平衡而崩溃了。![]()
![]()
-
覆盖返回地址法利用全过程图示
![图中红笔为溢出执行过程]()
接下来,我们使用jmp esp的方法来完成程序的破解。
-
使用 od 自带的插件 overflow return address 寻找可用的
jmp esp地址,查找结果如下![]()
![]()
-
本次实验选用了一个位于
ntdll.text段的jmp esp,地址为77f8948bh。根据
jmp esp构造相关知识,构造 payload 为 12 个填充字符 "a"+jmp esp地址0x77f8948b(大端书写)+shellcode(call bar()的机器码)。下面简单看一下运行该参数之后,栈的情况。
![payload图示]()
-
foo和bar的地址输出等同于方法一![]()
![]()
-
将命令行的内容复制到缓冲区时,观察输出,可以发现
0x12ff6c被覆盖成了jmp esp的地址![而临接地址的内容,就是本次我们要执行的shellcode]()
-
在函数
return之前,注意到它即将返回到一个内核地址77f8948b
![]()
-
陷入内核的一瞬间,可以看到
eip指针所指向的位置上,命令为jmp esp。观察右侧知esp现在的值为12ff70h,接下来,程序到12ff70h执行内容。![]()
-
跳转到
12ff70地址后,其中所写入的数据都被当作指令来执行,我们的 shellcode 被成功解析为了call指令。![]()
-
果然它紧接着调用了
00401060处的bar函数。![]()
-
调用完成后,它返回了
12ff70处,然后继续向下执行指令,直到崩溃。![]()
如果没有任何中断的话,接下来地址的所有的 hex 数据都会按照命令来执行,相当危险……
-
刚才执行的过程中成功产生预期输出。
![]()
-
刚才的 payload 可以正常在命令行中传参执行,成功触发
bar函数。![]()
-
jmp esp法利用全过程图示![图中红笔为溢出执行过程]()











此时的栈结构如此图所示 

























