Carrot Disclosure: Forgejo

Published: (April 28, 2026 at 06:14 PM EDT)
3 min read

Source: Hacker News

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: SSRF in many places, no CSP/Trusted‑Types, a bit of ghetto templating in JavaScript, cryptographic malpractices, oversights in the authentication mechanisms (OAuth2, OTP, session/access handling, post‑compromise recovery, …), a bunch of low‑hanging DoS vectors, information leaks all over the place, various TOCTOU issues, …

All in all, it took me one evening after work to find a good amount of vulnerabilities (adding to the ones I discovered in Gitea in the past), and chain them to obtain a full‑blown RCE, several secret leaks, persistent account access, and a handful of OAuth2 privilege‑escalations.

Fortunately (or unfortunately, depending on who you ask), 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 examined, so nothing exotic). This limits its selling value. I could disclose the bugs to Forgejo—they even have a Security Policy with many MUST/MUST NOT clauses—but given the sorry state of the codebase, I’m confident I could spend another evening finding another chain. I could fix the issues myself and send pull‑requests, but the effort required is non‑trivial.

After discussing the conundrum with a friend, I decided to use carrot disclosure, a model I usually advocate for in this kind of situation.


Carrot Disclosure

Carrot disclosure involves dangling a metaphorical carrot in front of the vendor to incentivise change. The main idea is to publish only the redacted output of an exploit for a critical vulnerability, demonstrating that the software is exploitable. The vendor then has two choices:

  1. Perform a holistic audit of the software, fixing as many issues as possible in the hope of addressing the showcased vulnerability.
  2. Lose users who are unwilling to run known‑vulnerable software.

Users of this disclosure model are, of course, called Bugs Bunnies.


Proof of Concept

Below is a trimmed excerpt of the exploit output that confirms command execution on a vulnerable Forgejo instance.

$ 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: ==========================================
================================================================

Verification

$ sha256 ./poc/chain_alpha.py
c10d28a5ff74646683953874b035ca6ba56742db2f95198b54e561523e1880d7  ./poc/chain_alpha.py

Repository Layout

$ 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
0 views
Back to Blog

Related posts

Read more »

Don't Open a Pull Request Yet

Why contributions matter You want to contribute to open source, but you don’t want to be that person: the one who opens a pointless pull request just to add th...