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

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

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

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

工具目录

分类

查看所有分类
Loading categories
CVE-2026-14361 — Consul Template 的 writeToFile 助手直接打开操作者提供的目标路径,并跟随链接的路径组件,从而允许渲染输出逃逸预期目录并覆盖已存在的文件。 | Kitploit
工具/GitHubGitHub/0xmrma/cve-2026-14361
漏洞分析代码分析漏洞利用学习与教育
GitHub0xmrma/cve-2026-14361

CVE-2026-14361

Consul Template 的 writeToFile 助手直接打开操作者提供的目标路径,并跟随链接的路径组件,从而允许渲染输出逃逸预期目录并覆盖已存在的文件。

查看仓库
19天前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-14361

Consul Template 的 writeToFile 辅助函数直接打开操作者提供的目标路径,并跟随被链接的路径组件,从而使渲染输出能够逃逸预期目录并覆盖已存在的文件。

简介

我在审查 HashiCorp Consul Template 时发现了这个问题,当时脑海中有一个直接的文件系统安全问题:

如果 writeToFile 收到的路径看似位于预期目录之内,它是否会验证文件系统实际将数据写入何处?

在本例中,答案是否定的。

writeToFile 模板辅助函数通过 os.Create() 或 os.OpenFile() 直接打开用户提供的最终路径。

这些操作会跟随目标路径中已存在的符号链接、目录联接(directory junction)以及等效的文件系统重定向。

这意味着路径字符串可以始终保持在操作者预期的根目录之下,而实际写入却落在其他位置。

在我可控的概念验证中,一个被链接的父目录将渲染输出重定向到预期目录树之外,并导致一个已存在的目标文件被覆盖。

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

HashiCorp 公告: HCSEC-2026-20
IBM 公告: CVE-2026-14361 安全公告
CVE: CVE-2026-14361
修复版本: 0.42.1

photo0


攻击链

操作者提供的目标路径看似位于预期根目录内 -> 攻击者预先放置被链接的父组件或最终路径组件 -> writeToFile 直接打开该路径 -> 文件系统将写入解析到预期目录之外 -> 渲染的机密被重定向 -> 已存在的目标可能被覆盖


writeToFile 的作用

Consul Template 从 Consul 和 Vault 等来源渲染数据。

writeToFile 辅助函数允许模板将选定内容写入单独的本地文件,同时应用所请求的所有者、组和权限模式。

HashiCorp 的文档专门以 PKI 材料演示了该辅助函数:

root@kitploit:~
private key -> writeToFile /my/path/to/cert.key
certificate authority -> writeToFile /my/path/to/cert.pem
certificate -> append to /my/path/to/cert.pem

这使得它不仅仅是一个普通的输出辅助函数。

跨越此边界的内容可能包括:

  • 私钥
  • 证书
  • 从 Vault 获取的机密
  • 配置值
  • 服务凭据

关键问题不在于 writeToFile 能否创建所请求的文件名。

真正的问题是:

该进程是写入操作者预期的文件系统位置,还是仅仅写入路径在打开时所解析到的任何对象?

在受影响版本中,它信任的是后者。


为什么这个攻击面值得研究

写入辅助函数是高价值的安全攻击面,因为它们从应用程序数据跨越到文件系统变更。

有趣的失败往往不是经典的 ../ 路径遍历。

而是解析失败:

  • 字符串看起来安全
  • 目录树看起来由操作者控制
  • 某个被链接的组件改变了真实目标
  • 打开调用自动跟随该重定向

当进程以比攻击者更高的文件系统权限运行时,这一点尤其重要。

低权限的本地攻击者可能无法直接覆盖敏感文件。

但如果他们能够影响预期写入目录下的某个路径组件,权限更高的 Consul Template 进程可能会替他们执行写入。

这就是我关注的安全边界。


我关注的安全边界

我并没有将其当作一次泛泛的路径遍历审查。

所提供的路径并不需要 .. 段。

它可以始终在字面上保持在预期根目录之内。

更强的问题是:

在写入敏感内容之前,是否会拒绝被链接的目标组件?

这个问题对以下两种情况都至关重要:

  • 被链接的最终文件名
  • 重定向整个剩余路径的被链接目录组件

第二种情况尤其值得关注,因为日志和配置中仍会显示预期目录下一个看似无害的路径。

而文件系统将其解析到其他位置。


根本原因

根本原因是直接基于路径创建文件,而没有进行感知链接的验证。

在测试的修订版本中,writeToFile() 选择两种打开路径之一。

追加模式使用:

root@kitploit:~
f, err = os.OpenFile(
    path,
    os.O_APPEND|os.O_WRONLY|os.O_CREATE,
    perm,
)

普通写入模式使用:

root@kitploit:~
dirPath := filepath.Dir(path)

if _, err := os.Stat(dirPath); err != nil {
    err := os.MkdirAll(dirPath, os.ModePerm)
    if err != nil {
        return "", err
    }
}

f, err = os.Create(path)

两种打开路径都没有在文件打开前拒绝被链接的目标组件。

