FlawOpen

flawopen.com/AI Security/hermes-agent-native-signin-redirect-parser-confusion

● Mapeamento da FlawOpen: CWE-601 · CVSS v4 8.6[1] · Alta
Pesquisa · FlawOpen

Falha no redirecionamento do login nativo do Hermes Agent

A Tenable divulgou uma falha grave na validação de redirecionamento do login nativo do Hermes Agent. Versões anteriores à 0.21.6 são afetadas. A correção da v0.21.6 chegou ao Docker/Hermes Cloud e às instalações CLI/git; a v0.21.7, lançada em 11 de outubro, também lista o aplicativo Desktop, Microsoft Store/MSIX, instalador, Docker e Hermes Cloud. As notas de versão consultadas não mencionam Termux; verifique esse canal separadamente. A Tenable classifica a falha como High (CVSS 4.0 8.6); não há CVE atribuído. CWE-601 é uma classificação editorial do resultado de redirecionamento.[1], [2], [5]

💡 Explicação em Linguagem Simples (ELI5)

Imagine que uma recepção confere um endereço em um mapa, anota sem alterações o texto recebido e o entrega a um mensageiro que usa outro mapa. O mensageiro pode chegar a um lugar que a recepção nunca autorizou. O Hermes conferia o callback com o parser do Python, mas entregava o texto original ao navegador, que pode interpretá-lo de outra forma.[1]

Conceitos Centrais e Termos

Native-app callback
Um endereço web local temporário usado por um aplicativo desktop para receber o resultado de um login iniciado no navegador.[1], [4]
URL parser differential
Dois parsers de URL atribuem significados diferentes à mesma entrada; validar uma interpretação e depois usar outra rompe a fronteira de segurança.[1], [3]
PKCE
Proof Key for Code Exchange vincula um código de autorização a um verificador secreto mantido pelo aplicativo que iniciou o login.[1], [4]
Loopback address
Um endereço IP que retorna ao mesmo dispositivo, como 127.0.0.1 ou ::1. A correção do Hermes aceita literais IP canônicos, não hosts remotos arbitrários.[3], [4]

Mapeamentos de frameworks

  • Mapeamento conceitual da FlawOpen para o MITRE CWE; nenhum fornecedor ou registro CVE atribuiu este identificador de fraqueza a esta demonstração de pesquisa. CWE-601

Fluxo de Ataque Passo a Paso

Passo 1

Uma rota pública de login nativo recebe um callback

O endpoint de autorização do login nativo recebe um URI de redirecionamento e um desafio PKCE antes da autenticação do usuário.[1]

Passo 2

Servidor e navegador interpretam o callback de formas diferentes

O validador vulnerável examinava o hostname analisado pelo Python, mas preservava a string original. Sintaxe ambígua pode fazer o navegador navegar para outra autoridade.[1], [3]

Passo 3

Um login concluído pode expor o código de autorização

Depois que a vítima conclui o login normal, o navegador pode enviar o código à autoridade que interpretou. Um atacante que escolheu o desafio PKCE correspondente pode tentar resgatá-lo. A falha exige o login da vítima; não é tomada de conta sem interação.[1]

Passo 4

Canonicalize e atualize a instalação distribuída

A correção rejeita sintaxe ambígua e autoridades não canônicas, retorna o URI canônico e armazena esse valor. Atualize para a v0.21.7 ou posterior por um canal compatível; verifique Termux separadamente, pois as notas consultadas da v0.21.7 não o mencionam.[2], [5], [3]

Código-Fonte: Vulnerável vs. Seguro

Este é um modelo ilustrativo da fronteira, não o código completo do Hermes Agent nem um exploit. O exemplo vulnerável resume o padrão validar-host-analisado/usar-entrada-original. O exemplo corrigido demonstra a propriedade da correção mesclada: rejeitar entradas ambíguas, permitir apenas autoridades loopback canônicas, retornar o URI canônico e persistir/usar esse valor. Consulte o PR vinculado para a implementação e os testes reais.[1], [3]

IMPLEMENTAÇÃO VULNERÁVEL
from urllib.parse import urlparse

def validate_callback(raw):
    parsed = urlparse(raw)
    # O fluxo original validava o host analisado, mas depois preservava a URL bruta.
    if parsed.scheme == "http" and parsed.hostname in {"127.0.0.1", "::1"}:
        return raw
    raise ValueError("callback must be loopback")
PATCH SEGURO E ROBUSTO
from urllib.parse import urlparse, urlunparse

LOOPBACK_HOSTS = {"127.0.0.1": "127.0.0.1", "::1": "[::1]"}

def validate_callback(raw):
    if not raw.isascii() or "\\" in raw or any(ord(c) <= 0x20 or ord(c) == 0x7f for c in raw):
        raise ValueError("callback must use canonical loopback syntax")
    try:
        parsed = urlparse(raw)
        host, port = parsed.hostname, parsed.port
    except ValueError as exc:
        raise ValueError("invalid callback") from exc
    if parsed.scheme != "http" or "#" in raw or parsed.username is not None or parsed.password is not None:
        raise ValueError("callback must be a plain loopback URL")
    authority_host = LOOPBACK_HOSTS.get(host)
    if authority_host is None:
        raise ValueError("callback host must be a loopback IP literal")
    authority = authority_host + (f":{port}" if port is not None else "")
    if parsed.netloc != authority:
        raise ValueError("callback authority is not canonical")
    return urlunparse(parsed._replace(path=parsed.path or "/"))

Lista de Verificação de Segurança para Engenharia

Fontes

← AI Security Directory →