0x01 淹没帧状态值控制程序执行原理
/*****************************************************************************
To be the apostrophe which changed "Impossible" into "I'm possible"!
POC code of chapter 2.2 in book "Vulnerability Exploit and Analysis Technique"
file name : stack_overflow_var.c
author : failwest
date : 2006.9.20
description : demo show nearby var overrun in stack
input 8 letters to bypass authentication
Noticed : complied with VC6.0 and build into begug version
version : 1.0
E-mail : failwest@gmail.com
Only for educational purposes enjoy the fun from exploiting :)
******************************************************************************/
#include <stdio.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[8];// add local buff
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;
}
}
system("pause");
}
1.1 动态调试结果
动态调试得到的栈帧数据
局部变量名 | 内存地址 | 偏移3处地址 | 偏移2处地址 | 偏移1处地址 | 偏移0处地址 |
---|---|---|---|---|---|
buffer[0-3] | 0019FAC8 | 0x71(’q’) | 0x71(’q’) | 0x71(’q’) | 0x71(’q’) |
buffer[4-7] | 0019FACC | NULL | 0x71(’q’) | 0x71(’q’) | 0x71(’q’) |
authenticated | 0019FAD0 | 0x00 | 0x00 | 0x00 | 0x01 |
前栈帧 EBP | 0019FAD4 | 0x00 | 0x19 | 0xFF | 0x30 |
返回地址 | 0019FAD8 | 0x00 | 0x40 | 0x10 | 0xEB |
1.2
用5组数据,也就是19个字符 "4321432143214321432" 把authenticated、前栈帧 EBP、返回地址全部淹没掉,观察栈的状态
1.3 分析
返回地址用于在当前函数返回时,重定向程序的代码,在函数返回 "retn"指令时,栈顶元素恰好是这个返回地址。"retn" 指令会把返回地址弹入EIP寄存器里,之后跳转到这个地址去执行。
从调试器中看出计算机发生的事件:
1、函数返回时,将返回地址放到了EIP寄存器
2、处理器按照EIP寄存器的地址0x00323334取值
3、内存0x00323334没有合法的指令,处理器不知如何处理,报错。
这里溢出程序返回到了无效的地址,那么如果这个地址是有效的,就可以让处理器到任意指令出去执行(比如:直接跳到程序验证通过的部分),也就是说,可以通过淹没函数返回地址而控制程序的执行流程。
0x02 控制程序的执行流程
用读文件的方式处理输入值,文件中便于操作十六进制的值(0x11 0x12等)。
/*****************************************************************************
To be the apostrophe which changed "Impossible" into "I'm possible"!
POC code of chapter 2.3 in book "Vulnerability Exploit and Analysis Technique"
file name : stack_overflow_ret.c
author : failwest
date : 2006.9.30
description : demo show to redirect program execute flow via over run return address
in stack. specify the exactly fake return address in password.txt file
to bypass the authentication
Noticed : should be complied with VC6.0 and build into debug version
version : 1.0
E-mail : failwest@gmail.com
Only for educational purposes enjoy the fun from exploiting :)
******************************************************************************/
#include <stdio.h>
#define PASSWORD "1234567"
int verify_password (char *password)
{
int authenticated;
char buffer[8];
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
main()
{
int valid_flag=0;
char password[1024];
FILE * fp;
if(!(fp=fopen("password.txt","rw+")))
{
exit(0);
}
fscanf(fp,"%s",password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
}
fclose(fp);
system("pause");
}
2.1 实验环境:
操作系统 | windows10 | |
---|---|---|
编译器 | visual C++6.0 | 有个bug需要注意:在退出程序的时候,最好还是按任意键退出程序,直接关闭窗口这种操作方式,有可能会导致程序并没停止 |
编译选项 | 默认编译选项 | |
build版本 | debug版本 | release和debug都可以 |
2.2
在PE文件目录下建立password.txt,写入测试用的密码,Olldbg就可以加载调试了。准备工作:
1、通过动态调试获取栈中的状况,如函数地址距离缓冲区的偏移量等
2、需要得到密码中程序验证通过的指令地址,方便程序直接跳到这里来执行。
3、在password.txt文件中相应的偏移处填上这个地址。
这样在verify_password函数返回后就直接跳转到验证通过的分支执行了。
2.3
用olldbg加载可执行的PE文件:
分析得到:程序验证通过的指令地址: 0x0040111F
程序通过的分支从0x0040111F这里开始压栈,就把返回地址覆盖为这个地址,verify_password函数返回的时候,就直接跳转到0x0040111F这里了。
2.4
分析得到:栈中的变量分布情况没有改变
地址全部淹没的情况:
buffer 需要2组 4321
authenticated 变量 需要1组 4321
前栈帧EBP需要1组 4321
返回地址需要1组 4321
现在需要把返回地址通过winhex工具修改为: 程序验证通过的指令地址0x0040111F
这4个字节在文本模式下显示是乱码
2.5 结果
Olldbg加载调试,堆栈如下:
执行状态:运行打开几秒就崩溃了,但是跳转到了正确的结果上。
由于栈内的EBP被覆盖为无效值,程序在退出时,堆栈无法平衡,导致崩溃,但已经成功淹没了返回地址,直接跳转并通过验证。