这一点很重要,因为:

  • os.Create(path) 会跟随已存在的文件系统重定向并截断解析后的文件
  • os.OpenFile(path, ...) 在追加模式下会跟随被链接的路径组件
  • 被链接的目录会改变最终文件名的解析位置
  • 被链接的最终组件可以将打开操作重定向到另一个已存在的文件

写入之后仍延续了同样的基于路径的假设。

所有者和权限再次通过路径来应用:

root@kitploit:~
err = os.Chown(path, uid, gid)
err = os.Chmod(path, perm)

这意味着元数据操作也绑定到了一个可变的路径名上,而非已经打开的文件描述符。

为何可以被利用

因为攻击者可以在 writeToFile 运行之前预先布置好重定向。

基本攻击不需要概率性的竞态条件。

攻击序列非常简单:

  • 操作者在预期根目录下配置目标
  • 攻击者获得足够的本地访问权限,在该写入位置下方创建或替换一个被链接的组件
  • 路径字符串看起来仍然保持在预期根目录之下
  • writeToFile 直接打开它
  • 操作系统跟随链接或联接
  • 渲染输出落到解析后的目标
  • 如果该目标已存在,普通创建模式会将其截断并覆盖

这就是整个漏洞。


为什么这是一个安全问题,而不仅仅是正常的符号链接行为

诚然,操作系统在基于路径的文件打开过程中通常会跟随符号链接。

但这并不意味着这是安全的应用程序行为。

安全问题不是:

“Go 是否按文档所述运行?”

真正的问题是:

一个写入敏感渲染数据的辅助函数,是否验证了解析后的目标与操作者预期的位置一致?

在受影响版本中,它没有进行验证。

这一区别很重要,因为 Consul Template 可能:

  • 作为长期运行的服务运行
  • 能够访问源自 Vault 的机密
  • 在高权限账户下运行
  • 拥有本地攻击者无法直接获得的写入权限

在这种情境下跟随攻击者预先布置的文件系统重定向,会引发真实的权限与信任边界问题。


概念验证

我围绕测试提交中 writeToFile 的确切行为构建了一个独立的复现程序。

可控环境使用了:

  • 一个预期的输出根目录
  • 一个位于该根目录之外的外部目录
  • 预期根目录下的一个被链接的父组件
  • 重定向目录中一个已存在的目标文件
  • 传入 writeToFile 的可控机密内容

复现流程如下:

  1. 创建预期的输出根目录。
  2. 创建单独的外部目标目录。
  3. 在外部目录中放置一个已存在的目标文件。
  4. 在预期根目录下创建解析到外部目录的被链接父目录。
  5. 使用被链接的父目录构建最终目标路径,同时保持路径字符串位于预期根目录之下。
  6. 使用可控机密内容调用易受攻击的写入路径。
  7. 解析并检查实际目标。
  8. 将最终文件字节和 SHA-256 与机密输入进行比较。

观察到的行为是:

  • 预期目标字符串保持在预期根目录之下
  • 被链接的父目录将实际写入重定向到该根目录之外
  • writeToFile 跟随了重定向
  • 已存在的外部目标被覆盖
  • 最终的外部文件在 SHA-256 上与机密输入完全一致

这证实了该论断的两个部分:

  • 输出能够逃逸预期目录树
  • 解析后目标处的现有文件可能被覆盖

为什么以这种方式设计 PoC

最终组件的符号链接已经足以证明不安全的链接跟随。

但被链接的父组件证明了更强的操作性要点:

  • 配置的文件名可以看起来完全正常
  • 路径可以在字面上保持在预期根目录之下
  • 重定向可以发生在路径中更高的位置
  • 最终写入仍然可以落在其他位置

已存在的目标也很关键。

没有它,PoC 只能展示意外的文件创建。

通过从现有文件开始并验证其最终哈希,复现程序直接证明了覆盖行为。

这使得结果比仅基于源代码的论断更加具体。


利用条件与影响范围

该问题需要本地文件系统的影响能力。

攻击者需要足够的访问权限,才能在预期写入位置或其下方创建或修改符号链接、目录联接或等效重定向。

随后,Consul Template 进程必须通过该路径进行写入。

影响程度在很大程度上取决于进程权限和渲染内容。

实际可能造成的结果包括:

  • 将模板输出重定向到操作者预期目录之外
  • 将敏感渲染数据置于攻击者可读取的位置
  • 覆盖解析后目标处的现有文件
  • 当进程以高权限运行时放大影响
  • 当内容包含密钥、证书或机密时增加保密性风险

这不是默认安装下可利用的远程未认证任意写入。

但在高权限或共享文件系统部署中,这是一个明确的本地信任边界失败,并具有切实的影响。


严重性与分类

HashiCorp 将该问题归类为:

  • CWE-59:文件访问前的链接解析不当
  • 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 and including 0.42.0
Fixed:    consul-template 0.42.1

修复版本随 0.42.1 版本于 2026 年 7 月 8 日发布。


修复分析

0.42.1 补丁在多个层面加固了写入路径。

1. 拒绝被链接的目标组件

