avatar

目录
简单的栈溢出实现

栈溢出 Stack Overflow

如果栈中局部变量有数组之类的缓冲区,并且程序中存在数组越界的缺陷,那么越界的数组元素就有可能破坏栈中相邻变量的值,甚至破坏栈帧中所保存的 EBP 值、返回地址等重要数据。

在编译时,设置VS-属性-C/C++-代码生成中,修改安全检查为禁用安全检查、基本运行时检查为默认值。

VS中的GS编译选项,会在函数调用时,向栈中压入随机DWORD,在IDA中标识为“Security Cookie”,Security Cookie位于EBP之前,还将.data的内存区域中存放一个Security Cookie副本。

当发生栈溢出时,会先淹没Security Cookie的值。当函数返回时,会比较Security Cookie和副本的值,当不一致时,说明栈已经溢出,程序会进入异常处理流程。

举个例子实现溢出对程序的破坏

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[8];// add local buffto be overflowed
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
while(1)
{
printf("please input password: ");
scanf("%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
break;
}
}
}

当执行到

c
1
strcpy(buffer,password);

栈内情况应依次是局部变量authenticated、局部变量char buffer[8]

Char buffer[0~3]
Char buffer[4~7]
int authenticated
EBP

程序编译后,当输入123456782时,在strcpy的时候,因为超过的buffer长度,会覆盖掉authenticated的值

在数据窗口Ctrl+G跟随EBP

跟着EBP往上数,前4个字节是authenticated,再往前8字节是buffer

可以看见输入的123456782,12345678被strcpy到了buffer中,溢出的”2”被填入到了authenticated中,这样在后面在判断authenticated的值时,authenticated的值为被我们覆盖的”2”

可以看见汇编中的判断代码为判断[ebp-0x4]是否为0,可以通过栈溢出修改authenticated为0

只需要输入任意八位数字,填满buffer,利用字符截断符NULL来填充[ebp-0x4]的值,绕过密码验证

利用截断符填充

而正常的输入错误密码情况是这样的

当我们溢出填充掉authenticated的值后,在后面判断时,会直接提示密码正确

而正确密码应该是1234567,我们输入12345688也可以提示密码正确

输入8位数字即可以淹没authenticated,而authenticated后面分别是EBP和返回地址,修改输入字符串的长度,一样可以覆盖掉函数返回地址

输入六位数字的栈帧

输入更长的字符串,淹没返回地址

接着运行,可以看见程序崩溃了,因为EIP跑到了我们刚才淹没输入的33333333,而这个地址没有指令,导致程序崩溃

既然可以淹没返回地址,也可以尝试将返回地址修改为通过验证的地址试试,前面的程序判断函数处,可以看见判断后有一个je跳转,不跳转是验证失败,跳转是验证成功

所以可以在栈溢出,淹没返回地址,直接跳转到成功函数处试试

试了很久,很多ASCII在键盘上打不出来,在010Editor里面写好十六进制复制出来也不能用,将代码改成password从txt文件获取

先在记事本中写好输入的内容

然后保存到txt文件,将程序修改为输入的password从txt中获取

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"
int verify_password(char* password)
{
int authenticated;
char buffer[8];// add local buffto be overflowed
authenticated = strcmp(password, PASSWORD);
strcpy(buffer, password);//over flowed here!
return authenticated;
}
main()
{
int valid_flag = 0;
char password[1024];
FILE* fp;
while (1)
{
fp = fopen("E:\\Mycode\\Stack overflow\\Stack OverFlow\\Debug\\11.txt", "r+");
if (fp == NULL)
{
printf("%d",GetLastError());
}
printf("please input password: ");
fscanf(fp,"%s", password);
valid_flag = verify_password(password);
if (valid_flag)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
break;
}
}
}C

运行后 OD中运行到strcpy的时候查看栈帧

返回地址已经被覆盖成了我们想要的成功函数处,继续运行,程序依然会报错。

返回地址是成功了,得到了正确的验证,但是栈帧被破坏了,程序依然会崩溃

文章作者: Yenn_
文章链接: https://0xdf1001f.github.io/2020/04/29/%E7%AE%80%E5%8D%95%E7%9A%84%E6%A0%88%E6%BA%A2%E5%87%BA%E5%AE%9E%E7%8E%B0/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Wei's Blog

评论