前言
本片博客是复现《0day安全:软件漏洞分析技术》2.2节修改邻接变量,顺便加点自己在实践中的小问题。虽然这本书是2011年发行的,但对我这种小白来说还是颇为友好的。废话不多说正式开始。
原理
简单的说就是当局部变量var1在数组array1前声明时,如果没有array1对进行越界检查,则可能导致array1的在堆栈中超出原本的长度,对var1进行覆盖。如下图所示:
Windows
复现环境
操作系统:windows xp sp2 home
ollydbg:1.1
虚拟机:VMware16
程序:stack_overflow_var.exe
源代码
/*****************************************************************************
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;
}
}
}
操作步骤
- ollydbg 打开相应程序
打开后我们发现这不是我们编写好后main函数的位置,真正位置一般在GetCommandLineA()函数后第5个函数,如下如所示
F7单步步入,F2下断点,方便我们后续F9快速找到main函数,找到我们想要的入口点后,如下图所示
对照着源代码和相应的ascii码,我们不难看出1是printf函数,2是scanf函数,3是verify_password函数。
这里有个值得注意的地方,执行到scanf函数的时候,ollydbg红色标记的位置是不一样的,如下面两张图所示,只有运行到第一张图的时候,程序才接受用户输入,接着按回车,返回ollydbg,才能继续调试
接着F7步入第三个函数,F8执行完strcmp函数后,如下图所示
因为我们输入的123456789比原密码1234567大,所以经过strcmp后EAX寄存器的值就变成了1,现在我们即将执行0x00401049,此处指令的含义是将EAX的值赋给EBP-4所指的地址当中,从图中可以看出EBP=0x0012FB24,那么EBP-4=0x0012FB20,从上图中可以看出0x0012FB20为0xCCCCCCCC,我们接下来执行以下,如下图所示
0x0012FB20确实变成了0x00000001,接着F8来到strcpy函数。由于没有做越界检查,我们输入了9个字节,但是程序只给buffer数组开辟了8个字节的栈空间,那么9肯定会覆盖到下一个字节上,也就是0x0012FB20的位置上,如下图所示
继续按F8,直到返回main函数,紧接着就是mov和cmp的指令
意思是拿0和[EBP-4]比较,比较之后开始跳转,因此我们可以推断出[EBP-4]所代表的值就是源代码中的valid_flag,同时也是authenticated。[EBP-4]有时又EAX赋值的,EAX的值我们需要返回到verify_password中看,重新F9,F8,如下图所示
EAX的值是又[EBP-4]=[0x0012FB20]的值也就是我们覆盖的9,这证明了通过栈溢出确实覆盖了邻接变量。
值得一提的是在verify_password中最后还有一个call调用,如下图所示,那个其实是做堆栈保护的,我们只是修改了邻接变量并没有改变函数流程,所以这个保护机制没有起到作用。
Linux
既然windows下会发生,Linux下也不会例外,下面我们看以下Ubuntu下的案例吧,这个是复现《黑客之道漏洞发掘的艺术》第二版
复现环境
操作系统:ubuntu21.04
gcc 10.3
gdb
虚拟机:VMware16
程序:stack_overflow_var.exe
源代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int check_authentication(char *password){
int auth_flag=0;
char password_buffer[16];
strcpy(password_buffer,password);
if(strcmp(password_buffer,"brillig")==0)
auth_flag=1;
if(strcmp(password_buffer,"outgrabe")==0)
auth_flag=1;
return auth_flag;
}
int main(int argc,char* argv[]){
if(argc<2){
printf("usage:%s <password>\n",argv[0]);
exit(0);
}
if(check_authentication(argv[1])){
printf("\n===============================\n");
printf(" Access Granted.\n");
printf("\n===============================\n");
}else{
printf(" Access Denied.\n");
}
}
从源代码中可以看出程序的本义是只有输入brillig或者outgrabe才能正确输出但实际上确是当我们输入大于28个字符时也能够使程序输出“Access Granted.”,至于为什么要28个以上就到gdb中调试看内存了。这里在编译时取消了gcc的安全优化。
操作步骤
- gcc -q auth_overflow
list 查看代码,可以按回车看完所有的代码,我们在第8行和13行下断点,开始运行
可以看出程序已停在第一个断点处,查看auth_flag以及password_buffer位置
0x7fffffffddac-0x7fffffffdd90=0x1c=28,意思是在栈中password_buffer起始位置距离auth_flag的位置有28个字节那么远,再查看password_buffer附近64个字节,不难看出两个变量在栈中的分布
,注意从右往左看。接着cotinue,来到到第二个断点,同样方法查看内存,发现auth_flag位置也被覆盖了,这样就导致了即使输入了错误的字符串也能绕过程序。