缓冲区溢出实验proj1

任务一:

1.攻击程序分析

在这里插入图片描述
缓冲区溢出发生在此函数中的strcpy函数。根据缓冲区溢出的常见攻击方法可知,我们可以构造字符串覆盖其返回地址。查看目标程序堆栈情况如下:
在这里插入图片描述
我们查看的是foo程序,由于foo程序首先开辟buf数组空间,为256字节空间,然后则调用bar函数。
在这里插入图片描述
即将调用bar函数,我们可以通过ebp和esp看出,buf数组的起始位置应为0xbffffc4c,大小为256字节,查看内存:
在这里插入图片描述
红框部分为当前buf中的内容(未初始化)。

2.shellcode构建

使用例子程序提供的shellcode

3.Exploit中的payload构建

在熟悉了攻击程序后我们可以开始构建payload。首先我们需要找到shellcode在内存中的位置,使用find指令:
在这里插入图片描述
地址为0xbffffee9,于是我们构造payload。
Payload =shellcode + ’\x90’ (前两项共260位)+ (shellcode地址)
在这里插入图片描述
结果如下,成功获得shell:
在这里插入图片描述

任务二:

1.攻击程序分析

在这里插入图片描述
这一次的溢出代码多出了一个字符串的"大小检测"。为什么要加双引号呢,是因为他的检测并不严谨。从14行的循环条件可以看到,他其实还是允许我们溢出一个字节的。
溢出一个字节不能像实验一那样直接对返回地址进行修改,但我们可以利用ebp。根据栈的知识可以知道,在内存中,buf的下部就是ebp。溢出一个字节可以改变ebp的最低位,也就是说我们可以将ebp指向一个我们能控制的区域,也就是buf数组中。

2.shellcode构建

使用例子程序提供的shellcode

3.Exploit中的payload构建

有了以上思路我们开始构建payload,首先我们肯定是需要一个201字节的数组payload。
在这里插入图片描述
首先查看buf数组的地址范围:0xbffffcb8 - 0xbffffd80 共200字节。
此时,ebp刚刚入栈,值为:
在这里插入图片描述
我们尝试通过溢出将ebp改为0xbffffd00,这个地址是在buf当中的。
于是:
在这里插入图片描述
接下来我们需要思考如何在buf中嵌套shellcode的起始地址,供跳转指令使用。
由于在函数返回的时候会出现三条指令:
Mov esp, ebp
Pop ebp
Pop eip
我们关心的是eip寄存器的值,所以不难看出,shellcode的地址应该放在ebp所指向的地址+4的位置。
由于此时我们的ebp指向0xbffffd00,相对于buf数组的起始地址0xbffffcb8相差72个字节。所以:
在这里插入图片描述
Payload构造完成。
结果如下:
在这里插入图片描述

任务三:

1.攻击程序分析

1)main函数
在这里插入图片描述
首先需要了解strtoul函数的功能。他从参数一(argv[1])中的头部开始,将argv[1]中的数字或-号以非符号int型数的形式返回,在这里被赋值给了count,但是在这里是以int型赋值给count的。
2)foo函数
在这里插入图片描述
首先定义了一个buf数组,根据widget_t结构体定义,我们能算出buf数组占用的字节空间大小为: (8+8+4)*1000=20000。
然后通过count进行判断,若判断成功则将in当中的字符串拷入buf内存中。
在这里问题就是这个count,由于之前我们分析过count是从strtoul函数返回值来的,但strtoul函数的返回值本是一个无符号整数,count却是一个有符号整数,那么我们运用负数则可以绕过MAX_WIDGETS的判定成功进入if语句。并且,大负数与sizeof(struct widget_t)相乘后得到的结果,可能将负数的符号位舍去,使得其又变回正数。

2.shellcode构建

使用例子程序提供的shellcode

3.Exploit中的payload构建

