0x01 修改邻里变量的原理
1.1 程序:
/*****************************************************************************
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.2 verify_password 的栈帧布局:
【ESP】 char buffer[0-3] (ASCII: q q q q) |
char buffer[4-7] (ASCII: q q q null) |
int authenticated (0x00000001) |
【EBP】 上一个栈帧的EBP |
返回地址 |
形参:password =>【输入qqqqqqq】 |
…… |
分析: verify_password 函数的返回值是 authenticated ,int authenticated 的内存是 DWORD ,占4个字节,只要能够让数组越界,数组越界的部分会写到相邻变量 authenticated 的内存里,代码逻辑: authenticated 值为 0 时,验证成功,否则不成功。
那么: 如果我输入的密码超过7个字符(null 占用一个字符),越界的字符ASCII码会修改掉 authenticated 的值,如果数组的溢出数据恰好把 authenticated 修改为 0 ,那么程序执行流程改变,从而绕过密码验证。
0x02 突破密码验证程序
2.1 实验环境:
操作系统 | windows XP | 用虚拟机运行 |
---|---|---|
编译器 | visual C++6.0 | |
编译选项 | 默认编译选项 | |
build版本 | debug版本 | release和debug都可以 |
2.2 程序正常运行的情况:
2.3 调试
如果输入密码为7个 "q" , 就会有 "qqqqqqq" > "1234567" , 函数verify_password中 "authenticated=strcmp(password,PASSWORD);" authenticated值为1,OllyDbg调试内存如下:
OllyDbg调试,直接F9看能够运行到哪里,一步一步调一下,找到strcpy的位置,下个断点,走到这里看下值
2.4 帧栈分布:
局部变量名 | 内存地址 | 偏移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 |
注意:
内存数据(内存存储): 底地址 -> 高地址
数值数据:高地址 -> 底地址
例如: authenticated在内存存储: 0x 01 00 00 00, 出于便于阅读的目的,OllyDbg在栈区显示的时候把内存双字字节序翻转了,也就是栈区显示的是"数值数据" 0x 00 00 00 01。因此在栈内看数据时,从左到右对于地址偏移是3、2、1、0。
2.5 数据淹没
再次输入超过7个字符,看能否写入authenticated变量的数据区;
"qqqqqqqqrst"
能够写入到authenticated变量区;
那就刚好输入8个"q",让截断符null写入到authenticated变量里,就可以直接验证成功了啊;
再次运行,通过了验证。
2.6 数据淹没后帧栈分布:
局部变量名 | 内存地址 | 偏移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 |
authenticated 被覆盖后 | 0019FAD0 | 0x00 | 0x00 | 0x00 | 0x00(NULL) |
0x03 注意
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
break;
}
if(valid_flag) ,只有valid_flag==0的时候,才能执行正确的流程,否则仍是不正确的;
进一步理解:
当输入的字符串大于"1234567"的时候,返回值为1,authenticated的内存值为0x00000001,用字符串截断符NULL淹没authenticated的低位数字而突破验证。
当舒服字符串小于"1234567"的时候,返回值为-1,authenticated在内存中的值是补码0xFFFFFFFF,用字符串截断符NULL淹没authenticated的低位数字后为0xFFFFFF00,此时不能突破验证程序。
输入"01234567"