目录
- 一、漏洞概述
- 二、screensharingd 架构与 SRP 协议
- 三、漏洞根因:Stale Success Code
- 四、完整利用链还原
- 五、Apple 文件复制协议
- 六、SRP 协议独立缺陷
- 七、SIP 持久化路径
- 八、AI 辅助漏洞挖掘争议
- 九、检测与防御
- 十、IoC 指标
一、漏洞概述
CVE-2026-43760 是 macOS 屏幕共享服务守护进程 screensharingd 中的一个预认证(pre-authentication)远程代码执行漏洞。该漏洞允许网络上的未认证攻击者完全绕过 SRP(Secure Remote Password)认证握手,在没有任何有效凭据的前提下,直接获得以 root 权限运行的会话,进而通过 Apple 私有文件复制协议实现任意文件读写,最终达成稳定的远程 root RCE。
Apple 于 2026 年 7 月 27 日在 macOS Tahoe 26.6 与 macOS Sonoma 14.8.8 中同步修复了该漏洞。值得注意的是,Apple 自评 CVSS 3.1 得分为 5.5(Medium),而 NVD 与 CIRCL 的独立评分高达 8.6(High),两者之间存在显著分歧。这种分歧的根源在于:Apple 的评分假设攻击需要"相邻网络访问"且"认证复杂度高",而实际上该漏洞是纯粹的预认证远程利用,攻击者仅需能向目标主机的 TCP 5900 端口(或经由远程管理端口)发包即可触发,无需任何凭据。
| 属性 | 值 |
|---|---|
| CVE 编号 | CVE-2026-43760 |
| 影响组件 | screensharingd(macOS Screen Sharing 服务) |
| 漏洞类型 | 预认证认证绕过 + 明文会话 + 任意文件读写 + Root RCE |
| Apple CVSS | 5.5(Medium) |
| NVD / CIRCL CVSS | 8.6(High) |
| 攻击前置条件 | 网络可达 screensharingd 监听端口,无需任何凭据 |
| 受影响版本 | macOS Tahoe ≤ 26.5;macOS Sonoma < 14.8.8 |
| 修复版本 | macOS Tahoe 26.6;macOS Sonoma 14.8.8 |
| 利用可靠性 | 1/5 ~ 1/15 次尝试成功,默认重试 20 次 |
| 修复日期 | 2026-07-27 |
1.1 严重性分歧的技术解读
Apple 的 5.5 评分通常对应"需要用户交互或本地访问"的漏洞等级。然而 CVE-2026-43760 的实际攻击面远比 Apple 描述的更广:
- 预认证:漏洞发生在 SRP 握手阶段,攻击者无需任何用户名/密码,甚至无需知道系统上存在哪些用户账户。
- 纯远程:只要 Screen Sharing 服务启用并可达(无论是局域网还是经端口转发/VPN 暴露到公网),攻击即可发起。
- Root 权限:
screensharingd以 root 身份运行,绕过认证后的会话直接继承 root 权限。 - 明文会话:由于加密层从未安装,所有后续通信均以明文传输,攻击者可自由构造任意协议消息。
NVD/CIRCL 的 8.6 评分更准确地反映了"网络可达 + 无认证 + 高权限"这一组合的实际风险。在企业环境中,一旦一台启用了 Screen Sharing 的 macOS 主机被攻陷,攻击者可借此作为跳板进行横向移动。
二、screensharingd 架构与 SRP 协议
2.1 screensharingd 服务概览
screensharingd 是 macOS 上实现屏幕共享与远程管理功能的核心守护进程,基于 RFB(Remote Frame Buffer)协议工作,但在标准 RFB 之上扩展了 Apple 私有的认证与加密机制。该服务在"系统设置 → 通用 → 共享 → 屏幕共享"或"远程管理"启用后启动,默认监听 TCP 5900 端口。
┌─────────────────────────────────────────────────────────────────┐
│ screensharingd 进程模型 │
│ │
│ root 权限运行 (screensharingd) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ RFB 协议层 │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 协议握手 │ → │ 安全类型协商 │ │ │
│ │ │ ProtocolVersion│ │ SecurityType │ │ │
│ │ └─────────────┘ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ┌──────────────┼──────────────┐ │ │
│ │ ▼ ▼ ▼ │ │
│ │ Type 2 Type 36 Type 30 │ │
│ │ (VNC密码) (SRP认证) (None/明文) │ │
│ │ │ │ │ │ │
│ │ │ ┌───────┴────────┐ │ │ │
│ │ │ │ SRP 状态机 │ │ │ │
│ │ │ │ (ccsrp_*) │ │ │ │
│ │ │ └───────┬────────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ ┌───────▼────────┐ │ │ │
│ │ │ │ ChaCha20-Poly1305│ │ │ │
│ │ │ │ 加密层安装 │ │ │ │
│ │ │ └───────┬────────┘ │ │ │
│ │ │ │ │ │ │
│ │ └──────────────┴──────────────┘ │ │
│ │ │ │ │
│ │ ┌────────▼─────────┐ │ │
│ │ │ Apple 扩展消息层 │ │ │
│ │ │ (文件复制/剪贴板/ │ │ │
│ │ │ 显示控制等) │ │ │
│ │ └──────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2.2 RFB 安全类型协商
RFB 协议握手的第一阶段是协议版本交换,第二阶段是安全类型协商。客户端发送支持的安全类型列表,服务端选择其中一种。screensharingd 支持的关键安全类型如下:
| 安全类型 | 数值 | 说明 |
|---|---|---|
| None | 1 | 无认证(通常被禁用) |
| VNC Password | 2 | 传统 VNC 密码认证(DES 挑战) |
| Apple ARD | 30 | Apple Remote Desktop 认证 |
| SRP | 36 | SRP-6a 认证(默认主路径) |
当客户端在安全类型列表中声明支持类型 36 时,screensharingd 会进入 SRP 认证流程。这是现代 macOS 屏幕共享的默认高强度认证路径,设计目标是在不传输密码的前提下完成双向认证,并在认证成功后派生出会话密钥用于后续加密。
2.3 SRP-6a 握手流程
SRP(Secure Remote Password)是一种增强型密码认证密钥交换协议,screensharingd 基于 corecrypto 库的 ccsrp_* 系列函数实现 SRP-6a 变体。正常流程如下:
客户端 screensharingd (root)
│ │
│ ──── SecurityType = 36 (SRP) ──────────────────→ │
│ │
│ ←── username (I) ────────────────────────────── │ 查找用户 verifier (v, salt)
│ │ 生成服务端公钥 B = kv + g^b
│ ←── salt, B (服务端公钥) ────────────────────── │
│ │
│ 生成客户端公钥 A = g^a │
│ 计算共享密钥 S │
│ ──── A (客户端公钥) ────────────────────────────→ │ ccsrp_server_generate_public_key
│ │ ccsrp_server_compute_session(S)
│ │ ccsrp_server_verify_session(M1)
│ ←── M2 (服务端证明) ───────────────────────────── │
│ │
│ ═══ 双向认证完成,派生会话密钥 K ═══ │
│ ═══ 安装 ChaCha20-Poly1305 加密层 ═══ │
│ │
│ ═══ 后续所有 RFB 消息均加密传输 ═══ │
正常情况下,ccsrp_server_generate_public_key、ccsrp_server_compute_session、ccsrp_server_verify_session 三个函数依次执行,任何一个失败都会中止认证。认证成功后,从共享密钥 S 派生出会话密钥 K,并安装 ChaCha20-Poly1305 AEAD 加密层,此后所有 RFB 消息均在加密通道内传输。
CVE-2026-43760 的破坏性在于:它让上述整个 SRP 状态机被完全跳过,三个核心函数一个都未执行,加密层也从未安装,连接退化为完全明文状态——但调用者却"认为"认证已经成功。
三、漏洞根因:Stale Success Code
3.1 SRP 帧读取器与长度验证
SRP 认证消息以"帧"的形式在 RFB 通道内传输。每个 SRP 帧以一个 4 字节的大端序(big-endian)32 位长度前缀开头,指示后续载荷的字节数。screensharingd 中的 SRP 帧读取器负责读取这个长度前缀,验证其是否在合理范围内,然后读取对应长度的载荷。
漏洞位于帧长度验证逻辑中。帧读取器在读取 4 字节长度时的伪代码如下:
// SRP 帧读取器(伪代码,还原自反汇编)
int srp_frame_read(connection_t *conn, srp_frame_t *out) {
uint32_t length_be;
int ret;
// 步骤1: 读取 4 字节大端序长度
ret = read_exact(conn->fd, &length_be, 4);
if (ret != 0) { // ret == 0 表示成功
return ret; // 读取失败,返回错误码
}
// 注意: 此时 ret == 0 (成功)
uint32_t length = ntohl(length_be);
// 步骤2: 验证长度是否过大
if (length > MAX_SRP_FRAME_SIZE) {
// 错误路径: 帧过大
// BUG: 这里本应返回错误码,但实际捕获了 ret(仍为 0)
log_error("SRP frame too large: %u", length);
return ret; // ← 返回 0(成功)!而非错误码
}
// 步骤3: 读取载荷
ret = read_exact(conn->fd, out->payload, length);
if (ret != 0) {
return ret;
}
out->length = length;
return 0;
}
关键缺陷在于"帧过大"错误路径:return ret; 返回的 ret 仍然是步骤 1 中 4 字节读取操作的成功返回值(0),而不是一个表示"帧过大"的错误码。这意味着,当攻击者发送一个长度字段超标的 SRP 帧时,srp_frame_read 不仅没有拒绝该帧,反而向调用者报告"读取成功"。
3.2 ARM64 反汇编分析
通过对受影响版本 screensharingd 二进制的 ARM64 反汇编,可以清晰地看到这一"陈旧成功码"(Stale Success Code)缺陷。以下是关键路径的反汇编片段:
;; ==========================================
;; srp_frame_read 函数关键片段 (ARM64)
;; ==========================================
;; 步骤1: 读取 4 字节长度前缀
;; 调用 read_exact(conn->fd, &length_be, 4)
mov w0, w19 ; conn->fd
add x1, sp, #0x20 ; &length_be (栈缓冲区)
mov w2, #4 ; size = 4
bl _read_exact ; 返回值在 x0
;; 保存 read_exact 的返回值到栈
;; x0 = 0 表示读取成功
str w0, [sp, #0x1c] ; 保存 ret 到栈 [sp+0x1c]
;; 检查读取是否失败
ldr w8, [sp, #0x1c] ; w8 = ret
cbnz w8, loc_read_failed ; if (ret != 0) goto read_failed
;; 步骤2: ntohl 转换长度
ldr w8, [sp, #0x20] ; w8 = length_be
rev w8, w8 ; w8 = ntohl(length_be) ← 大端转小端
str w8, [sp, #0x24] ; 保存 length
;; 步骤3: 验证长度上限
;; length > MAX_SRP_FRAME_SIZE (0x8000 = 32768) ?
ldr w8, [sp, #0x24] ; w8 = length
mov w9, #0x8000 ; w9 = MAX_SRP_FRAME_SIZE
cmp w8, w9
b.ls loc_length_ok ; if (length <= 0x8000) goto ok
;; ─────────────────────────────────────────
;; 错误路径: 帧过大
;; ─────────────────────────────────────────
loc_frame_too_large:
adrp x1, _str_frame_too_large@PAGE
add x1, x1, _str_frame_too_large@PAGEOFF
mov w2, w8 ; length 作为日志参数
bl _log_error ; log_error("SRP frame too large: %u", length)
;; ★★★ 关键缺陷 ★★★
;; 此处 x0 仍持有先前 log_error 的返回值,
;; 而 log_error 成功时返回 0。
;; 但更早期版本的代码模式是直接复用寄存器:
;;
;; mov x21, x0 ; x0 仍为步骤1 read_exact 的返回值 0
;;
;; 无论哪种模式,x21 被设置为 0(成功),
;; 而非一个负数错误码(如 -1 / 0xFFFFFFFF)
mov x21, x0 ; ← BUG: x0 仍为 0(陈旧的成功码)
; x21 作为函数最终返回值寄存器
b loc_return ; 跳转到返回
;; ─────────────────────────────────────────
;; 正常路径: 长度合法,继续读取载荷
;; ─────────────────────────────────────────
loc_length_ok:
ldr w0, [sp, #0x24] ; length
... ; 分配缓冲区并读取载荷
bl _read_exact
str w0, [sp, #0x1c] ; 更新 ret
...
loc_return:
ldr w0, [sp, #0x1c] ; 或 mov w0, w21
ldp x29, x30, [sp], #0x40
ret ; 返回 x0 = 0(成功)
loc_read_failed:
mov w0, #-1 ; 返回 -1(真正的错误)
ldp x29, x30, [sp], #0x40
ret
核心问题在 loc_frame_too_large 标签处:mov x21, x0 将 x0 的当前值保存到 x21(最终返回值寄存器)。然而此时 x0 仍持有步骤 1 中 _read_exact 的成功返回值 0,而非一个错误码。log_error 调用虽然被执行,但其副作用并未改变控制流——函数仍然以"成功"状态返回。
3.3 调用者的误判
srp_frame_read 的调用者(SRP 握手状态机的某个阶段)根据返回值判断帧读取是否成功。当收到返回值 0 时,调用者认为:
- SRP 帧已成功读取;
- 当前握手步骤已完成;
- 应推进到下一阶段。
由于"帧过大"路径返回 0,调用者错误地将这一异常状态解释为"当前 SRP 步骤成功完成"。状态机因此跳过后续所有 SRP 验证步骤,直接进入"认证完成"状态。
3.4 触发条件精确分析
触发条件取决于 MAX_SRP_FRAME_SIZE 的具体值与比较逻辑。根据反汇编,长度验证为 length > 0x8000(即 32768)。但更精确的分析表明,触发条件是大端序 32 位长度值在"位置 ≥ 15 的任何位被置位"。
这里的"位置"指的是从最低有效位(LSB)起算的位编号。位置 15 对应数值 0x8000(32768)。位置 ≥ 15 的任何位被置位,意味着:
位编号: 31 30 ... 16 15 14 ... 1 0
数值: 2^31 2^30 2^16 2^15 2^14 2^1 2^0
触发条件: bit[15] 或更高位中任意一位为 1
等价于: length >= 0x8000 (32768)
因此,任何长度值 length ≥ 32768(即 0x8000)的 SRP 帧都会触发该漏洞。例如:
0x00008000(32768)→ 触发0x00010000(65536)→ 触发0x80000000(2147483648)→ 触发0x00007FFF(32767)→ 不触发,走正常路径
攻击者只需在 SRP 帧的 4 字节长度前缀中填入任意 ≥ 0x8000 的大端序值,即可使 srp_frame_read 走入错误路径并返回"成功"。
四、完整利用链还原
4.1 利用链全景
┌─────────────────────────────────────────────────────────────────────┐
│ CVE-2026-43760 完整利用链 │
│ │
│ [1] 网络连接 │
│ │ TCP → screensharingd:5900 │
│ ▼ │
│ [2] RFB 握手 + 安全类型协商 (Type 36 = SRP) │
│ │ 声明支持 SRP,进入 SRP 认证流程 │
│ ▼ │
│ [3] 发送畸形 SRP 帧 (length ≥ 0x8000) │
│ │ ┌────────────────────────────────────────────┐ │
│ │ │ 4 字节长度: 0x00 0x01 0x00 0x00 (= 65536) │ │
│ │ │ 载荷: (无需发送,帧读取已"成功"返回) │ │
│ │ └────────────────────────────────────────────┘ │
│ │ → srp_frame_read 返回 0(成功) │
│ │ → 调用者误判为"SRP 认证步骤完成" │
│ ▼ │
│ [4] SRP 状态机被完全跳过 │
│ │ ccsrp_server_generate_public_key ─ ✗ 未执行 │
│ │ ccsrp_server_compute_session ─ ✗ 未执行 │
│ │ ccsrp_server_verify_session ─ ✗ 未执行 │
│ │ → 无会话密钥派生 │
│ │ → ChaCha20-Poly1305 加密层从未安装 │
│ │ → 连接完全明文 │
│ ▼ │
│ [5] 认证绕过确认 — 获得未加密 root 会话 │
│ │ screensharingd 认为"用户已认证" │
│ │ 后续消息以明文传输 │
│ ▼ │
│ [6] 通过 Apple 文件复制协议(消息类型 0x22) │
│ │ 实现任意文件读 / 写 │
│ ▼ │
│ [7] 持久化 + RCE │
│ │ 写入 /etc/sudoers.d/pwn → 免密码 sudo 提权 │
│ │ 写入 /etc/zshenv → 每次开 shell 执行payload │
│ │ 写入 /Library/LaunchDaemons/ → 开机自启 root 守护进程 │
│ │ com.evil.helper.plist │
│ ▼ │
│ [8] 稳定 Root RCE │
└─────────────────────────────────────────────────────────────────────┘
4.2 利用步骤详解
步骤 1-2:建立连接与协商
攻击者向目标主机的 TCP 5900 端口发起连接,完成 RFB 协议版本握手(RFB 003.889 或类似版本字符串)。在安全类型协商阶段,客户端发送包含类型 36(SRP)的安全类型列表。服务端选择 SRP,进入认证流程。
步骤 3:发送畸形 SRP 帧
在 SRP 认证的第一个帧交换点(通常是客户端需要发送其 SRP 公钥 A 的阶段),攻击者不发送合法的 SRP 公钥,而是发送一个长度字段超标的畸形帧:
攻击者发送的畸形帧:
┌──────────────────┬───────────────────────────────────┐
│ 长度 (4字节, BE) │ 载荷 (可选, 可不发送) │
│ 00 01 00 00 │ (65536 字节, 但实际可不发或发少量) │
│ = 65536 ≥ 32768 │ │
└──────────────────┴───────────────────────────────────┘
由于长度值 65536(0x00010000)≥ 32768,srp_frame_read 进入"帧过大"错误路径,但因 Stale Success Code 缺陷返回 0(成功)。
步骤 4-5:认证绕过与明文会话
调用者收到"成功"返回值后,认为当前 SRP 步骤已完成,推进状态机。由于后续步骤依赖于本应读取的 SRP 载荷(客户端公钥 A),而这些载荷从未被正确读取,整个 SRP 状态机的三个核心函数均被跳过:
ccsrp_server_generate_public_key:需要客户端公钥 A,但 A 从未被读取 → 跳过ccsrp_server_compute_session:依赖上一步输出 → 跳过ccsrp_server_verify_session:依赖会话计算结果 → 跳过
最终状态机到达"认证完成"状态。由于没有任何会话密钥被派生,ChaCha20-Poly1305 加密层从未安装,连接保持在完全明文状态。screensharingd 认为客户端已通过认证,允许后续操作。
步骤 6-7:文件读写与持久化
获得明文 root 会话后,攻击者利用 Apple 扩展的文件复制协议(消息类型 0x22)进行任意文件读写,并写入持久化载荷(详见第五、七节)。
4.3 利用可靠性
该漏洞的利用并非 100% 可靠。由于 SRP 状态机的跳过依赖于时序与内部状态的特定组合,实际成功率约为 1/5 到 1/15。公开的利用工具默认配置 20 次重试,在大多数情况下能在首轮重试窗口内成功。失败时通常表现为连接被服务端重置或超时,攻击者只需重新建立连接并重试即可。
五、Apple 文件复制协议
5.1 协议概述
认证(或绕过认证)完成后,screensharingd 在 RFB 通道之上运行 Apple 私有的扩展消息协议。这些扩展消息使用 RFB 的"客户端消息"(Client Message)和"服务端消息"(Server Message)机制,通过消息类型字段区分不同功能。其中,消息类型 0x22 对应 Apple 文件复制功能。
文件复制协议允许认证用户在客户端与服务端之间传输文件,原本用于屏幕共享会话中的剪贴板文件传输、远程拖放等场景。然而,由于认证已被绕过且会话以 root 权限运行,这一功能沦为任意文件读写原语。
5.2 消息类型 0x22 结构
消息类型 0x22 的基本结构如下:
Apple 文件复制消息 (Type 0x22)
┌────────────┬────────────┬────────────┬─────────────────────┐
│ msg_type │ sub_type │ flags │ 路径/数据载荷 │
│ 0x22 (1B) │ (1B) │ (2B) │ (变长) │
└────────────┴────────────┴────────────┴─────────────────────┘
子类型 (sub_type) 定义:
0x01 → 文件读取请求 (Read)
0x02 → 文件写入请求 (Write)
0x03 → 文件创建/截断
0x04 → 目录创建
0x05 → 文件删除
0x06 → 文件属性查询
0x07 → 文件枚举 (ls)
...
5.3 任意文件读写原语
由于 screensharingd 以 root 身份运行,文件复制操作以 root 权限执行,且不受用户家目录限制。攻击者可以:
# 伪代码: 利用文件复制协议的写入原语
def write_file(path, content):
msg = struct.pack(">BBH", 0x22, 0x02, 0x0000) # type, sub=Write, flags
path_bytes = path.encode('utf-8')
msg += struct.pack(">I", len(path_bytes)) + path_bytes
msg += struct.pack(">I", len(content)) + content
send_raw(msg) # 明文发送, 无加密
def read_file(path):
msg = struct.pack(">BBH", 0x22, 0x01, 0x0000) # type, sub=Read, flags
path_bytes = path.encode('utf-8')
msg += struct.pack(">I", len(path_bytes)) + path_bytes
send_raw(msg)
return recv_raw() # 明文接收文件内容
这一原语允许攻击者读取任意敏感文件(如 /etc/sudoers、用户密钥链)以及向任意路径写入文件,包括系统关键配置目录。
六、SRP 协议独立缺陷
6.1 第二个独立漏洞
除了 Stale Success Code 之外,研究者还发现 screensharingd 的 SRP 实现中存在第二个独立缺陷。该缺陷位于 ccsrp_server_compute_session 的输入验证逻辑中,涉及对客户端公钥 A 的零值检查。
6.2 A ≡ 0 (mod N) 检查缺失
在 SRP-6a 协议中,服务端计算共享密钥 S 的公式为:
S = (A * v^u)^b (mod N)
其中 A 是客户端公钥。SRP 规范要求服务端必须验证 A mod N ≠ 0,否则攻击者可以通过选择特殊的 A 值来操纵共享密钥。
screensharingd 的 SRP 实现检查了 A == 0(即 A 的所有字节为零),但没有检查 A ≡ 0 (mod N)(即 A 是素数模数 N 的整数倍)。这是一个关键的安全检查遗漏。
6.3 强制 S = 0 的攻击
当攻击者发送 A = N(即 A 等于 SRP 素数模数)时:
A = N
A mod N = 0
S = (A * v^u)^b (mod N)
S = (0 * v^u)^b (mod N)
S = 0^b (mod N)
S = 0 (mod N)
S = 0
共享密钥 S 被强制为零。由于会话密钥 K 由 S 派生,攻击者已知 S = 0 即可计算出 K,从而:
- 伪造有效的认证证明 M1 / M2;
- 推导出 ChaCha20-Poly1305 加密层的密钥;
- 解密(并伪造)所有后续加密通信。
┌──────────────────────────────────────────────────────────┐
│ SRP 独立缺陷: A ≡ 0 (mod N) 检查缺失 │
│ │
│ 正常检查 (规范要求): │
│ if (A mod N == 0) reject; ← screensharingd 缺失 │
│ │
│ 实际检查 (有缺陷): │
│ if (A == 0) reject; ← 仅检查全零 │
│ │
│ 攻击: │
│ 发送 A = N (非零, 但 A mod N == 0) │
│ → S = (A * v^u)^b mod N = 0 │
│ → K = KDF(S=0) → 已知会话密钥 │
│ → 可伪造认证 + 解密/伪造所有流量 │
│ │
│ 与 CVE-2026-43760 的关系: │
│ 这是独立缺陷, 即使修复了 Stale Success Code, │
│ 此缺陷仍可被利用来绕过认证(需知道用户名/verifier信息) │
└──────────────────────────────────────────────────────────┘
需要注意的是,这第二个缺陷与 CVE-2026-43760 是独立的。Stale Success Code 缺陷允许完全无凭据的认证绕过,而 A ≡ 0 (mod N) 缺陷则需要攻击者知道目标用户名(以获取对应的 salt 和 verifier),但允许攻击者解密后续通信。两者组合时,攻击者既可以绕过认证,又可以获得加密会话的完全控制权。
七、SIP 持久化路径
7.1 SIP 保护机制概述
macOS 的系统完整性保护(System Integrity Protection,SIP)是内核级的安全机制,限制即使是 root 用户对系统关键路径的写入。SIP 保护以下路径:
/System(系统文件)/usr(除/usr/local)/bin、/sbin- 预装应用
crontab相关路径
然而,SIP 的保护范围并非覆盖所有 root 可写路径。CVE-2026-43760 的利用链精心选择了 SIP 未保护但仍能实现持久化的路径。
7.2 SIP 影响矩阵
| 持久化路径 | SIP 启用时 | SIP 禁用时 | 持久化效果 | 备注 |
|---|---|---|---|---|
/etc/sudoers.d/pwn |
✅ 可写入 | ✅ 可写入 | 免密码 sudo 提权 | sudo 启动时加载该目录所有文件 |
/etc/zshenv |
✅ 可写入 | ✅ 可写入 | 每次 zsh 启动执行 payload | 影响所有 zsh 交互/非交互会话 |
/Library/LaunchDaemons/*.plist |
✅ 可写入 | ✅ 可写入 | 开机自启 root 守护进程 | launchd 在启动时加载 |
/Library/LaunchAgents/*.plist |
✅ 可写入 | ✅ 可写入 | 用户登录时启动 | 以登录用户权限运行 |
~/Library/LaunchAgents/*.plist |
✅ 可写入 | ✅ 可写入 | 特定用户登录时启动 | 需知道用户家目录 |
crontab -e (系统 crontab) |
❌ 被阻断 | ✅ 可写入 | 定时执行 | SIP 阻断 crontab 路径 |
/etc/periodic/* |
⚠️ 部分受限 | ✅ 可写入 | 周期执行 | 取决于具体子路径 |
/System/Library/LaunchDaemons/ |
❌ SIP 保护 | ❌ 仍受 CFRV 保护 | — | 不可写入 |
/usr/lib/ |
❌ SIP 保护 | ✅ 可写入 | dylib 劫持 | SIP 禁用时可利用 |
7.3 持久化载荷详解
路径 1:/etc/sudoers.d/pwn — 免密码 sudo 提权
# /etc/sudoers.d/pwn 内容
ALL ALL=(ALL) NOPASSWD: ALL
写入此文件后,任何用户执行 sudo 均无需密码。攻击者可通过低权限用户会话(如通过 SSH 或其他入口获得的普通用户 shell)直接 sudo su 提升至 root。由于 /etc/sudoers.d/ 不受 SIP 保护,此路径在 SIP 启用时仍然有效。
路径 2:/etc/zshenv — 全局 zsh 启动执行
# /etc/zshenv 内容
nohup /tmp/.update_agent >/dev/null 2>&1 &
/etc/zshenv 在每次 zsh 启动时(无论交互或非交互模式)被 source。写入此文件后,任何打开终端、运行脚本或通过 SSH 登录的用户都会触发 payload 执行。这是极其隐蔽的持久化手段,因为 zshenv 不像 LaunchDaemons 那样有明显的进程特征。
路径 3:/Library/LaunchDaemons/com.evil.helper.plist — 开机自启 root 守护进程
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.evil.helper</string>
<key>ProgramArguments</key>
<array>
<string>/tmp/.update_agent</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StartInterval</key>
<integer>300</integer>
</dict>
</plist>
写入 /Library/LaunchDaemons/ 后,launchd 在系统启动时以 root 权限加载并运行指定程序。KeepAlive 确保进程被杀后自动重启,StartInterval 提供定时保活。由于该目录不受 SIP 保护,此持久化在 SIP 启用时仍然有效。
7.4 SIP 的局限性启示
CVE-2026-43760 的持久化路径选择揭示了 SIP 的一个重要局限:SIP 保护的是"系统完整性"(即操作系统自身文件不被篡改),而非"阻止 root 持久化"。/etc/sudoers.d/、/etc/zshenv、/Library/LaunchDaemons/ 这些路径虽然不属于 SIP 保护范围,但它们同样是系统级持久化的关键位置。这意味着,即使 SIP 完全启用,一旦攻击者获得 root 写权限,仍然可以实现可靠持久化。
这一发现对防御者的启示是:SIP 不能作为唯一的持久化防御手段,必须结合文件完整性监控(FIM)和端点检测响应(EDR)来监控这些关键路径的变更。
八、AI 辅助漏洞挖掘争议
8.1 fG! 的独立发现
著名 macOS 安全研究员 fG!(Féréol Pouget,博客 reverse.put.as)独立发现了 CVE-2026-43760,并对其严重性给予了远高于 Apple 官方评分的评价。fG! 长期深耕 macOS 逆向工程,曾发现并披露多个 macOS 内核与用户态漏洞,其在 macOS 安全社区具有高度权威性。
8.2 对 AI 辅助漏洞挖掘的批评
CVE-2026-43760 的披露引发了关于 AI 辅助漏洞挖掘的讨论。fG! 公开批评了当前业界对"AI 自动发现漏洞"能力的过度宣传,认为这种叙事低估了此类漏洞的真实严重性。
fG! 的核心观点可归纳为以下几点:
-
漏洞发现的本质是深度逆向工程:CVE-2026-43760 的发现需要对 ARM64 反汇编、SRP 协议状态机、corecrypto 库内部实现有深入理解,这些是当前 AI 工具难以独立完成的。Stale Success Code 这种"陈旧寄存器值"缺陷,需要对调用约定和寄存器生命周期有精细追踪,AI 模型在静态分析中容易遗漏这类跨函数的寄存器状态问题。
-
AI 挖掘的低估效应:当漏洞被贴上"AI 发现"标签时,公众和厂商倾向于低估其严重性,认为"AI 找到的都是简单问题"。然而 CVE-2026-43760 是一个预认证 root RCE,其实际影响远超"简单"范畴。这种叙事偏差可能导致修复优先级被错误降低。
-
人类直觉不可替代:fG! 强调,发现此类漏洞往往依赖于研究者对"代码气味"的直觉——例如,错误处理路径中寄存器复用、状态机跳转条件的不对称等。这些模式识别能力目前仍需要经验丰富的人类研究员。
8.3 争议的启示
这场争议凸显了漏洞挖掘领域人机协作的复杂性。AI 工具确实能加速某些重复性分析工作(如批量模式匹配、fuzzing 种子生成),但对于需要深度语义理解和跨层关联分析的漏洞(如 CVE-2026-43760 这种涉及协议状态机、寄存器生命周期、认证逻辑的组合缺陷),人类研究员的核心地位仍然不可动摇。防御者应警惕"AI 万能论"带来的安全盲区,不应因为某漏洞"可能被 AI 发现"而降低其处置优先级。
九、检测与防御
9.1 修复方案
首选方案:立即升级至修复版本。
| macOS 版本线 | 受影响版本 | 修复版本 |
|---|---|---|
| macOS Tahoe | ≤ 26.5 | 26.6 |
| macOS Sonoma | < 14.8.8 | 14.8.8 |
升级路径:系统设置 → 通用 → 软件更新。建议同时检查 Sequoia 及更早版本线是否仍在维护窗口内,如已停止维护则需考虑硬件升级或隔离。
9.2 缓解措施(无法立即升级时)
-
禁用 Screen Sharing / 远程管理:系统设置 → 通用 → 共享 → 关闭"屏幕共享"与"远程管理"。这是最有效的临时缓解措施。
-
网络层隔离:使用防火墙规则限制 TCP 5900 端口的访问来源,仅允许受信任的管理网络访问。不应将 5900 端口暴露到公网。
# pf 防火墙规则示例: 仅允许管理网段访问 5900
block in on en0 proto tcp from any to any port 5900
pass in on en0 proto tcp from 10.0.0.0/24 to any port 5900
-
启用 SSH 替代远程图形管理:如需远程管理,优先使用 SSH(配合密钥认证),避免暴露图形协议。
-
监控关键路径:部署文件完整性监控,覆盖
/etc/sudoers.d/、/etc/zshenv、/Library/LaunchDaemons/等持久化路径。
9.3 网络层检测规则
以下 Snort/Suricata 规则可用于检测 CVE-2026-43760 的利用尝试。核心检测逻辑是:在 SRP 安全类型协商后的帧中,检测长度字段 ≥ 0x8000 的畸形 SRP 帧。
# Suricata 规则: 检测 CVE-2026-43760 畸形 SRP 帧
# 检测 RFB 安全类型 36 (SRP) 协商后的大长度帧
alert tcp $EXTERNAL_NET any -> $HOME_NET 5900 (
msg:"CVE-2026-43760 macOS screensharingd SRP frame length bypass attempt";
flow:established,to_server;
content:"|36|"; # 安全类型 36 (SRP)
depth:1;
offset:0;
detection_filter:track,by_src;
# 后续帧中长度字段 >= 0x8000 的大端序值
content:"|00 01 00 00|"; # 示例: length = 65536
distance:0;
within:64;
threshold:type both, by_src, count 1, seconds 60;
reference:cve,2026-43760;
classtype:attempted-admin;
sid:2026043760;
rev:1;
)
# 更通用的检测: 任何 SRP 帧长度 >= 0x8000
alert tcp $EXTERNAL_NET any -> $HOME_NET 5900 (
msg:"CVE-2026-43760 screensharingd SRP oversized frame (length >= 32768)";
flow:established,to_server;
# 检测 4 字节大端序长度, 高 17 位中任意位置位
byte_test:4, >, 32767, 0, big, relative;
threshold:type both, by_src, count 1, seconds 30;
reference:cve,2026-43760;
classtype:attempted-admin;
sid:2026043761;
rev:1;
)
9.4 主机层检测
1. 检测明文 RFB 会话(加密层未安装的异常)
正常的 SRP 认证完成后,RFB 会话应处于加密状态。如果检测到 SRP 协商完成后会话仍为明文,则可能存在利用:
# 检测 screensharingd 的明文会话 (异常)
# 正常 SRP 认证后流量应无法被明文解析
# 使用 lsmp 或网络抓包分析会话状态
sudo tcpdump -i en0 -A 'tcp port 5900' | grep -E 'RFB|frame'
# 若在 SRP 协商后仍能看到明文 RFB 消息头, 则为异常
2. 文件完整性监控规则
监控 CVE-2026-43760 利用链涉及的关键持久化路径:
#!/bin/bash
# CVE-2026-43760 持久化路径监控脚本
# 建议配合 fswatch 或 osquery 定期执行
MONITOR_PATHS=(
"/etc/sudoers.d/"
"/etc/zshenv"
"/etc/zprofile"
"/Library/LaunchDaemons/"
"/Library/LaunchAgents/"
)
echo "[*] CVE-2026-43760 persistence path monitor"
echo "[*] Baseline check: $(date)"
for path in "${MONITOR_PATHS[@]}"; do
if [ -d "$path" ]; then
echo "--- Directory: $path ---"
ls -la "$path"
# 计算哈希基线
find "$path" -type f -exec shasum -a 256 {} \;
elif [ -f "$path" ]; then
echo "--- File: $path ---"
shasum -a 256 "$path"
fi
done
# 检查 sudoers.d 中是否存在可疑 NOPASSWD 规则
echo "--- Suspicious sudoers rules ---"
grep -r "NOPASSWD" /etc/sudoers.d/ 2>/dev/null
# 检查 zshenv 是否被篡改
echo "--- /etc/zshenv content ---"
cat /etc/zshenv 2>/dev/null || echo "(not present)"
# 检查 LaunchDaemons 中的可疑项
echo "--- LaunchDaemons with KeepAlive ---"
grep -rl "KeepAlive" /Library/LaunchDaemons/ 2>/dev/null | \
while read f; do
echo " $f"
grep -A1 "ProgramArguments" "$f" 2>/dev/null
done
3. osquery 检测查询
-- 检测 sudoers.d 中的免密码规则 (CVE-2026-43760 持久化指标)
SELECT path, line
FROM sudoers
WHERE line LIKE '%NOPASSWD%'
AND path LIKE '/etc/sudoers.d/%';
-- 检测可疑 LaunchDaemons (KeepAlive + 非标准路径)
SELECT label, program, path
FROM launchd
WHERE keep_alive = 1
AND program NOT LIKE '/usr/%'
AND program NOT LIKE '/System/%';
-- 检测 /etc/zshenv 异常修改时间
SELECT path, mtime, btime
FROM file
WHERE path = '/etc/zshenv'
AND mtime > strftime('%s', 'now', '-7 days');
-- 检测 screensharingd 进程的异常网络连接
SELECT p.pid, p.name, l.address, l.port
FROM processes p
JOIN process_open_sockets l ON p.pid = l.pid
WHERE p.name = 'screensharingd'
AND l.state = 'ESTABLISHED';
9.5 日志检测
screensharingd 的认证绕过可能在统一日志系统中留下痕迹。使用 log 命令查询:
# 检测 SRP 认证异常
sudo log show --predicate '
process == "screensharingd" &&
(eventMessage CONTAINS "frame too large" ||
eventMessage CONTAINS "SRP" ||
eventMessage CONTAINS "authentication")
' --last 7d --style compact
# 检测认证成功但无对应密码验证日志的异常会话
sudo log show --predicate '
process == "screensharingd" &&
eventMessage CONTAINS "authenticated"
' --last 24h
异常指标:如果日志中出现"frame too large"错误但随后紧接"authenticated"成功消息,则高度疑似 CVE-2026-43760 利用。
十、IoC 指标
10.1 网络层 IoC
| IoC 类型 | 指标 | 说明 |
|---|---|---|
| 端口 | TCP 5900 | screensharingd 默认监听端口 |
| 协议特征 | RFB + SecurityType 36 | SRP 安全类型协商 |
| 异常帧 | SRP 帧长度字段 ≥ 0x8000 | 畸形帧触发认证绕过 |
| 流量特征 | SRP 协商后明文 RFB 消息 | 加密层未安装的标志 |
| 消息类型 | 0x22 (文件复制) | 认证绕过后的文件读写操作 |
10.2 主机层 IoC
文件系统 IoC:
| 路径 | 异常指标 | 检测方法 |
|---|---|---|
/etc/sudoers.d/* |
新增未知文件,含 NOPASSWD 规则 | 文件完整性监控 |
/etc/zshenv |
文件被创建或修改,含 nohup/隐藏进程启动 | 哈希基线对比 |
/etc/zprofile |
同上 | 哈希基线对比 |
/Library/LaunchDaemons/com.evil.helper.plist |
非标准 Label,KeepAlive=true | launchctl list 审查 |
/Library/LaunchDaemons/*.plist |
指向 /tmp、/var/tmp 等临时路径 | 路径合理性检查 |
/tmp/.update_agent |
隐藏文件,可执行权限 | 隐藏文件扫描 |
可疑 plist Label 命名模式:
com.evil.helper
com.apple.update.agent # 伪装系统更新
com.system.securityd # 伪装安全进程
com.macos.sync.helper # 伪装同步服务
10.3 进程层 IoC
# 检测异常 screensharingd 子进程 (root 权限的异常派生)
sudo ps aux | grep -E 'screensharingd'
# 正常情况: screensharingd 不应派生 /tmp、/var/tmp 下的进程
# 检测从临时路径启动的 root 进程
sudo ps aux | awk '$1=="root" && $11 ~ /^\/(tmp|var\/tmp)\//'
10.4 YARA 规则
以下 YARA 规则可用于扫描持久化载荷文件:
rule CVE_2026_43760_Sudoers_Persistence {
meta:
description = "Detects sudoers persistence from CVE-2026-43760"
cve = "CVE-2026-43760"
severity = "high"
strings:
$nopasswd = /ALL\s+ALL=\(ALL\)\s+NOPASSWD:\s+ALL/i
$path = /\/etc\/sudoers\.d\//
condition:
$nopasswd
}
rule CVE_2026_43760_LaunchDaemon_Persistence {
meta:
description = "Detects LaunchDaemon persistence from CVE-2026-43760"
cve = "CVE-2026-43760"
severity = "high"
strings:
$plist_xml = "<?xml version" nocase
$keepalive = "<key>KeepAlive</key>" nocase
$runatload = "<key>RunAtLoad</key>" nocase
$tmp_path = /\/tmp\/\.|\/var\/tmp\//
condition:
$plist_xml and $keepalive and $runatload and $tmp_path
}
rule CVE_2026_43760_Zshenv_Persistence {
meta:
description = "Detects zshenv persistence from CVE-2026-43760"
cve = "CVE-2026-43760"
severity = "high"
strings:
$nohup = "nohup" nocase
$hidden = /\.update_agent|\.sync_helper|\.securityd/
$devnull = "/dev/null"
condition:
$nohup and $hidden and $devnull
}
10.5 IoC 汇总检查脚本
#!/bin/bash
# CVE-2026-43760 综合 IoC 检查脚本
# 用法: sudo bash cve_2026_43760_ioc_check.sh
echo "============================================"
echo " CVE-2026-43760 IoC 综合检查"
echo " 日期: $(date)"
echo "============================================"
# 1. 检查 macOS 版本是否受影响
echo ""
echo "[1] 系统版本检查"
PRODUCT_VERSION=$(sw_vers -productVersion)
echo " 当前版本: $PRODUCT_VERSION"
# (此处省略版本比较逻辑, 见修复版本表)
# 2. 检查 Screen Sharing 是否启用
echo ""
echo "[2] Screen Sharing 服务状态"
if launchctl list | grep -q screensharing; then
echo " [!] screensharingd 已启用 (存在暴露面)"
else
echo " [OK] screensharingd 未运行"
fi
# 3. 检查持久化路径
echo ""
echo "[3] 持久化路径检查"
echo " [3.1] /etc/sudoers.d/ 内容:"
if ls /etc/sudoers.d/ >/dev/null 2>&1; then
grep -rn "NOPASSWD" /etc/sudoers.d/ 2>/dev/null && \
echo " [!] 发现 NOPASSWD 规则" || \
echo " [OK] 未发现可疑规则"
fi
echo " [3.2] /etc/zshenv:"
if [ -f /etc/zshenv ]; then
echo " [!] /etc/zshenv 存在, 内容:"
cat /etc/zshenv | sed 's/^/ /'
else
echo " [OK] /etc/zshenv 不存在"
fi
echo " [3.3] /Library/LaunchDaemons/ 可疑项:"
for plist in /Library/LaunchDaemons/*.plist; do
[ -f "$plist" ] || continue
prog=$(/usr/libexec/PlistBuddy -c "Print :ProgramArguments:0" "$plist" 2>/dev/null)
case "$prog" in
/tmp/*|/var/tmp/*|/private/tmp/*)
echo " [!] 可疑 plist: $plist -> $prog"
;;
esac
done
# 4. 检查网络连接
echo ""
echo "[4] screensharingd 网络连接"
ESTABLISHED=$(lsof -i :5900 -sTCP:ESTABLISHED 2>/dev/null | wc -l)
echo " 5900 端口 ESTABLISHED 连接数: $ESTABLISHED"
# 5. 检查统一日志中的异常
echo ""
echo "[5] 近期日志中的 SRP 异常"
sudo log show --predicate '
process == "screensharingd" &&
eventMessage CONTAINS "frame too large"
' --last 24h --style compact 2>/dev/null | head -5
echo ""
echo "============================================"
echo " 检查完成"
echo "============================================"
总结
CVE-2026-43760 是 macOS 安全史上最严重的预认证远程漏洞之一。其破坏力源于多个因素的叠加:
- 预认证触发:漏洞发生在认证握手阶段,攻击者无需任何凭据。
- Root 权限继承:
screensharingd以 root 运行,绕过后的会话直接获得 root 权限。 - 加密层缺失:SRP 状态机被完全跳过,连接退化为明文,攻击者可自由构造任意协议消息。
- SIP 无法阻止持久化:
/etc/sudoers.d/、/etc/zshenv、/Library/LaunchDaemons/等 SIP 未保护路径足以实现可靠持久化。 - 双重缺陷:Stale Success Code 与 A ≡ 0 (mod N) 检查缺失是两个独立缺陷,修复时必须同时处理。
从防御角度,组织应立即将所有 macOS 主机升级至 Tahoe 26.6 或 Sonoma 14.8.8,审计 Screen Sharing 服务的暴露面,部署针对畸形 SRP 帧和关键持久化路径的检测规则。同时,应认识到 SIP 并非持久化防御的银弹,必须结合文件完整性监控与端点检测响应构建纵深防御体系。
Apple 与 NVD 之间的 CVSS 评分分歧也提醒安全团队:不应完全依赖厂商自评得分来排序漏洞修复优先级,而应结合攻击面分析、利用前置条件和实际影响进行独立评估。对于预认证、远程、root 级别的漏洞,无论厂商评分如何,均应按最高优先级处置。
浙公网安备 33010602011771号