前言
uaf
漏洞全称是use after free
,free是指函数是在堆上动态分配空间后不再使用该改数据从而被回收。但是由于程序员的一些不适当的操作,会导致攻击者能够操控已经被释放的区域,从而执行一些byte codes。本文简述了uaf的基本原理,并使用一个简单的例子进行讲解。
正文
首先让我们看一下存在漏洞程序的源码:
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BANNER \
"Welcome to UAF practice"
struct auth {
char name[32];
int auth;
};
struct auth *auth;
char *service;
int main(int argc, char **argv) {
char line[128];
printf("%s\n", BANNER);
while (1) {
printf("[ auth = %p, service = %p ]\n", auth, service);
if (fgets(line, sizeof(line), stdin) == NULL) break;
if (strncmp(line, "auth ", 5) == 0) {
auth = malloc(sizeof(struct auth));
memset(auth, 0, sizeof(struct auth));
if (strlen(line + 5) < 31) {
strcpy(auth->name, line + 5);
}
}
if (strncmp(line, "reset", 5) == 0) {
free(auth);
}
if (strncmp(line, "service", 6) == 0) {
service = strdup(line + 7);
}
if (strncmp(line, "login", 5) == 0) {
if (auth && auth->auth) {
printf("you have logged in already!\n");
} else {
printf("please enter your password\n");
}
}
}
}
在该段代码中一共有4个功能:
auth命令进行用户注册;reset
命令对建立的用户进行释放;service
命令写入一个数据并将其复制到分配的heap上;login
命令进行登录的判断。
在使用login指令进行登录的时候,会进行两个判断,一个是auth是否存在,再一个是auth对应的auth字段是否有值存在。
auth结构体的结构是,先是32个bytes的buffer,然后跟的是一个auth的int数值。所以我们的目的就是想办法将这个auth值覆盖为不为0的数值。
auth指令用途是调用malloc()
函数分配一个auth结构体大小的空间,然后对name进行赋值,这里进行了拷贝数据长度的检测,所以不能直接通过buffer overflow对auth数据进行修改。
在分配完空间后,会使用事先定义的*auth
指针指向这个分配的区域。这里面用了太多的auth字段,可能会绕晕,但是仔细分析也不是很难懂。
在每一次运行完指令之后,都会显示auth指针指向的地址以及service的地址。细心的可以发现,在执行reset命令之后,auth指针指向的还是原来的地址。这就达成了uaf漏洞可以被利用的第一个条件,在释放了buffer之后指针没有被重置。在随后的login命令中,调用的还是原有的auth指针。此时如果我们使用service指令:
可以发现,service指针指向的地址与auth指向的地址相同。
发生这种情况的原因是因为service调用了strdup()函数。该函数代码如下:
char * __strdup(const char *s)
{
size_t len = strlen(s) +1;
void *new = malloc(len);
if (new == NULL)
return NULL;
return (char *)memecpy(new,s,len);
}
首先会调用malloc函数在heap上分配空间,然后通过memcpy对内存进行复制。
malloc()
函数存在一个特性是会将新申请分配的空间分配到之前被free()
回收的buffer区域。这也就形成了uaf漏洞可以被利用的第二个条件,使用malloc()函数重新分配空间。
这样就可以通过service命令对在login时检测的auth指针对应的buffer数据段进行修改和覆盖。
下面使用gdb仔细观察heap的分配情况并进行攻击,在每一次打印出auth和service地址后下断点进行调试:
在现实auth的地址,发现了输入的”admin”字符串。
使用reset指令使auth struct空间被释放:
然后使用查看auth->auth
对应的地址,来判断需要输入数据的长度去覆盖该区域:
可以看到login判断对应的区域为0xf7ffd280
与之前分析的一致,auth->auth
的位置时0xf7ffd280+0x20
,所以我们要准备的paylaod为32bytes的padding加上后四位的数值进行覆盖,使用service
命令写入数据:
auth->auth
位置已经被覆盖修改。
此时在输入login指令:
显示成功登录。
结语
利用uaf漏洞主要是注意几点:
- 在free()函数被调用回收buffer之后,指向该地址的指针是否被重置。
- 后续是否存在malloc()函数可以将新申请分配的空间分配到之前被free()回收的buffer区域。
- 利用没有被重置的指针进行攻击