栈溢出实践

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/swjtu100/article/details/49976089

0x00 strcpy()函数

void main()  
{  
 char s[]="123456789";  
 char d[]="abc";  
 strcpy(d,s);   printf("d=%s,\ns=%s",d,s);  
 getchar();
}
  • Visual C++6.0中运行结果:
    这里写图片描述
  • 拷贝前后栈中变量分布:
    拷贝前 —————->>>拷贝后
  • 解释:strcpy(d,s)将s中的所有字符拷贝到s中,直遇到结束符’\0’,而不检查d是否越界。

0x01 修改邻接变量

  • 不进行编译优化前提下,局部变量在栈中是相邻存放的,若局部变量中有数组等缓冲区,并且程序存在数组越界的缺陷,则越界的数组元素有可能破坏栈中相邻变量、EBP值、返回地址等
#include <stdio.h>
#include<string.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;
        }
    }
}
  • 运行结果:
    这里写图片描述
  • 错误密码通过验证原理
    函数栈中参数的分布:
    这里写图片描述
    拷贝前后栈中变量分布:
    这里写图片描述—————>>>这里写图片描述
    输入8个’q’,第9个为字符串截断符0x00,溢出至authenticated的低字节,恰好将0x00000001覆盖成0x00000000,成功通过验证。
  • 说明:
    a) 只有输入的8个字符大于”1234567”是才能通过验证。由于authenticated的值是strcmp()的返回值,输入字符串大于”1234567”是,返回1,内存中为0x00000001,可以用字符串截断符NULL覆盖authenticated的低字节通过验证。当输入字符串小于”1234567”时,函数返回-1,内存中为0xffffffff,覆盖后为0xffffff00,仍不能通过验证。
    b) Visual C++ 6.0 Debug版本编译能实现该实验,VS2010的GS(缓冲区安全检查)会使该实验失败。

0x02 修改函数返回地址

  • 通过缓冲区溢出改写函数栈帧最下方的EBP值和函数返回地址等栈帧状态值。
#include<stdio.h>
#include<string.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] = "AAAAAAAAAAAAAAAA\x0A\x11\x40\x00";
    while(1)
    {
        valid_flag = verify_password(password);

        if(valid_flag)
        {
            printf("incorrect password!\n\n");
        }
        else
        {
            printf("Congratulation! You have passed the verification!\n");
            break;
        }
}
}
  • 通过验证原理:
    程序中password[1024] = “AAAAAAAAAAAAAAAA\x0A\x11\x40\x00”,其中”\x0A\x11\x40\x00”将覆盖函数返回地址的值,且0x0040110A为密码验证成功处理分支的指令地址(该地址通过动态调试得到)。故程序能在verify_password()调用后直接跳转至通过验证分支,成功绕过密码验证。
    这里写图片描述
    拷贝前后栈中变量分布:
    这里写图片描述————- ->>>这里写图片描述
    由于EBP被覆盖为无效值,是程序退出时堆栈无法平衡,导致程序崩溃。
  • 说明:
    a) Visual C++ 6.0 Debug版本编译能实现该实验,VS2010的GS(缓冲区安全检查)会使该实验失败。
    b) 可以password[1024]中写入自己的代码,用该代码的虚拟地址覆盖返回地址实现代码植入。

——《0day安全》学习笔记

猜你喜欢

转载自blog.csdn.net/swjtu100/article/details/49976089