Title: Carrot disclosure: Forgejo
Date: 2026-04-28 22:40

Since Fedora [moved from Pagure to
Forgejo](https://communityblog.fedoraproject.org/the-forge-is-our-new-home/), I
finally had an incentive to take a good look at
[Forgejo](https://forgejo.org/)'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, … 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](https://about.gitea.com/) 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. I could disclose the
bugs to Forgejo, they even have a [Security
Policy](https://codeberg.org/forgejo/governance/src/branch/main/SECURITY-POLICY.md),
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](https://codeberg.org/forgejo/forgejo/pulls/12285)
[oh](https://codeberg.org/forgejo/forgejo/pulls/12285)
[well](https://codeberg.org/forgejo/forgejo/pulls/12283).

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]({filename}/security/carrot_disclosure.md) 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.

So without further ado:

```console
$ 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
$
```
