Blog
GiteaCVE-2026-60004CISA KEVGit Hook RCE

Gitea CVE-2026-60004: ti registri, invii una patch, il server Git è tuo

Gitea CVE-2026-60004 è un RCE CVSS 9.8 nell'endpoint diffpatch. La registrazione aperta trasforma un server Git self-hosted in una shell pre-auth. In CISA KEV, sfruttata ora.

Zero Hunt Research··9 min di lettura

Questa settimana la via più rapida all'esecuzione di codice remoto su 8.393 server Gitea esposti su internet era il pulsante di registrazione. CVE-2026-60004 — CVSS 9.8, aggiunta al catalogo Known Exploited Vulnerabilities di CISA il 25 agosto con scadenza di patch federale al 28 agosto — trasforma l'endpoint diffpatch di Gitea in una shell pre-auth. Non perché l'endpoint sia esposto agli utenti anonimi, ma perché Gitea viene distribuito con la registrazione aperta attiva per default: un attaccante crea un account, crea un repository, e ora possiede esattamente l'accesso in scrittura che l'exploit richiede. Tra "visitatore non autenticato" ed "esecuzione di codice come account di servizio git" ci sono tre richieste HTTP.

È la modalità di fallimento che il source control self-hosted continua a produrre: il server SCM viene trattato come infrastruttura interna, quindi distribuito con impostazioni predefinite che presumono utenti fidati, e poi finisce comunque raggiungibile da internet. Gitea è l'alternativa piccola, veloce e self-hosted a GitHub Enterprise e GitLab — ed è esattamente per questo che finisce su un VPS con IP pubblico e non viene più guardato.

Come CVE-2026-60004 trasforma una patch in un Git hook

L'interfaccia web di Gitea consente a un utente di modificare un file, generare una patch e applicarla — il flusso diffpatch. La vulnerabilità, divulgata e corretta a fine luglio e attribuita al ricercatore Shai Rod (NightRang3r), è che un contenuto di patch appositamente costruito induce Gitea a scrivere un Git hook eseguibile in un repository temporaneo e poi a invocarlo quando Git aggiorna l'indice.

I Git hook sono script shell che Git esegue in punti definiti del suo ciclo di vita — pre-receive, post-receive, update, post-commit. Sono eseguibili collocati in una directory hooks/, e Git li lancia con i privilegi di chi esegue l'operazione Git. Su un server Gitea, quello è l'account di servizio Gitea — l'utente git, spesso all'interno del container applicativo. Il meccanismo completo: si invia la patch malevola due volte, e il secondo invio scrive un eseguibile controllato dall'attaccante nella directory degli hook e lo attiva. Shell arbitraria, senza acrobazie di memory corruption, senza catene ROP — solo Git che fa esattamente ciò per cui i Git hook sono progettati, puntato su un file piazzato dall'attaccante.

L'aritmetica dei privilegi è ciò che rende questo un 9.8 invece di un 7 e qualcosa:

  • L'accesso in scrittura a un repository è l'unico requisito. Va bene qualsiasi repo su cui l'account può fare push — incluso uno appena creato dall'attaccante.
  • La registrazione aperta è il default. La configurazione out-of-box di Gitea permette a chiunque di creare un account. Così la precondizione "accesso in scrittura autenticato" collassa in "riesco a raggiungere la pagina di login".
  • L'account di servizio non è sandboxato per default. L'utente git di solito ha una shell reale e può raggiungere la rete, lo storage dei repository e qualunque segreto montato nel container.

"La nostra macchina Gitea è solo interna, dietro la VPN." Rispondeva su un IP pubblico perché uno sviluppatore ha aperto la porta 3000 per mostrare un repo, e la registrazione aperta non è mai stata disattivata. Shadowserver ha contato 8.393 server con la stessa assunzione il 27 agosto.

Undici secondi dalla scansione al miner

Non è teoria. Uno sviluppatore ha segnalato che la sua istanza Gitea non aggiornata è stata compromessa tramite scansione automatizzata, documentato da The Hacker News e Help Net Security. L'attaccante ha registrato un account, creato un repository ed eseguito codice dentro il container Gitea come utente git. Il payload era un dropper simile a un miner di criptovalute: ha eseguito ricognizione del sistema, terminato i processi miner concorrenti, scaricato un binario specifico per l'architettura, lo ha eseguito e si è cancellato. La fase attiva sull'host è durata circa undici secondi. L'unico motivo per cui la vittima se n'è accorta è stato l'uso di CPU sostenuto sopra il 70%, che ha fatto scattare l'alerting del provider di hosting.

