《信息系统安全》对教材P297覆盖堆栈中变量的内容示例程序的调试与解析

程序代码:

#include <stdio.h>
#include <string.h>
#define PASSWD "1234567890"

int verify_passwd( char *passwd)
{
int authenticated;
char buffer[8];

authenticated = strcmp( passwd, PASSWD);
strcpy(buffer, passwd);
return authenticated;}

void main(){

int valid_flag= 0;
char passwd[1024];

while(1){
    printf("please input passwd:");
    scanf("%s",passwd);
    valid_flag=verify_passwd(passwd);
    if(valid_flag)
    printf("incorrect passwd!\n\n");

    else{
    printf("Correct password!\n");
    break; 
    }
  }
}

子函数存在溢出:

int verify_passwd( char *passwd)
{
int authenticated;
char buffer[8];

authenticated = strcmp( passwd, PASSWD);
strcpy(buffer, passwd);//溢出处
return authenticated;
}

编译:

  • 编译为32位程序,保护全部关闭,方便调试:
gcc -m32 -z execstack -no-pie -fno-stack-protector -z norelro  -o test_x32 test.c

请添加图片描述

调试:

  • 断点下在子程序入口:

请添加图片描述

  • 测试输入到变量passwd的值为“aaaaaaaa”,用于之后在子函数填满buffer[8]:

请添加图片描述
一直步入到strcmp函数的调用

  • 查看栈中数据:

‘aaaaaaaa’是strcmp函数的第一个参数,0x8048620存储的‘1234567890’为第二个参数
注意栈底ebp=0xffffcc38
请添加图片描述

之前执行了strcmp函数,发现其结果也就是eax存入了[ebp-0xc],那么我们可以推断程序中定义的比较结果标志变量authenticated的地址是[ebp-0xc]
请添加图片描述

  • 步入strcpy函数附近:

请添加图片描述

  • 再步入至strcpy函数调用处:

请添加图片描述

  • 此时查看栈内数据:

请添加图片描述观察栈空间发现,下一步应该就是向栈顶对应存入的地址(0xffffcc24)写入‘aaaaaaaa’

  • 再往下执行一步:发现写入成功

请添加图片描述strcpy函数是存在漏洞的,如果输入的数据足够大,那么buffer的地址装不下而向上溢出,这里从图可以看出,buffer的地址为0xffffcc24

  • 查看此处数据:

发现了我们写入的8个a,
请添加图片描述而之前我们知道标志变量authenticated的地址是[ebp-0xc],计算栈偏移,那么就是:
0xffffcc2c,观察栈上此地址处元素为0:

同时与buffer[8] (0xffffcc24)的距离为8,所以标志变量authenticated与buffer地址相邻的,那么我们可以输入超过8字符的数据实现对标志变量authenticated的覆盖

溢出示例:

  • 多输入一个‘b’

请添加图片描述

  • 查看strcpy函数执行后的栈空间:

请添加图片描述
发现

标志变量authenticated被覆盖为‘b’,实现了栈溢出

总结:

程序溢出点在于scanf函数和strcpy函数

  1. scanf函数并未对passwd输入大小做限制
  2. strcpy函数无法对复制到buffer的数据大小做限制
  3. 可以说,buffer的设置对这个程序整体来说本来就没有意义,写入后没有对buffer有任何别的操作
  4. strcpy(buffer, passwd);放在authenticated = strcmp( passwd, PASSWD);之后我们能理解写程序的人的良苦用心,就是为了告诉我们:我们可以把标志位给覆盖,

解决假设:

如果把这个strcpy(buffer, passwd);放在strcmp函数之前,那么即使authenticated被覆盖了,后执行strcmp函数也是会被修改为0或1的,就不会影响strcmp函数其比较结果了

那么这样这个程序就安全了吗?其实并不是,这里就不深入探讨了

拓展:

Tips:这里涉及到一点点Pwn入门知识

绕过思路:

实现此程序的栈溢出,修改变量authenticated内容为0,则通过主函数的判断检查

尝试输入8个a加一个0
请添加图片描述
接收到“incorrect password!”,验证失败

分析

此程序scanf接收的是char类型的字符串,通过scanf正常输入是无法写入我们想要的0的,即使我们在输入时输入0,想要通过检查,可是写到内存地址上的数据还是0吗?
答案是:不是0了,而是0x30

那么我们只能想办法让输入的溢出数据存在内存中为0x00才能实现溢出,如何实现?
32位程序定义的整形int为4字节,那么我们就需要覆盖这4字节

绕过实现

利用脚本,实现byte类型的0的写入

from pwn import*
io= process('./test_x32')
io.sendline(b'a'*0x8+p32(0))
print(io.recv().decode('utf8'))

运行结果:

请添加图片描述
接收到“Correct password!”,验证通过!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值