FlawOpen

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

● Correspondance FlawOpen : CWE-601 · CVSS v4 8.6[1] · Haute
Recherche · FlawOpen

Défaut de redirection de la connexion native de Hermes Agent

Tenable a publié une faille grave de validation des redirections dans la connexion native de Hermes Agent. Les versions antérieures à 0.21.6 sont concernées. Le correctif de la version 0.21.6 a été livré pour Docker/Hermes Cloud et les installations CLI/git ; la version 0.21.7, publiée le 11 octobre, indique aussi la disponibilité pour l’application Desktop, Microsoft Store/MSIX, l’installateur, Docker et Hermes Cloud. Les notes consultées ne mentionnent pas Termux ; vérifiez ce canal séparément. Tenable lui attribue le niveau High (CVSS 4.0 8.6) ; aucun CVE n’est assigné. CWE-601 est une classification éditoriale de l’effet de redirection.[1], [2], [5]

💡 Explication en Langage Simple (ELI5)

Imaginez un accueil qui vérifie une adresse sur une carte, recopie l’adresse reçue sans la modifier et la remet à un coursier qui utilise une autre carte. Le coursier peut aller ailleurs que l’endroit autorisé. Hermes vérifiait le callback avec le parseur Python, puis transmettait la chaîne brute au navigateur, qui peut l’interpréter différemment.[1]

Concepts Clés et Termes

Native-app callback
Une adresse web locale temporaire utilisée par une application de bureau pour recevoir le résultat d’une connexion lancée dans le navigateur.[1], [4]
URL parser differential
Deux parseurs d’URL donnent un sens différent à la même entrée ; valider une interprétation puis en utiliser une autre rompt la frontière de sécurité.[1], [3]
PKCE
Proof Key for Code Exchange lie un code d’autorisation à un vérificateur secret conservé par l’application ayant lancé la connexion.[1], [4]
Loopback address
Une adresse IP qui renvoie vers le même appareil, comme 127.0.0.1 ou ::1. Le correctif accepte des littéraux IP canoniques, pas des hôtes distants arbitraires.[3], [4]

Correspondances de référentiels

  • Correspondance conceptuelle de FlawOpen avec MITRE CWE ; aucun fournisseur ni enregistrement CVE n’a attribué cet identifiant de faiblesse à cette démonstration de recherche. CWE-601

Déroulement de l'Attaque Étape par Étape

Étape 1

Une route publique de connexion native reçoit un callback

Le point de terminaison d’autorisation reçoit un URI de redirection et un défi PKCE avant l’authentification de l’utilisateur.[1]

Étape 2

Le serveur et le navigateur interprètent différemment le callback

Le validateur vulnérable examinait le nom d’hôte analysé par Python, mais conservait la chaîne d’origine. Une syntaxe ambiguë peut conduire le navigateur vers une autre autorité.[1], [3]

Étape 3

Une connexion terminée peut exposer le code

Après la connexion normale de la victime, le navigateur peut envoyer le code à l’autorité qu’il a interprétée. Un attaquant ayant choisi le défi PKCE correspondant peut tenter de l’échanger. La connexion de la victime est nécessaire : ce n’est pas une prise de compte sans interaction.[1]

Étape 4

Canonicaliser et mettre à jour la version distribuée

Le correctif rejette les syntaxes ambiguës et les autorités non canoniques, renvoie l’URI canonique et stocke cette valeur. Passez à la version 0.21.7 ou ultérieure par un canal pris en charge ; vérifiez Termux séparément, car les notes consultées de la version 0.21.7 ne le mentionnent pas.[2], [5], [3]

Code Source : Vulnérable vs Sécurisé

Il s’agit d’un modèle illustratif de la frontière, pas du code complet de Hermes Agent ni d’un exploit. L’exemple vulnérable résume le schéma vérifier l’hôte analysé/utiliser l’entrée brute. L’exemple corrigé montre la propriété du correctif fusionné : rejeter les entrées ambiguës, n’autoriser que des autorités loopback canoniques, renvoyer l’URI canonique puis persister/utiliser cette valeur. Le PR lié contient l’implémentation et les tests réels.[1], [3]

IMPLÉMENTATION VULNÉRABLE
from urllib.parse import urlparse

def validate_callback(raw):
    parsed = urlparse(raw)
    # Le flux d’origine vérifiait l’hôte analysé, puis conservait l’URL brute.
    if parsed.scheme == "http" and parsed.hostname in {"127.0.0.1", "::1"}:
        return raw
    raise ValueError("callback must be loopback")
PATCH SÉCURISÉ ET ROBUSTE
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 "/"))

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources

← AI Security Directory →