Undici secondi è il numero su cui i difensori dovrebbero soffermarsi. Non c'è finestra perché un analista umano faccia triage di una richiesta diffpatch, non c'è tempo perché un aggiornamento di firma si propaghi, non c'è il momento del digest SIEM del mattino. Il payload di mining era quasi una cortesia — si è annunciato con la CPU. La stessa primitiva rilascia una reverse shell, esfiltra ogni repository e ruota il proprio hook di persistenza senza mai toccare il grafico della CPU. Un miner è la cosa più rumorosa che un attaccante con RCE non autenticato sul tuo source control possa scegliere di fare.

E il source control è il posto peggiore dove perdere. Un server Gitea contiene codice proprietario, token CI/CD, deploy key, segreti dei webhook e chiavi di firma. Un RCE lì non è un incidente su un singolo host — è un punto d'appoggio nella supply chain con un percorso firmato verso tutto ciò che la pipeline può raggiungere.

Perché gli scanner mancano il vero percorso d'attacco

Molti strumenti segnaleranno "Gitea versione 1.26 — vulnerabile a CVE-2026-60004". Quello è il facile 20% del finding. La parte che decide se sei davvero sfruttabile è una catena a cui un banner di versione non sa rispondere:

  1. Il server è raggiungibile da una rete non fidata?
  2. La registrazione aperta è attiva, o il confine dell'account è realmente applicato?
  3. Un account appena registrato può creare un repository (la precondizione dell'accesso in scrittura)?
  4. Il percorso diffpatch va a buon fine end-to-end e piazza un eseguibile in una directory di hook?
  5. Cosa raggiunge l'account di servizio git una volta che il codice gira — segreti, rete, altri repo?

Un numero CVSS e una stringa di versione ti danno il passo 1. I passi da 2 a 5 sono la differenza tra "teoricamente vulnerabile" e "un attaccante sta minando Monero nel tuo container proprio ora". La maggior parte delle scansioni si ferma al banner perché ragionare su una catena di exploit a più passi — registra, crea, invia due volte, piazza l'hook — è esattamente ciò che il pattern matching non sa fare.

Remediation

Considera compromessa qualsiasi Gitea raggiungibile da internet sotto la 1.27.1 finché non dimostri il contrario. Esiste un proof-of-concept pubblico e la scansione di massa è in corso.

1. Sono interessato?

Controlla la versione in esecuzione e la tua esposizione:

# Versione — qualsiasi cosa da 1.17 fino a 1.27.0 è vulnerabile
gitea --version
# oppure dal container
docker exec <gitea-container> gitea --version

# La registrazione aperta è attiva? (il moltiplicatore pre-auth)
grep -A2 '\[service\]' /etc/gitea/app.ini | grep -i DISABLE_REGISTRATION
# assente o =false significa che chiunque può auto-registrarsi

# È raggiungibile da reti non fidate? Verifica cosa ascolta su :3000 / :443
ss -tlnp | grep -E ':3000|:443'

Range interessato: da 1.17 a 1.27.0. Se risponde su un'interfaccia pubblica con la registrazione attiva, presumi una pressione di sfruttamento attiva.

2. Patch — versione corretta esatta

Aggiorna a Gitea 1.27.1 (rilasciata il 27 luglio 2026) o successiva; la manutenzione corrente è la 1.27.2. Questa è l'unica correzione completa — i controlli compensativi qui sotto restringono la finestra, non la chiudono.

3. Non puoi patchare in quest'ora? Controlli compensativi

  • Disattiva la registrazione aperta: imposta [service] DISABLE_REGISTRATION = true in app.ini e riavvia. Rimuove il percorso non autenticato — ora l'attaccante ha bisogno di credenziali reali.
  • Blocca l'endpoint sul proxy: nega le richieste POST il cui path contiene /diffpatch sul reverse proxy / WAF.
  • Sandbox dell'account di servizio: systemd NoNewPrivileges=true, PrivateTmp=true, ProtectSystem=strict; assegna all'account git /usr/sbin/nologin.
  • Toglilo da internet pubblico: metti l'istanza dietro VPN o un proxy identity-aware. Un SCM solo-interno dovrebbe essere interno.

4. Cerca segni di compromissione (MITRE ATT&CK)

L'exploit è POST a diffpatch (T1190) → shell tramite un hook piazzato (T1059) → persistenza via hook (T1505.003 / T1547) → resource hijacking (T1496).

  • Hook malevoli — ispeziona la directory hook di ogni repository per eseguibili creati di recente o inattesi:
# adatta alla tua root repo: /var/lib/gitea, /data/git, /home/git/gitea-repositories, /opt/gitea
find /var/lib/gitea/repositories -path '*/hooks/*' -type f -perm -u+x -mtime -14 -ls

Gli hook legittimi di Gitea sono gestiti e prevedibili; un pre-receive/post-receive/update piazzato dall'attaccante con mtime recente è il segnale.

  • Genealogia dei processi — il servizio Gitea che genera sh, bash, curl, wget, nc o un binario miner è un IOC di esecuzione live.
  • Log di accessoPOST a percorsi /diffpatch da account appena registrati o IP sorgente non-sviluppatore. La firma che lega la catena: una POST /diffpatch il cui IP sorgente ha colpito anche /user/sign_up pochi minuti prima.
  • L'account dell'attaccante — poiché il punto d'ingresso è l'auto-registrazione, l'intruso ha lasciato una riga nella tua tabella utenti. Cerca gli account creati nella finestra di esposizione e i token o le chiavi SSH che hanno aggiunto per la persistenza:
# Backend SQLite (adatta per Postgres/MySQL); GITEA_HOME es. /var/lib/gitea
sqlite3 "$GITEA_HOME/data/gitea.db" \
  "SELECT id,name,lower_email,datetime(created_unix,'unixepoch') FROM user ORDER BY created_unix DESC LIMIT 20;"
# Token / chiavi SSH aggiunti dagli account sospetti (persistenza)
sqlite3 "$GITEA_HOME/data/gitea.db" \
  "SELECT t.name,u.name,datetime(t.created_unix,'unixepoch') FROM token t JOIN user u ON t.uid=u.id ORDER BY t.created_unix DESC LIMIT 20;"
  • Segnale di risorse — il payload osservato si è annunciato come CPU sostenuta >70%. Non farci affidamento — la primitiva è silenziosa per default.

5. Eradica e verifica

  1. Prima preserva: copia i log di accesso (14+ giorni), i journal systemd, i log del container e qualsiasi hook malevolo per la forensics prima di toccarli.
  2. Rimuovi gli impianti: cancella gli hook piazzati, termina e rimuovi i binari rilasciati, controlla persistenza cron/systemd che la shell potrebbe aver aggiunto.
  3. Ruota tutto ciò che la macchina poteva leggere: token utente e CI/CD, deploy key, app OAuth, segreti dei webhook e chiavi di firma del codice. Presumi che codice sorgente e segreti della pipeline siano esposti.
  4. Verifica dopo la patch: conferma 1.27.1+, conferma la registrazione disattivata o limitata, riesegui la caccia agli hook e ai processi, e solo allora riconnetti a reti non fidate.

Dove la validazione continua cambia l'esito

CVE-2026-60004 è un caso da manuale per trattare l'esposizione come qualcosa che si dimostra, non che si presume. La vulnerabilità esisteva in Gitea dalla 1.17 in poi; ciò che rendeva un dato server sfruttabile era una catena di stato di configurazione — raggiungibilità pubblica, registrazione aperta, diritti di creazione repo, un account di servizio non sandboxato — che nessuna scansione di versione risolve. Quella catena è ciò che il pilastro AI Generative Pentest di Zero Hunt è costruito per percorrere.

Lo swarm a 10 agenti non si ferma a un banner di versione. L'agente Recon trova l'istanza Gitea esposta, gli agenti Web e Credential verificano se la registrazione aperta lascia davvero un account nuovo raggiungere l'accesso in scrittura al repository, e l'agente Exploit genera una catena diffpatch per-target — codice scritto localmente per la tua istanza, non tratto da un PoC pubblico — per confermare se un hook si piazza davvero e cosa può raggiungere l'account di servizio. Ogni passo è validato nell'AI Gym contro il corpus di CVE Vulhub-Bench prima di girare in produzione, e ogni finding è firmato ECDSA perché l'evidenza "abbiamo confermato che era sfruttabile in questa data" regga in un audit o in una richiesta assicurativa. Poiché le campagne sono change-triggered, il giorno in cui uno sviluppatore apre la porta 3000 per mostrare un repo, una validazione completa parte entro l'ora — invece di emergere nel pentest dell'anno prossimo, dopo il miner.

E per la fase di undici secondi sull'host, l'AI Traffic Analysis è la rete di sicurezza che il grafico della CPU non è: il download di un binario specifico per l'architettura e la connessione in uscita verso una mining pool o un C2 sono segnali comportamentali di egress che un modello di deep learning addestrato su miliardi di sequenze PCAP segnala mentre l'attività sta accadendo — non nel digest del mattino, quando gli undici secondi sono ormai passati.

Aggiornare alla 1.27.1 chiude questa porta specifica. Sapere quali dei tuoi server un attaccante potrebbe davvero attraversare — prima che lo faccia — è la disciplina che chiude la prossima.