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

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2026-5061 — Consul Template 在模板评估期间验证了符号链接指向的位置,但其后续的依赖获取读取的是原始路径。在这两个操作之间重新指向该链接,会将沙箱内的文件引用转变为沙箱外的文件泄露。 | Kitploit
工具/GitHubGitHub/0xmrma/cve-2026-5061
漏洞分析代码分析漏洞利用数据泄露学习与教育
GitHub0xmrma/cve-2026-5061

CVE-2026-5061

Consul Template 在模板评估期间验证了符号链接指向的位置,但其后续的依赖获取读取的是原始路径。在这两个操作之间重新指向该链接,会将沙箱内的文件引用转变为沙箱外的文件泄露。

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

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-5061

Consul Template 在模板评估期间验证了符号链接的指向位置,但其后的依赖项获取读取的却是原始路径。在这两个操作之间重新定向链接,会将沙箱内的文件引用变成沙箱外的文件泄露。

简介

我在审查 HashiCorp Consul Template 时发现了这个问题,当时我带着一个非常具体的安全疑问:

如果 sandbox_path 在模板评估期间验证了符号链接,那么后续的文件读取是否仍然绑定到同一个已验证的目标?

在这个案例中,答案是否定的。

file 模板助手在模板评估期间解析了路径并强制执行了配置的沙箱。但在那次检查之后,它使用原始未处理的路径创建了一个文件依赖项。

该依赖项稍后才被获取。

如果攻击者在验证与依赖项获取之间的间隙中重新定向了符号链接,Consul Template 就可能读取沙箱外的文件。如果链接在下一次渲染前被恢复,沙箱验证会再次通过,缓存的沙箱外内容仍会被渲染。

该问题最终成为 CVE-2026-5061。

HashiCorp 公告: HCSEC-2026-12
IBM 公告: CVE-2026-5061 安全公告
CVE: CVE-2026-5061
修复版本: 0.42.0

photo0


攻击链

attacker-controlled in-sandbox symlink -> sandbox validation resolves to safe target -> dependency stores original raw path -> attacker retargets link outside sandbox -> dependency fetch reads external file -> attacker restores safe link -> next validation passes -> cached external content is rendered


Consul Template 的作用

Consul Template 是一款用于渲染 Consul 和 Vault 数据的模板渲染工具。

它可以持续运行、监视依赖项的变更,并将数据渲染到文件或环境变量中供应用程序使用。

file 模板助手用于读取本地文件并将其内容插入到渲染输出中。

由于本地文件读取可能暴露进程可读取的机密信息,Consul Template 提供了 sandbox_path 作为该助手的边界。

文档中描述的安全属性非常明确:

  • 传给 file 的路径必须位于配置的沙箱之内
  • 相对路径不得逃逸出沙箱
  • 链接目标不得将已允许的路径变成对外部文件的读取

这使得 sandbox_path 成为真正的安全边界。

关键问题不在于路径看起来是否位于沙箱之下。

真正的问题是:

被读取的文件是否仍然是那个通过了沙箱验证的文件?

在受影响版本中,答案是否定的。


为什么这个攻击面值得关注

文件系统沙箱的失败点往往在于路径验证与实际文件访问之间的间隙。

常见的模式是:

  • 验证路径
  • 将控制权交还给文件系统
  • 稍后再次使用该路径
  • 假设它仍然指向同一个对象

当攻击者可以在两个操作之间修改符号链接或等效的文件系统重定向时,这种假设就是不安全的。

Consul Template 使这个攻击面格外有趣,因为模板评估和依赖项获取是两个独立的阶段。

这种分离引出了正确的安全问题:

依赖项获取是绑定到已验证的目标,还是会再次解析攻击者可控的路径?

这就是我聚焦的边界。


根本原因

根本原因是检查时间与使用时间(time-of-check to time-of-use)之间的不匹配,具体发生在:

  • template/funcs.go 中的沙箱验证
  • dependency/file.go 中后续的依赖项读取

在测试的修订版本中,fileFunc() 执行了以下操作:

root@kitploit:~
normalized := strings.TrimSpace(s)
err := pathInSandbox(sandboxPath, normalized)
if err != nil {
    return "", err
}

d, err := dep.NewFileQuery(s)

验证助手在检查包含关系之前正确地解析了符号链接:

root@kitploit:~
sandboxResolved, err := filepath.EvalSymlinks(filepath.Clean(sandbox))
targetResolved, err := filepath.EvalSymlinks(filepath.Clean(path))

rel, err := filepath.Rel(sandboxResolved, targetResolved)
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
    return fmt.Errorf("'%s' is outside of sandbox", path)
}

因此沙箱检查本身理解了解析后的目标。

