Suite de scripting servers: Webmin, Usermin, accel-ppp y CUPS

Cuatro sinks de inyección de comandos en una misma familia de scripting servers (Webmin y Usermin comparten el motor miniserv), más el túnel L2TP de accel-ppp y la cadena cups-browsed→foomaticrip de CUPS. Incluye el análisis del bypass de confianza X-SSL-Client-* y su alcance real.

/suite_scripting_servers +

00. Resumen de los Cuatro Sinks

TargetSinkClasePrivilegioAlcance
Webmincpan/install.cgi:45CWE-78rootPost-auth
Userminssh/setup.cgi:9/12CWE-78no-rootPost-auth
accel-pppip-up/down ($6)CWE-78rootL2TP (config)
CUPSfoomaticrip sh -cCWE-78lpLAN

El hilo común es que todos ejecutan un shell con datos que en algún punto provienen de la red o de un campo de configuración sin sanear. Son la clase de bug que el core de cada producto ha ido endureciendo, pero que sobrevive en los scripts admin y los helpers —lo que esta serie llama la superficie que el mantenimiento no mira.

01. 0x00 — Webmin cpan/install.cgi

El módulo de instalación de módulos Perl de Webmin (CPAN) pasa los argumentos del usuario directamente a la apertura de un pipe de comandos:

// cpan/install.cgi:45 — el argumento del usuario entra a un comando open(CMD, "..." . $module . " |") or die(...); // un pipe "|" se ejecuta con /bin/sh -c; $module sin escapar => inyección // Presente sin cambios desde hace muchos años (sink heredado/legacy)

El sink está en el cierre del pipe con |, que el intérprete ejecuta con /bin/sh -c. Un módulo como ;touch /tmp/x;# termina la parte "normal" y ejecuta el comando inyectado. El alcance es post-auth: requiere una sesión válida de Webmin (con los privilegios del administrador, root). No es un bug pre-auth.

02. 0x01 — Usermin ssh/setup.cgi y find.cgi

Usermin comparte el motor miniserv con Webmin pero sirve a usuarios normales, no al admin. Tiene dos sinks de la familia:

// ssh/setup.cgi:9/12 — el tipo ($type) se interpola sin quotemeta $cmd = "..." . $type . " ..."; system($cmd); // $type del usuario, sin escapar // htaccess/find.cgi:9 — open con pipe sobre input open(FIND, "find ..." . $_ . " |") or die(...); // $_ (dato del request) entra a un pipe sh -c sin quotemeta

Ambos son post-auth y, por defecto, corren con el uid del usuario de Usermin (miniserv baja el euid): el RCE llega como un usuario no-root. La inyección es real pero el impacto queda acotado a la cuenta del usuario autenticado salvo que la configuración eleve privilegios.

03. 0x02 — accel-ppp L2TP → scripts ip-up

accel-ppp es un servidor PPP/BGP/L2TP para BRAS y LNS de ISP. En su modo de compatibilidad pppd_compat, ejecuta los scripts ip-up/ip-down con los campos de la sesión sin escapar. El AVP que llega sin sanear es el CALLING_SID (el identificador llamante), que se expande en el script:

// ip-up/ip-down — CALLING_SID ($6) se evalúa sin comillas eval "$6"; // el AVP llamante del túnel L2TP entra a un eval // Origen: el Calling-SID del paquete ICRQ L2TP (UDP 1701), 0-click // con conteño: ;id > /tmp/x;# => ejecución en el script de sesión

Es un camino pre-auth y 0-click por UDP L2TP (puerto 1701), porque el túnel se inicia antes de autenticarse. La ejecución es root (los scripts de ppp corren como root). La limitación real es que requiere la config de compatibilidad pppd_compat con los scripts presentes, que no es la instalación por defecto pura.

04. 0x03 — CUPSF: cups-browsed → foomaticrip

CUPS es el sistema de impresión estándar. El daemon cups-browsed descubre impresoras de red y, al procesar trabajos, deriva el filtro foomaticrip que ejecuta con un shell:

// cups-browsed → foomaticrip → sh -c // El perfil de color (cupsICCProfile) se pasa fuera del allowlist // y se interpola en el comando del filtro: sh -c "... foomatic-rip ..." // perfil no validado => inyección // Origen: perfil manipulado vía descubrimiento cups-browsed // (ya cubierto por la familia CVE-2024-47177 — variante allowlist)

El vector es el descubrimiento de impresoras de red (cups-browsed): un perfil fuera del allowlist del mantenedor se interpola en el comando del filtro. Ejecuta como lp (usuario del sistema de impresión). El alcance es una LAN con impresión en red (cups-browsed activo), no Internet de forma directa.

05. 0x04 — El bypass de confianza X-SSL-Client-*

Webmin/Usermin validan la autenticación por cliente SSL. En despliegues detrás de un reverse proxy, el motor miniserv confía en las cabeceras X-SSL-Client-* que el proxy inyecta para indicar el certificado del cliente:

// miniserv-lib.pl — confianza en las cabeceras SSL del proxy $header{"X-SSL-Client-DN"}, $header{"X-SSL-Client-Cert"} ... // se usan para resolver el usuario por certificado (find_user_by_cert)

La clase de riesgo conceptual es que, si el motor no distingue una cabecera inyectada por el atacante de una inyectada por el proxy, todos los sinks post-auth de la familia podrían volverse pre-auth por composición. Tras validación rigurosa, el alcance real de este bypass en config estándar es limitado:

Nota de honestidad técnica

Con los tres prerequisitos activos, el bypass sí convierte los sinks post-auth en pre-auth. Pero ninguna de esas condiciones es el valor por defecto de la configuración: son cambios que un admin habilita para operar tras un reverse proxy. La lección general de este análisis es verificar siempre el valor real de la configuración por defecto antes de declarar una clase de alcance; "común en producción" no es lo mismo que "config por defecto".

06. 0x05 — Referencias

ReferenciaDescripción
Webmin cpan/install.cgi:45Sink open(CMD,"...|") → sh -c, heredado/multi-año
Usermin ssh/setup.cgi:9/12system() con $type sin quotemeta
Usermin htaccess/find.cgi:9open(FIND,"...|") con input del request
accel-ppp ip-up/downeval de CALLING_SID ($6) sobre AVP L2TP
CUPSF cups-browsedcupsICCProfile → foomaticrip sh -c (variante CVE-2024-47177)
miniserv-lib.plConfianza en cabeceras X-SSL-Client-* (bypass condicional)
CWE-78Improper Neutralization in OS Command
Divulgación responsable

Estos sinks se publican con fines de hardening y concienciación. La mitigación común a toda la familia es simple y robusta: nunca interpolar datos de red o de request en un shell — usar quotemeta/escapado en Perl para la familia miniserv, no derivar comandos por eval en los scripts ppp, y validar los perfiles contra el allowlist en CUPS. Cada caso fue comunicado al mantenedor como parte del proceso responsable.

← Volver al índice de la serie