FlawOpen

flawopen.com/Teardowns/cve-2026-102437-deepseek-reasonix-git-filter-command-injection

● CVE-2026-102437 · CVSS 7.8[2] · 高危
安全研究 · FlawOpen

CVE-2026-102437:Reasonix 差异视图 Git 命令注入

被植入的 .gitattributes/.git/config 可在 Reasonix 显示差异时运行 Git clean 过滤器。将 npm 包升级至 1.39.3,或 Studio 升级至 2.21.0。[1], [2], [3], [6], [7]

💡 通俗易懂的原理解析 (ELI5)

想象一张分拣桌,它承诺只比较两张标签。包裹上却可以附带一张路线便条,要求分拣桌在比较内容前运行一个已注册的助手。Reasonix 禁用了 Git 的一些助手机制,但按文件调用的 clean 过滤器是另一条路径。当差异窗口要求 Git 准备文件以便比较时,该过滤器可能以开发者的权限运行。[1], [3]

核心概念与专有名词

Git clean 过滤器
Git 可执行的一种配置转换,用于将工作区文件内容转换为仓库中保存的形式。[1], [3]
.gitattributes
仓库跟踪的文件,可为匹配路径指定一个命名过滤器。[1]
.git/config
仓库本地 Git 配置,可定义命名过滤器调用的命令。普通 clone 不会复制这个本地文件。[1], [3]
差异查看器
请求 Git 准备并比较文件工作区内容与已提交内容的功能。[1]

框架映射

  • 本指南所述弱点的 CWE 参考: CWE-78

根本原因剖析 (Root Cause)

Reasonix 在 git diff 调用中加入了 --no-ext-diff 和 --no-textconv ,但这些选项控制外部 diff 和文本转换机制,并不会禁用 Git 的 clean 内容过滤器。工作区变更视图会针对选定路径调用 git diff 。如果仓库内容通过 .gitattributes 为该路径分配了过滤器,而本地 Git 配置又定义了该驱动的命令,Git 就可能在生成比较 blob 时调用它。该 CVE 描述的是本地攻击者,且需要用户查看 diff。GitLab 的披露说明,普通克隆不会传递 .git/config ;归档文件、同步目录、缓存或预构建 devcontainer 可能带有该文件。披露还描述了具有相同用户权限的本地代理直接写入仓库本地配置的情形。影响是以 Reasonix 用户权限执行本地命令,而不是仅因打开 GitLab 托管仓库就发生远程代码执行。[1], [3]

攻击执行流程分解

步骤 1

工作区收到不可信的仓库状态

开发者打开一个项目,其中跟踪的属性为文件指定了过滤器,而本地 Git 配置定义了该过滤器。维护者公告要求这两部分同时存在;普通克隆本身不会提供本地配置。[1], [3]

步骤 2

开发者请求查看文件差异

Reasonix 的变更视图会对选定文件运行 Git diff。这一界面操作就是 CVE 所需的用户交互。[1], [3]

步骤 3

Git 应用 clean 过滤器

Git 为比较准备内容时可能调用配置的 clean 过滤器。 --no-ext-diff 和 --no-textconv 禁用的是其他机制,不会阻止该过滤器。[1], [3]

步骤 4

过滤器以用户权限运行

配置的过滤器命令在 Reasonix 进程上下文中运行。已记录的后果是以该用户权限执行本地命令。[1], [2], [3]

源代码对比:漏洞与安全实现

这是用于说明的参数构造器示例,并非可直接套用的补丁。调用方必须枚举由 .gitattributes、.git/info/attributes 以及全局/系统属性文件选中的所有 driver,并且只有在完整扫描成功后才能将 discoveryComplete 设为 true。此函数只验证传入的名称并生成命令行配置覆盖项;它不会发现或核实 driver 集合。易受影响的调用来自 GitLab 的披露;防御模式是对维护者指导及已合并加固 PR 的原创综合。[1], [3], [4], [5]

源码摘录: desktop/workspace_changes.go

存在漏洞的实现
// GitLab Threat Research 引用的源代码调用;这是调用点,不是完整函数。
args := []string{"-C", base, "diff", "--no-ext-diff", "--no-textconv", "--relative", "HEAD", "--", filepath.FromSlash(rel)}
加固后的安全修复
// Go 参数构造器示例,并非供应商补丁原文。
package gitguard

import (
    "errors"
    "fmt"
    "path/filepath"
    "regexp"
)

// 调用方必须找出有效属性选中的所有过滤器,并传入完整集合。
// 此辅助函数只会在 Git 的所有配置范围内禁用这些 driver 键。
var filterDriverName = regexp.MustCompile(`^[A-Za-z0-9_-]{1,64}$`)

func hardenedDiffArgs(base, rel string, drivers []string, discoveryComplete bool) ([]string, error) {
    if !discoveryComplete {
        return nil, errors.New("cannot safely diff: filter-driver discovery failed")
    }
    args := []string{"-C", base}
    seen := map[string]struct{}{}
    for _, driver := range drivers {
        if !filterDriverName.MatchString(driver) {
            return nil, fmt.Errorf("cannot safely diff: unsupported Git filter driver name %q", driver)
        }
        // Git config 子节名称区分大小写:不要转换 driver 名称的大小写。
        key := driver
        if _, ok := seen[key]; ok {
            continue
        }
        seen[key] = struct{}{}
        for _, setting := range []string{"clean", "smudge", "process", "required"} {
            args = append(args, "-c", "filter."+driver+"."+setting+"=")
        }
    }
    return append(args, "diff", "--no-ext-diff", "--no-textconv", "--relative", "HEAD", "--", filepath.FromSlash(rel)), nil
}

工程与系统安全加固清单

参考来源