Carrot disclosure: Forgejo
Carrot disclosure: Forgejo
Since Fedora moved from Pagure to Forgejo, I finally had an incentive to take a good look at Forgejo’s security posture. The results aren’t pretty to be honest: SSRF in a lot of places, no CSP/Trusted-Types, a bit of ghetto templating in javascript, cryptographic malpractices, overlooks in the authentication mechanisms (OAuth2, OTP, sessions/access handling, post-compromission recovery, …), a bunch of low-hanging DoS, information leak all over the place, various TOCTOU, …
自从 Fedora 从 Pagure 迁移到 Forgejo 后,我终于有了动力去仔细审视一下 Forgejo 的安全状况。老实说,结果并不乐观:随处可见的 SSRF(服务端请求伪造)、没有 CSP/Trusted-Types、JavaScript 中粗糙的模板处理、加密实践上的不规范、身份验证机制(OAuth2、OTP、会话/访问控制、入侵后的恢复等)中的疏漏、大量低门槛的 DoS 漏洞、到处泄露的信息、各种 TOCTOU(检查时机与使用时机不一致)问题等等。
All in all, it took me one evening after work to find a good amount of vulnerabilities (adding to the one I got from looking at gitea at some point in the past), and chain them to obtain a full-blown RCE, some secrets leaks, a bunch of persistent account access, a handful of OAuth2 privesc, … Fortunately (or unfortunately depending who you’re asking), the RCE relies on open registration, and on a configuration option set to a non-default value (which is the case on some instances I’ve looked at, so nothing exotic), meaning that its selling value is pretty low/nonexistent.
总而言之,我只花了一个下班后的晚上就发现了大量漏洞(加上我过去研究 Gitea 时发现的一个),并将它们串联起来,实现了完整的 RCE(远程代码执行)、一些密钥泄露、多个持久化账户访问权限以及若干 OAuth2 提权等。幸运的是(或者不幸的是,取决于你问谁),这个 RCE 依赖于开放注册功能以及一个非默认的配置选项(我在查看的一些实例中确实存在这种情况,所以并不罕见),这意味着它的“黑市价值”相当低甚至不存在。
I could disclose the bugs to Forgejo, they even have a Security Policy, with a lot of MUST/MUST NOT about what I must or mustn’t do should I decide to go this way. But given the sorry state of the codebase, I’m pretty sure I could spend another evening and find another chain. I could fix the issues myself and send pull-requests, but oh well.
我本可以将这些漏洞披露给 Forgejo,他们甚至有安全策略,里面写满了如果我决定走这条路时必须或禁止做的事情。但考虑到代码库糟糕的状态,我敢肯定,我再花一个晚上还能找到另一条漏洞链。我也可以自己修复这些问题并提交 Pull Request,但……唉。
I discussed the conundrum with a friend of mine, and was told to put my money where my mouth is, and just go with carrot disclosure that I usually advocate for in this kind of situation: Carrot Disclosure, dangling a metaphorical carrot in front of the vendor to incentivise change. The main idea is to only publish the (redacted) output of the exploit for a critical vulnerability, to showcase that the software is exploitable. Now the vendor has two choices: either perform a holistic audit of its software, fixing as many issues as possible in the hope of fixing the showcased vulnerability; or losing users who might not be happy, running a known-vulnerable software. Users of this disclosure model are of course called Bugs Bunnies.
我和一位朋友讨论了这个困境,他建议我言出必行,直接采用我通常在这种情况下所倡导的“胡萝卜披露”(Carrot Disclosure):在厂商面前悬挂一根隐喻的“胡萝卜”,以激励其做出改变。其核心思想是仅发布针对关键漏洞的(脱敏后的)利用输出,以展示该软件存在可被利用的风险。现在厂商有两个选择:要么对软件进行全面审计,尽可能多地修复问题,以期修复被展示的漏洞;要么失去那些不愿运行已知漏洞软件的用户。使用这种披露模式的用户当然被称为“兔八哥”(Bugs Bunnies)。
So without further ado:
废话少说,直接上演示:
$ python3 ./poc/chain_alpha.py --target http://127.0.0.1:3000 > out.txt
$ grep Backdoor out.txt
[+] Backdoor admin created: svc_ljeopgid / dukecepapsygiqks!A1
$ tail -n17 out.txt
================================================================
[+] COMMAND EXECUTION CONFIRMED!
================================================================
Server-side hook output (received via git push stderr):
remote: ==========================================
remote: FORGEJO RCE PoC - Command Execution Proof
remote: ==========================================
remote: hostname: chernabog
remote: uid: uid=1000(jvoisin) gid=1000(jvoisin) groups=1000(jvoisin),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
remote: date: Tue Apr 28 19:16:59 UTC 2026
remote: proof: chernabog
remote: ==========================================
================================================================
$ sha256 ./poc/chain_alpha.py
c10d28a5ff74646683953874b035ca6ba56742db2f95198b54e561523e1880d7 ./poc/chain_alpha.py
$ ls -l ./poc
total 140
-rw-r--r--. 1 jvoisin jvoisin 23530 Apr 28 21:18 chain_alpha.py
-rw-r--r--. 1 jvoisin jvoisin 6382 Apr 28 01:14 chain_beta.py
-rw-r--r--. 1 jvoisin jvoisin 11410 Apr 28 21:54 chain_gamma.py
-rw-r--r--. 1 jvoisin jvoisin 10334 Apr 28 22:20 leak_secrets.py
-rw-r--r--. 1 jvoisin jvoisin 9171 Apr 28 23:15 merge.py
-rw-r--r--. 1 jvoisin jvoisin 83861 Apr 27 23:59 NOTES.md
$
[edit] the corresponding Mastodon post was removed as “Some of your posts have been found to violate one or more community guidelines and have been subsequently removed by the moderators of infosec.exchange.”
[编辑] 相应的 Mastodon 帖子已被删除,理由是:“您的部分帖子被发现违反了一项或多项社区准则,并已被 infosec.exchange 的版主删除。”