yauzl 3.2.0 NTFS解析越界读取CVE-2026-31988
yauzl(Yet Another Unzip Lipary)是 Node.js 生态最稳定、最安全、最推荐的 ZIP 解压库,官方和社区公认的 Node.js 解压首选方案。
一、基本情况
yauzl 基于Node.js Stream API,边读边解压,内存占用和体积极低,专注于流式解压,内存高效,广泛用于处理文件上传和解压任务。

yauzl 纯 JavaScript 实现,无原生模块依赖,跨平台(Windows/macOS/Linux)完美兼容,安全、轻量、流式、无依赖、大文件友好。
栋科技漏洞库关注到 yauzl 3.2.0 版本中存在的NTFS解析越界读取导致 DoS 漏洞,追踪为CVE-2026-31988,漏洞CVSS 3.X评分4.8。
二、漏洞分析
CVE-2026-31988是 yauzl 相关版本 getLastModDate() 函数在解析 NTFS 扩展时间戳额外字段时的 off-by-one(差一错误)逻辑漏洞。
由于 NTFS 时间戳扩展字段解析器的一个越界错误,允许一个格式不正确的 zip 文件触发一个未处理的 ERR_OUT_OF_RANGE 异常。
这因此导致 Node.js 进程崩溃、没有优雅的错误处理,没有恢复,一个zip文件,一次崩溃。
yauzl 3.2.0 增加了读取 NTFS 扩展时间戳字段(额外字段 ID 0x000a)的支持。
这是一个受欢迎的改进,NTFS 时间戳提供了纳秒精度,而 DOS 时间戳的精度为 2 秒。
解析器遍历NTFS扩展字段数据中的属性标签:
// index.js:620 — NTFS extra field parser in getLastModDate()
var cursor = 4;
while (cursor < data.length + 4) { //BUG: allows cursor to exceed buffer by 4
var tag = data.readUInt16LE(cursor); // OOB read when cursor >= data.length
cursor += 2;
var size = data.readUInt16LE(cursor); // OOB read when cursor >= data.length
cursor += 2;
// ... process tag data ...
}
分析代码可以可以发现,该条件cursor < data.length + 4允许cursor达到最高值data.length + 3。
当cursor等于data.length时,readUInt16LE(cursor)调用尝试读取缓冲区边界之外偏移量开始的2个字节。
Node.js抛出ERR_OUT_OF_RANGE,进程崩溃。
while 循环的边界检查条件被错误地实现为 cursor < data.length + 4,导致readUInt16LE()函数可能在超出缓冲区边界的位置进行读取。
远程攻击者可以通过上传一个构造的、包含恶意 NTFS 额外字段的 Zip 文件,诱导应用处理该文件并调用entry.getLastModDate()。
这会触发 Node.js 的 ERR_OUT_OF_RANGE 异常,由于该异常通常会导致未捕获的错误,造成 Node.js 进程崩溃(拒绝服务攻击)。
三、POC概念验证
1、该漏洞可以通过最小缓冲区直接演示:
管理员已设置登录后刷新可查看2、我们确认了NTFS数据长度为4、8和11字节时的崩溃情况。
长度为12字节或更长的对应于格式良好的NTFS字段(其中包含一个24字节的时间戳三元组),不会崩溃。
3、攻击向量是任何应用程序,该应用程序:
(1)接受用户上传的zip文件
(2)使用yauzl解析这些zip文件
(3)调用entry.getLastModDate()解析条目
攻击者创建一个包含NTFS扩展字段的单个条目的zip文件(0x000a),该扩展字段包含恰好4、8或11字节的数据有效负载。
当应用程序处理此文件时,NTFS解析器循环溢出,readUInt16LE() 抛出异常,Node.js进程退出。
四、影响范围
yauzl <= 3.2.0
五、修复建议
yauzl >= 3.2.1
六、参考链接
管理员已设置登录后刷新可查看