首先我们来计算count的值。由于之前分析buf占用空间为20000个字节,所以我们必须要溢出。20000 = 20*1000,那我们不妨选择1001这个数字。
1001的十六进制表示为0x3E9,将其符号位置1,则为0x800003E9。由于在计算机中,负数使用补码存储,所以我们需要将0x800003E9取补码,结果为2,147,482,647,再取负,所以我们的count为-2147482647。
在这里插入图片描述
Payload构建如上,不难看出,结构为”-2147482647” + “,” + shellcode + nop + ebp + 返回地址。同之前的实验我们需要寻找返回地址,即shellcode的地址:
在这里插入图片描述
结果如下:
在这里插入图片描述

任务四:

1.攻击程序分析

首先,攻击程序开门见山的告诉了我们
在这里插入图片描述
那么我们只能另寻他路了。
根据对foo函数的查看,我发现了一个有些特别的地方:
在这里插入图片描述
明明只有两次tmalloc,为什么出现了三次tfree?
首先我们需要知道tmalloc和tfree是干什么的。
根据tmalloc.c中注释,tmalloc是一个用来/* create a remainder chunk */
其实应该有点像malloc这类的函数,用来申请空间的。
那么tfree则应该是用来释放空间的。但在查看tfree之前,先看一下chunk这个结构体的结构:
在这里插入图片描述
不难发现,chunk是一个类似于双向链表的结构,他当中有左指针和右指针,分别指向左侧和右侧的chunk结构体。Chunk结构体的大小为8字节,其中右指针的低位还包含了一个叫free bit的符号位,它是用来标注是否为空的。
于是我们开始分析tfree:
在这里插入图片描述
其思想已经写在备注里了,我们注意到他是一个双向指针删除的操作,并且会将空闲空间进行合并。

2.shellcode构建

使用例子程序提供的shellcode

3.Exploit中的payload构建

其实分析完攻击程序,我除了发现多了一个tfree之外毫无头绪。于是我只能从这里下手了。我们知道,缓冲区溢出攻击的常规途径有:覆盖返回地址和覆盖ebp。但这里我们的缓冲区无懈可击,那谁来帮我们覆盖返回地址呢?只能是这个tfree了。
首先我们看一看前面的两次tmalloc:
在这里插入图片描述
申请的两个数组地址如上。
在这里插入图片描述
之后,明明是对p进行tmalloc,怎么突然对q进行了tfree操作呢?
我们可以发现,此时的p申请内存为1024字节,基地址为0x804a068,是会覆盖q之前的地址的,也就是说我们可以通过p影响q。
于是,tfree函数的功能就显现出来了。
在这里插入图片描述
假设当前区域我们使得一个叫current的指针指向,那么上面这段代码做的是:
Current.left.right = current.right;
Current.right.left = current.left;
不难发现,他其实是可以通过这一部分来“交互”左右的地址的。这里我也没找到什么好的词语,我们直接开始构造payload,就很清晰了。

首先我们要确定,payload的哪一部分是q的块信息,也就是q数组对应的CHUNK结构体。我们知道之前,p[500]和q[300]的基地址相差512字节,也就是说p数组的最后一个单元之后,还存在12字节不知道被用来干什么了。那么这里,这12字节中的4-12字节就是用来存储q的块信息,也就是payload[504] - payload[511]。前四个为left指针地址,后四个为right指针地址。
在构造payload之前我们需要找到foo函数的返回地址:
在这里插入图片描述
即0xbffffa68 + 4 = 0xbffffa6c。
然后我们需要将shellcode放入payload中,所以说我们的目标地址就应该是p数组的起始地址,即0x0804a068。
在这里插入图片描述
于是我们构造payload:

在这里插入图片描述
但是,我们还要注意的是,tfree函数中的if条件:
在这里插入图片描述
也就是current.left.right的free bit要为1。我们知道current.left是指向p数组的起始地址,即0x0804a068的,那么他的.right的地址就应该是p[4],也就是payload[4]。
在这里插入图片描述
但到这里还没有结束,因为tfree同样会执行current.left.right=current.right的操作,所以payload的5-8个字节可能会被重新赋值,就会导致寻址出错,这里我们使用一个跳转指令,跳过这一段,防止不明指令的执行:
在这里插入图片描述

最后的payload:
在这里插入图片描述

执行结果如下:
在这里插入图片描述

任务五:

1.攻击程序分析

