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

从Chrome渲染器代码执行到内核:MSG_OOB漏洞分析与利用

从Chrome渲染器代码执行到内核:MSG_OOB漏洞分析

引言

2025年6月初,我在审查Linux内核新功能时了解到面向流的UNIX域套接字支持的MSG_OOB特性。在审查MSG_OOB实现时,我发现了影响Linux >=6.9的安全漏洞(CVE-2025-38236),并向Linux报告了该漏洞,随后得到了修复。有趣的是,虽然Chrome不使用MSG_OOB特性,但它在Chrome渲染器沙箱中暴露了此功能。

该漏洞很容易触发,以下代码序列会导致UAF:

char dummy;
int socks[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
send(socks[1], "A", 1, MSG_OOB);
recv(socks[0], &dummy, 1, MSG_OOB);
send(socks[1], "A", 1, MSG_OOB);
recv(socks[0], &dummy, 1, MSG_OOB);
send(socks[1], "A", 1, MSG_OOB);
recv(socks[0], &dummy, 1, 0);
recv(socks[0], &dummy, 1, MSG_OOB);

背景:MSG_OOB特性

2021年通过commit 314001f0bf92("af_unix: Add OOB support",在Linux 5.15中落地)添加了对AF_UNIX流套接字使用MSG_OOB的支持。此功能允许发送单个字节的"带外"数据,接收方可以在其余数据之前读取该数据。

该功能非常有限 - 带外数据始终是单个字节,并且一次只能有一个待处理的带外数据字节。此功能几乎只在Oracle产品中使用,但由于Chrome渲染器沙箱允许面向流的UNIX域套接字且未过滤send()/recv()函数的flags参数,这个深奥的特性在沙箱内可用。

背景:漏洞及其成因

2024年中,发现了一个用户空间API不一致性问题,当尝试从包含由接收OOB SKB留下的剩余长度0 SKB的接收队列读取套接字时,recv()可能虚假返回0(通常表示文件结束)。修复此问题的补丁引入了两个密切相关的可导致UAF的安全问题。

漏洞的根本原因是:当接收队列包含由recv(..., MSG_OOB)留下的剩余长度0 SKB时,manage_oob()会删除剩余长度为0的SKB并跳转到后续SKB,但没有经过skb == u->oob_skb检查,这意味着它不会在正常接收路径消耗SKB之前清除->oob_skb指针,从而创建悬空指针。

初始利用原语

该漏洞产生一个悬空的->msg_oob指针。使用此悬空指针的唯一方式是通过带有MSG_OOB的recv()系统调用,该调用在unix_stream_recv_urg()中实现。

在高层次上,对state->recv_actor()的调用提供了一个读取原语:它尝试将oob_skb引用的一个字节数据复制到用户空间。该漏洞产生的唯一写入原语是当未设置MSG_PEEK时发生的递增操作UNIXCB(oob_skb).consumed += 1。

利用策略

由于此问题相对直接地导致半任意读取(受用户复制强化限制),但写入原语更加复杂,我决定采用以下通用方法:首先使读取原语工作;然后使用读取原语协助利用写入原语。

设置读取原语

在目标Debian内核上,struct sk_buff位于skbuff_head_cache SLUB缓存中,通常使用order-1不可移动页面。我通过分配大量order-0不可移动页面来耗尽order-0和order-1不可移动空闲列表,以增加将order-1页面重新分配为order-0页面的成功率。

之后,我创建41个UNIX域套接字,并使用它们每个产生256个SKB分配。然后设置包含悬空指针的SLUB页面,尝试将此页面完全刷新到伙伴分配器中,并通过使用256个管道每个分配2个页面将其重新分配为管道页面。

读取原语的属性

基于copy_to_user()的读取原语的一个很酷的方面是,即使在无效的内核指针上调用它也不会崩溃 - 如果内核内存访问失败,recv()系统调用将简单地返回错误(-EFAULT)。

主要限制是用户复制强化(__check_object_size())会捕获尝试从某些特定内存范围读取的操作。

定位内核镜像

此时有多种选项可以破坏内核镜像的KASLR,部分得益于copy_to_user()在访问无效地址时不会崩溃。一个不错的选择是通过固定地址0xfffffe0000000000(CPU_ENTRY_AREA_RO_IDT_VADDR)处的只读IDT映射读取中断描述符表(IDT)条目,从而获取内核中断处理程序的地址。

重新分配目标:CONFIG_RANDOMIZE_KSTACK_OFFSET的魔力

最终我意识到我一直走错了路。显然尝试以堆对象为目标是不明智的,因为有更好的选择:可以将目标页面重新分配为内核栈的顶部页面!

Debian的内核配置启用了CONFIG_RANDOMIZE_KSTACK_OFFSET=y和CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y,导致每个系统调用调用随机将堆栈指针向下移动最多0x3f0字节,粒度为0x10字节。这本来是一个安全缓解措施,但在我已经有任意读取的情况下对我有利。

重新分配SLUB页面为栈页面

为了获得在栈页面中递增释放后值的能力,我再次开始耗尽低阶页面分配器缓存。但这次,任意读取可用于确定正确页内偏移的对象何时位于sk_buff slub缓存的SLUB空闲列表顶部;任意读取还可以确定我是否成功分配了整个slab页面的对象,没有混合其他对象。

使用写入原语的前提条件

此时,我已设置写入原语,可以在特定的栈内存位置触发它。写入原语首先读取一些周围的(栈)内存,并期望该内存具有某种结构,然后递增特定栈位置的值。

我还知道要覆盖哪个栈分配。

剩余问题是:

  • 需要确保管道缓冲区页面后面的OOB copy_from_user()将覆盖有助于破坏内核的某些数据
  • 需要能够检测pipe_write()在哪个栈深度运行,并根据该信息要么重试要么继续触发漏洞

选择OOB覆盖目标

页表在此具有几个不错的属性:

  • 我可以轻松导致分配任意数量的页表
  • 我可以轻松确定内核为我的进程分配的页表的物理和内核虚拟地址
  • 它们是order-0不可移动分配,就像管道缓冲区一样

因此我选择使用OOB copy_from_user()覆盖页表。

检测pipe_write()栈深度

为了通过write()系统调用运行pipe_write(),以便能够可靠地确定函数在哪个深度运行并决定是否继续破坏,我可以准备一个管道,使其最初只有一个空闲的pipe_buffer,然后使用0x3000的长度调用write()。

减慢copy_from_iter()

我需要减慢copy_from_iter()调用。只要只需要延迟单个用户空间内存读取,就有另一种选择:我可以创建一个非常大的匿名VMA;用4KiB零页的映射填充它;确保在VMA中的一个特定位置没有映射页面;然后让一个线程在此大型匿名VMA上运行mprotect()操作,而另一个线程尝试访问当前未映射页面的用户空间区域部分。

页表控制

将所有内容放在一起,我可以使用受控数据覆盖页表的内容。我使用该受控写入在页表中放置一个新条目,该条目指回页表,从而有效地创建页表的用户空间映射;然后我可以使用它来将任意内核内存可写地映射到用户空间。

我的漏洞利用通过使用它覆盖uname打印的UTS信息来演示其修改内核内存的能力。

结论

即使在相对受限的环境中,也可以执行中等复杂度的Linux内核漏洞利用。

Chrome的Linux桌面渲染器沙箱暴露了在沙箱中从未合法使用的内核攻击面。这种不必要的功能不仅允许攻击者利用他们原本无法利用的漏洞;还暴露了用于漏洞利用的内核接口,启用堆整理、延迟注入等。Linux内核通过相同的系统调用公开深奥的特性和常用的核心内核功能,从而加剧了这个问题。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)