修复后的辅助函数使用 os.Lstat() 检查:

  • 直接父目录
  • 最终目标组件

并在这些组件是链接时予以拒绝。

这封堵了报告和回归测试所涵盖的直接父目录重定向与最终文件符号链接场景。

2. 在受支持的平台上对最终打开使用 O_NOFOLLOW

在受支持的 Unix 平台上,目标使用 O_NOFOLLOW 打开。

这样,如果在预检查与打开之间最终组件变成符号链接,打开操作本身就会失败。

这一点很重要,因为仅靠预检查就可能产生另一个 TOCTOU 窗口。

平台特定实现是 Windows 上的空操作(no-op),因为 Windows 无法通过相同机制使用 O_NOFOLLOW。

3. 通过已打开的描述符应用元数据

补丁将基于路径的所有者和模式操作替换为基于描述符的操作:

root@kitploit:~
f.Chown(uid, gid)
f.Chmod(perm)

这将元数据更改绑定到实际打开的文件上,而不是稍后再次解析路径。

4. 遇到意外 stat 错误时故障关闭

辅助函数现在仅在 stat 失败确实是 os.IsNotExist 时才创建父目录。

其他错误(例如权限失败)会被直接返回,而不会继续进入写入路径。

回归测试覆盖

补丁添加了针对以下内容的专项测试:

  • 最终组件符号链接拒绝
  • 被链接父目录拒绝
  • 正常路径成功
  • 追加模式下最终符号链接拒绝
  • 字节级验证敏感目标保持不变

一个重要的修复边界

公开的补丁讨论明确记录了一个重要的限制。

新的验证检查:

  • 直接父目录
  • 最终路径组件

它不会遍历并拒绝每一个更上层的祖先组件。

维护者选择这一边界是因为 writeToFile 没有配置沙箱根目录来锚定完整的包含性检查,而且常见操作系统可能在路径前缀中包含合法的受管链接,例如 macOS 上的 /var -> /private/var。

O_NOFOLLOW 同样只保护最终组件,而非每一个祖先目录。

这并不会改变所报告漏洞的状态或官方修复版本。

但它确实明确了补丁所提供的精确安全属性:

  • 所报告的立即父目录和最终组件重定向路径被拒绝
  • 元数据操作绑定到已打开的描述符
  • 该辅助函数并不声称对所有祖先路径提供通用的文件系统沙箱

这一区别值得在技术文章中保留。


披露

我于 2026 年 3 月 21 日以第二个独立 Consul Template 发现的名义,私下向 HashiCorp 安全团队报告了此问题。

报告内容包括:

  • 源代码层面的根本原因分析
  • 独立的概念验证
  • 运行时输出
  • 预期路径与解析后路径的证据
  • 文件行为的前后对比
  • SHA-256 确认重定向目标与机密输入一致
  • 受影响修订版本的详细信息

最初的跟进邮件并未出现在安全团队的报告队列中,很可能是被邮件列表垃圾邮件过滤器拦截了。

在我重新转发完整报告后,HashiCorp 联系了工程团队,并将其与第一个 Consul Template 问题分开调查。

HashiCorp 在 0.42.1 中修复了该漏洞,并于 2026 年 7 月 8 日发布了 HCSEC-2026-20。

IBM 为同一 CVE 发布了相应的安全公告。

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

Mohamed Abdelaal (0xmrma)


这个漏洞真正教会我们什么

核心教训很简单:

路径字符串并不等同于它所命名的文件系统对象

只要特权代码写入受攻击者影响的路径,这一区别就至关重要。

仅仅检查字符串是否以预期目录开头是不够的。

即使是没有遍历序列的干净路径,也可能通过以下方式解析到其他位置:

  • 符号链接
  • 目录联接
  • 挂载重定向
  • 可变的路径组件

敏感操作必须绑定到身份已在正确边界得到验证的目标。

该问题还强化了一条更广泛的规则:

当你已经拥有打开的文件描述符时,应通过该描述符执行安全敏感操作,而不是再次解析路径

这正是基于描述符的 Chown 和 Chmod 更改至关重要的原因。


要点

  • writeToFile 被文档化为用于证书和私钥等敏感材料
  • 受影响版本直接使用 os.Create 或 os.OpenFile 打开目标
  • 被链接的父组件和最终组件可能重定向实际写入
  • 配置的路径可以保持在预期根目录之下,而解析后的目标却在根目录之外
  • 普通创建模式可能截断并覆盖已存在的目标
  • 基于路径的 Chown 和 Chmod 引入了额外的可变路径信任问题
  • PoC 通过字节级精确的 SHA-256 比较证明了重定向和覆盖
  • 0.42.1 版本添加了链接检查、受支持平台上的 O_NOFOLLOW、基于描述符的元数据操作以及回归测试

结语

这个漏洞与 ../ 遍历无关。

路径字符串看起来是正确的。

文件系统目标却不是。

Consul Template 接受了操作者预期的路径,跟随了攻击者预先布置的重定向,并将渲染内容写入了另一个文件。

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

已在 consul-template 0.42.1 中修复。

下载工具