但那个解析后的目标被丢弃了。

NewFileQuery() 转而存储了原始字符串:

root@kitploit:~
return &FileQuery{
    stopCh: make(chan struct{}, 1),
    path:   s,
}, nil

随后,依赖项获取再次读取了该路径:

root@kitploit:~
data, err := os.ReadFile(d.path)

这就是整个漏洞。

代码检查了一次文件系统解析,随后却使用了另一次解析的结果。

为什么它可以被利用

因为符号链接是可变的。

攻击者不需要让沙箱外的目标直接通过 pathInSandbox()。

他们只需要在验证之后、Fetch() 执行读取之前,改变那个已获准的原始路径所解析到的目标。

具体时序是:

  • 在 pathInSandbox() 期间,链接解析到一个安全文件
  • 验证成功
  • FileQuery 记录了原始链接路径
  • 链接被替换或重新定向到外部文件
  • os.ReadFile(d.path) 再次解析该链接
  • 外部文件被读取
  • 获取的值被缓存在模板缓存中
  • 链接在下一次模板评估之前被恢复
  • 验证再次成功
  • 缓存的沙箱外内容被返回并渲染

最后一步至关重要。

恢复安全链接并不会将已经获取的机密从依赖项缓存中移除。


为什么这是安全问题,而不仅仅是文件系统竞态

重要的区别在于对明确安全控制的绕过。

这不仅仅是:

"Consul Template 监视期间文件发生了变化"

监视文件变化是预期行为。

真正的问题是:

一个通过了文档所述沙箱限制的路径,后来可能被用来读取该沙箱之外的文件

这是直接的可信边界失效。

应用程序已经做出了安全决策:

  • 此目标位于沙箱内
  • 因此注册并获取它是安全的

但后续的获取并没有与支撑该决策的目标绑定。

正是这一点把普通的文件系统可变性变成了漏洞。


概念验证

我围绕确切的评估与依赖项获取时序构建了一个独立的复现程序。

可控的设置包括:

  • 一个配置好的沙箱目录
  • 沙箱内的一个安全文件
  • 沙箱外的一个机密文件
  • 沙箱内的一个被监视的符号链接路径
  • 围绕依赖项获取的确定性链接切换

复现流程如下:

  1. 将被监视的符号链接指向沙箱内的安全文件。
  2. 评估 file 助手,使沙箱验证成功,并将原始路径注册为依赖项。
  3. 在依赖项获取之前,将被监视的符号链接重定向到沙箱外的机密文件。
  4. 运行依赖项获取,让 os.ReadFile(d.path) 通过重定向后的链接进行读取。
  5. 将获取的值存储到模板缓存中。
  6. 将符号链接恢复到沙箱内的安全文件。
  7. 再次评估模板。
  8. 确认沙箱验证仍然通过。
  9. 确认渲染输出包含之前获取的沙箱外机密。

观察到的行为是:

  • 初始沙箱验证通过
  • 依赖项保留了原始链接路径
  • 链接改变后,获取读取了沙箱外的文件
  • 安全目标在下一次渲染前被恢复
  • 下一次沙箱检查通过
  • 渲染输出与外部机密在 SHA-256 上完全一致

这个哈希比较至关重要。

它证明了最终渲染的值不是过期的安全内容、文件名痕迹或错误路径的副作用。

它就是沙箱外文件的逐字节内容。


为什么 PoC 采用这种方式

对于 TOCTOU 问题,最有力的证明必须掌控时间线。

仅仅展示符号链接可以指向沙箱外部是较弱的证据,因为 pathInSandbox() 在观察到外部目标时已经会拒绝它。

该安全论断依赖于按顺序证明所有这些状态:

  • 验证时安全
  • 获取时不安全
  • 渲染时再次安全
  • 外部内容仍然从缓存中被消费

这就是为什么复现程序明确分离了模板验证、依赖项获取、链接恢复和下一次渲染。

PoC 不依赖随机时序或反复猜测。

它以确定性的方式驱动了整个易受攻击的生命周期。

这使得根本原因和影响都更容易论证。


利用条件与影响范围

此问题需要本地文件系统影响力。

攻击者需要足够的权限,在易受攻击的时间窗口内创建、替换或重定向相关符号链接或等效的链接路径。

Consul Template 进程还必须:

  • 拥有读取外部目标的权限
  • 评估使用受攻击者影响路径的模板
  • 将渲染结果暴露在攻击者能够获取或影响其用途的地方

这些条件很重要。

这并不是默认部署下未经身份验证的远程任意文件读取。

但在受影响的本地信任模型内,其影响是实实在在的:

  • 绕过文档所述的 sandbox_path 限制
  • 沙箱外的本地文件泄露
  • 可能暴露 Consul Template 进程可读取的机密信息