在这里插入图片描述
不难发现,我们的攻击程序中涉及缓冲区的是foo函数。它申请了一个400字节的缓冲区,并使用snprintf函数将arg中的内容拷入缓冲区中。
在这里插入图片描述
Snprintf函数特征如上,但这个函数对于格式化字符串则拥有特殊的用途。常见的格式化字符串参数有:
%d, %u, %c, %n…
其中%n是唯一能改变地址中值的参数的格式化字符串参数。所以毋庸置疑我们需要用这个参数来对地址进行修改。

2.shellcode构建

使用例子程序提供的shellcode

3.Exploit中的payload构建

首先我们查看snprintf函数前的栈情况:
在这里插入图片描述
在调用函数之前,栈中除了buf缓冲区,还以以下顺序压入了函数的参数:
Arg, sizeof buf, buf, snprintf返回地址。
通过此时的ebp我们不难推导出,snprintf返回地址的地址为0xbffffb2c。
也就是说我们要向0xbffffb2c中写入我们的shellcode地址。
在这里插入图片描述
于是我们先构造format。
在这里插入图片描述
以及shellcode。
由于%n写入的是已经输出的字符个数,所以我们需要对输入进行构造。当前format中已经存在32个字符了,而我们根据寻找arg地址发现,arg的地址为:
在这里插入图片描述
由于arg前半部分有我们构造的格式化字符串,我们直接跳过这部分指向shellcode,所以我们的返回地址应该改为0xbffffea2。
1)0xa2 = 162
2)0xfe = 254
3)0xff = 255
4)0xbf = 191
于是就变成了构造以上个数的字符串
1)162 = 32 + 130
2)254 = 162 + 92
3)255 = 254 + 1。但是这里我们不能用%1u%n,因为这并不能表示一个字符输出。所以我们将255 = 0xff转化为0x1ff = 511。则511 = 254 + 257
4)同上,将0xbf转化为0x2bf = 703 = 511+192.
于是!我们的格式化字符串就诞生了:
在这里插入图片描述
总的payload如下:
在这里插入图片描述

运行结果:
在这里插入图片描述

任务六:

1.攻击程序分析

在这里插入图片描述
这个程序和任务二的程序看上去很相似,溢出方式都是利用单字节溢出来修改ebp的值。但是不同的是,这里的foo函数出现了一个_exit(0)函数,如果这个函数正常运行,那么我们的shell就没法正常执行。所以我们这次需要做的是利用缓冲区溢出来阻止这个函数执行。

2.shellcode构建

使用例子程序提供的shellcode

3.Exploit中的payload构建

首先查看foo函数的ebp:
在这里插入图片描述
由于我们需要绕开foo函数中的exit,所以我们通过反汇编查看foo函数工作原理:
在这里插入图片描述
我们注意到,foo函数中有一个指针和一个整型变量:
在这里插入图片描述
而红框标注的两步操作在反汇编中不难发现,p的地址应该为ebp+4;a地址应该为ebp+8。于是,由于我们可以决定ebp的值,也就意味着我们可以手动指定p和a(通过payload)。
但是我们需要做什么呢,首先我们应该查看_exit函数的反汇编。根据foo的反汇编,我们发现调用exit函数发生在内存0x0804858b中,我们查看这一段对应的指令:
Call 0x8048380。
也就是说,0x8048380应该是exit函数的起始地址。我们查看反汇编:
在这里插入图片描述
这里它使用了段地址寻址,我们不用想那么多,直接注意到0x804a00c地址。也就是说,要想完成exit,就要跳到0x804a00c地址。那要想不完成exit,我们就可以利用这个0x804a00c所指向的空间,如果0x804a00c = shellcode的地址,那么不就意味着我们直接运行了shellcode吗?
修改方法就是利用p和a。
于是我们构造payload:
在这里插入图片描述
其中,payload[200]为溢出字节,它使得溢出后,ebp的值被修改成了0xbffffd00,对应buf区域为buf[80]。所以buf[72]-buf[75]为a,buf[76]-buf[79]为p。
由于
p = a.
则我们应该将a构造为目标地址即返回地址,将p构造为要修改的目标地址,即0x0804a00c。

运行结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/HizT_1999/article/details/106941098