公众号二维码

公众号二维码

http://www.hskmm.com/?act=detail&tid=25384

相关文章:

  • assistant-ui
  • 20251006 之所思 - 人生如梦
  • C# Avalonia 16- Animation- RotateButton
  • 2025 十一集训
  • 汇编实验3
  • 20251005 模拟测 总结
  • 基于Python+Vue开发的体育用品商城管理系统源码+运行步骤
  • 完整教程:Microsoft Word使用技巧分享(本科毕业论文版)
  • (转)The Ten Commandments of Digital Cotrol(Part1)
  • ctf逆向常见算法----base64
  • 02020409 EF Core基础09-一对一、多对多、EF Core基于关系的复杂查询
  • 02020503 EF Core高级03-分页查询、IQuerable底层的实现形式、DataReader、DataTable、EF Core中的异步方法
  • 02020502 EF Core高级02-IQuerable会延迟执行、分部和动态构建IQuerable、IQuerable的复用
  • 在 PyCharm 中,环境:bert_env , 执行 import wandb 报错。但是,在CMD窗口,环境:bert_env , 执行 import wandb 正常。
  • Linux_T(Sticky Bit)粘滞位详解 - 详解
  • P2831 [NOIP 2016 提高组] 愤怒的小鸟 题解
  • 库存中心(三层库存模型)
  • Valley靶机渗透实战:从凭证复用到Python库劫持
  • 10.05模拟赛反思
  • MariaDB收购SkySQL增强AI与无服务器能力
  • 单片机寄存器的四种主要类型! - 实践
  • TDengine 高级特性——读缓存
  • 非合作博弈之软性均衡:东方智慧与西方理论的融合框架
  • 如何快速搭建spring-boot工程 - murphy
  • Ai元人文:东谈西论——非合作博弈之软性均衡
  • 反向传播与梯度下降:神经网络如何真正学会分类
  • Spring Cloud Alibaba微服务开发
  • OI 各种东西的板子
  • 价值弥漫:AI元人文的场域革命与共生之路
  • 做题记录 #1