当进程拥有对敏感凭据、服务配置、令牌或私钥的访问权限时,风险会进一步增加。


严重性与分类

HashiCorp 将该问题分类为:

  • CWE-59:文件访问前的链接解析不当(Improper Link Resolution Before File Access)
  • CVSS 3.1:
root@kitploit:~
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N

公布的基准评分为:

root@kitploit:~
4.7 / Medium

该评分反映了真实的约束条件:

  • 本地攻击向量
  • 影响路径所需权限较低
  • 攻击复杂度高,因为链接必须在相关的生命周期窗口内发生变化
  • 无需受害者交互
  • 如果获取到敏感的进程可读文件,保密性影响较高

这是一个合理的分类。

该问题对攻击者的前置要求较少,但沙箱绕过及其导致的文件泄露都是切实存在的。


受影响版本

HashiCorp 公告列出了:

root@kitploit:~
Affected: consul-template up to 0.41.4
Fixed:    consul-template 0.42.0

修复于 2026 年 4 月 15 日 发布的 0.42.0 版本中。


修复分析

修复方案很小,并且直接针对了被破坏的绑定关系。

补丁不再验证解析后的目标却用原始输入构建依赖项,而是返回解析后的路径,并将该路径传给 NewFileQuery():

root@kitploit:~
resolvedPath, err := resolveSandboxedPath(
    sandboxPath,
    strings.TrimSpace(s),
)
if err != nil {
    return "", err
}

d, err := dep.NewFileQuery(resolvedPath)

安全属性从:

  • 验证解析后的目标
  • 丢弃解析后的目标
  • 稍后通过原始路径获取

变为:

  • 验证解析后的目标
  • 保留解析后的目标
  • 通过该已验证路径获取

这封堵了所报告的符号链接重定向路径,因为改变原始链接不再会改变依赖项存储的路径。

补丁还添加了一个针对性的回归测试,覆盖了确切的时序:

  • 验证期间为安全符号链接
  • 获取期间为外部符号链接
  • 在下一次调用前恢复安全链接
  • 不得返回缓存的沙箱外机密

这正是修复此类 bug 时你想要的补救方式:

  • 修复被破坏的验证/使用绑定
  • 保留沙箱行为
  • 为完整的竞态生命周期添加回归测试

披露时间线

我于 2026 年 3 月 20 日 私下向 HashiCorp 安全团队报告了此问题。

报告内容包括:

  • 源代码级别的根本原因分析
  • 验证到获取的 TOCTOU 时序
  • 独立的确定性复现程序
  • 运行时输出
  • SHA-256 证据,证明渲染数据与外部机密一致
  • 受影响修订版本的详细信息

HashiCorp 在 0.42.0 中修复了该问题,并于 2026 年 5 月 12 日 发布了 HCSEC-2026-12。

IBM 针对同一个 CVE 发布了相应的安全公告。

两份官方公告都将该报告归属于:

Mohamed Abdelaal (0xmrma)


这个 Bug 真正教会我们的

核心教训很简单:

如果后续的文件操作可以将路径解析到不同的对象,那么仅验证路径是不够的

这条规则远远不止适用于 Consul Template。

任何执行以下操作的代码都适用:

  • 沙箱检查
  • 上传目标检查
  • 压缩包解压
  • 本地机密读取
  • 临时文件处理
  • 权限分离的文件操作

更深层的安全属性不是:

"路径字符串曾经看起来是安全的"

而是:

敏感操作所使用的对象,必须是那个通过了验证的对象

在这个案例中,Consul Template 验证了一个解析结果,却获取了另一个。

这个间隙已经足够了。


要点总结

  • sandbox_path 是一个明确的本地文件安全边界
  • pathInSandbox() 正确地解析并验证了符号链接目标
  • 解析后的目标在验证后被丢弃
  • FileQuery 保留了原始的、受攻击者影响的路径
  • 依赖项获取随后通过 os.ReadFile 再次解析了该路径
  • 恢复安全链接并不会移除已缓存的沙箱外内容
  • PoC 通过 SHA-256 证明了沙箱外文件的逐字节泄露
  • 0.42.0 版本通过将依赖项绑定到解析且验证后的路径修复了该 bug

结语

这个漏洞与绕过字符串前缀检查无关。

它关乎时间与身份。

Consul Template 在模板评估期间检查了链接指向哪里。 依赖项获取稍后又向文件系统问了同样的问题。 攻击者可以在这两个时刻之间改变答案。

这就是它成为 CVE-2026-5061 的原因。

已在 consul-template 0.42.0 中修复。

下载工具