uaf漏洞原理浅析

Use after free漏洞原理浅析实例

Posted by Weizhou on January 24, 2019

前言

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字段,可能会绕晕,但是仔细分析也不是很难懂。

首先按照正常的逻辑运行一次程序: image.png

在每一次运行完指令之后,都会显示auth指针指向的地址以及service的地址。细心的可以发现,在执行reset命令之后,auth指针指向的还是原来的地址。这就达成了uaf漏洞可以被利用的第一个条件,在释放了buffer之后指针没有被重置。在随后的login命令中,调用的还是原有的auth指针。此时如果我们使用service指令: image.png

可以发现,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地址后下断点进行调试:

image.png

在现实auth的地址,发现了输入的”admin”字符串。

使用reset指令使auth struct空间被释放:

image.png

然后使用查看auth->auth对应的地址,来判断需要输入数据的长度去覆盖该区域: image.png

可以看到login判断对应的区域为0xf7ffd280与之前分析的一致,auth->auth的位置时0xf7ffd280+0x20,所以我们要准备的paylaod为32bytes的padding加上后四位的数值进行覆盖,使用service命令写入数据:

image.png

auth->auth位置已经被覆盖修改。

此时在输入login指令:

image.png

显示成功登录。

结语

利用uaf漏洞主要是注意几点:

  • 在free()函数被调用回收buffer之后,指向该地址的指针是否被重置。
  • 后续是否存在malloc()函数可以将新申请分配的空间分配到之前被free()回收的buffer区域。
  • 利用没有被重置的指针进行攻击