Neutron, nuestro motor de IA, obtuvo un 96.75% en el benchmark CyberGym de UC Berkeley. Más información

Seguridad

Seguridad

Análisis en profundidad: vulnerabilidad de XSS almacenado en el plugin LiteSpeed Cache para WordPress (CVE-2024-47374)

Un análisis detallado de la vulnerabilidad CVE-2024-47374 que afecta al plugin LiteSpeed Cache para WordPress, su impacto y un desglose técnico de nuestro método de detección.

Introducción

CVE-2024-47374 es una vulnerabilidad de severidad alta que afecta al plugin LiteSpeed Cache para WordPress, un plugin integral de aceleración de sitios que ayuda a mejorar el rendimiento de un sitio web almacenando en caché los datos de acceso frecuente. Esta vulnerabilidad de cross-site scripting (XSS) almacenado afecta a todas las versiones del plugin hasta la 6.5.0.2 incluida. El fallo, si se explota, podría permitir a actores maliciosos ejecutar código JavaScript arbitrario.

Detalles técnicos

La vulnerabilidad se origina en la forma en que el plugin gestiona la cabecera HTTP X-LSCACHE-VARY-VALUE. El valor de esta cabecera se analiza sin un saneamiento ni un escape de salida adecuados, lo que permite la inyección de código JavaScript. El fallo está relacionado específicamente con las funciones de optimización de CSS del plugin.

Componentes vulnerables

Las funciones de generación de CCSS y UCSS, _ccss() y _load(), aceptan los parámetros y las cabeceras HTTP necesarios para generar y almacenar los datos. La cola se crea con las siguientes líneas de código:

  • _ccss() en litespeed-cache/src/css.cls.php
private function _ccss()
{
    global $wp;
    $request_url = home_url($wp->request);

    $filepath_prefix = $this->_build_filepath_prefix('ccss');
    $url_tag = $this->_gen_ccss_file_tag($request_url);
    $vary = $this->cls('Vary')->finalize_full_varies();

    ===============  Skip ================

    // Store it to prepare for cron
    Core::comment('QUIC.cloud CCSS in queue');
    $this->_queue = $this->load_queue('ccss');

    if (count($this->_queue) > 500) {
        self::debug('CCSS Queue is full - 500');
        return null;
    }

    $queue_k = (strlen($vary) > 32 ? md5($vary) : $vary) . ' ' . $url_tag;
    $this->_queue[$queue_k] = array(
        'url' => apply_filters('litespeed_ccss_url', $request_url),
        'user_agent' => substr($ua, 0, 200),
        'is_mobile' => $this->_separate_mobile(),
        'is_webp' => $this->cls('Media')->webp_support() ? 1 : 0,
        'uid' => $uid,
        'vary' => $vary,
        'url_tag' => $url_tag,
    ); // Current UA will be used to request
    $this->save_queue('ccss', $this->_queue);
  • load() en litespeed-cache/src/ucss.cls.php
public function load($request_url, $dry_run = false)
{
    ===============  Skip ================

    $vary = $this->cls('Vary')->finalize_full_varies();

    ===============  Skip ================

    // Store it for cron
    $this->_queue = $this->load_queue('ucss');

    if (count($this->_queue) > 500) {
        self::debug('UCSS Queue is full - 500');
        return false;
    }

    $queue_k = (strlen($vary) > 32 ? md5($vary) : $vary) . ' ' . $url_tag;
    $this->_queue[$queue_k] = array(
        'url' => apply_filters('litespeed_ucss_url', $request_url),
        'user_agent' => substr($ua, 0, 200),
        'is_mobile' => $this->_separate_mobile(),
        'is_webp' => $this->cls('Media')->webp_support() ? 1 : 0,
        'uid' => $uid,
        'vary' => $vary,
        'url_tag' => $url_tag,
    ); // Current UA will be used to request
    $this->save_queue('ucss', $this->_queue);

Observe que la variable $vary se almacena mediante la función $this->save_queue. La variable $vary la construye la función finalize_full_varies, que incluye datos de la cabecera HTTP X-LSCACHE-VARY-VALUE.

public function finalize_full_varies()
{
    $vary = $this->_finalize_curr_vary_cookies(true);
    $vary .= $this->finalize_default_vary(get_current_user_id());
    $vary .= $this->get_env_vary();
    return $vary;
}

/**
    * Get request environment Vary
    *
    * @since  4.0
    */
public function get_env_vary()
{
    $env_vary = isset($_SERVER['LSCACHE_VARY_VALUE']) ? $_SERVER['LSCACHE_VARY_VALUE'] : false;
    if (!$env_vary) {
        $env_vary = isset($_SERVER['HTTP_X_LSCACHE_VARY_VALUE']) ? $_SERVER['HTTP_X_LSCACHE_VARY_VALUE'] : false;
    }
    return $env_vary;
}

Al mostrar la cola en el panel de administración, la información de Vary Group (derivada de $vary) se imprime directamente en la página de administración sin ningún saneamiento ni escape.

Según el sitio web del proveedor del plugin, la funcionalidad Vary Group fusiona las ideas de cache varies y user roles. La vulnerabilidad surge porque los usuarios pueden proporcionar el Vary Group mediante una cabecera HTTP, que luego se muestra en la página de administración sin un saneamiento adecuado.

Este es el fragmento de código relevante de litespeed-cache/tpl/page_optm/settings_css.tpl.php:

<?php if (!empty($ucss_queue)) : ?>
    <div class="litespeed-callout notice notice-warning inline">
        <h4>
            <?php echo sprintf(__('URL list in %s queue waiting for cron', 'litespeed-cache'), 'UCSS'); ?> ( <?php echo count($ucss_queue); ?> )
            <a href="<?php echo Utility::build_url(Router::ACTION_UCSS, UCSS::TYPE_CLEAR_Q); ?>" class="button litespeed-btn-warning litespeed-right">Clear</a>
        </h4>
        <p>
            <?php $i = 0;
            foreach ($ucss_queue as $k => $v) : ?>
                <?php if ($i++ > 20) : ?>
                    <?php echo '...'; ?>
                    <?php break; ?>
                <?php endif; ?>
                <?php if (!is_array($v)) continue; ?>
                <?php if (!empty($v['_status'])) : ?><span class="litespeed-success"><?php endif; ?>
                    <?php echo esc_html($v['url']); ?>
                    <?php if (!empty($v['_status'])) : ?></span><?php endif; ?>
                <?php if ($pos = strpos($k, ' ')) echo ' (' . __('Vary Group', 'litespeed-cache') . ':' . substr($k, 0, $pos) . ')'; ?>
                <?php if ($v['is_mobile']) echo ' <span data-balloon-pos="up" aria-label="mobile">📱</span>'; ?>
                <?php if (!empty($v['is_webp'])) echo ' WebP'; ?>
                <br />
            <?php endforeach; ?>
        </p>
    </div>

Esta falta de saneamiento permite a un atacante inyectar scripts maliciosos en el panel de administración.

Requisitos para la explotación

Para que el exploit tenga éxito, deben estar habilitados dos ajustes específicos de Page Optimization:

  1. CSS Combine: ON
  2. Generate UCSS: ON

Impacto de CVE-2024-47374

El impacto de la vulnerabilidad es significativo debido a varios factores:

Severidad alta: con una puntuación CVSS de 7.2, esta vulnerabilidad se clasifica como de severidad alta.

Amplia superficie de ataque: LiteSpeed Cache es un plugin de WordPress muy popular, instalado en más de 6 millones de sitios web de WordPress.

Toma de control de la cuenta de administrador: si se secuestra la sesión de un administrador, el atacante podría obtener el control total del sitio de WordPress. Este es el escenario más grave, ya que permite:

  • Instalar plugins o temas maliciosos
  • Modificar el contenido del sitio
  • Acceder a datos sensibles y exfiltrarlos
  • Utilizar el sitio comprometido como pivote para nuevos ataques

Secuestro de sesiones: la vulnerabilidad podría utilizarse para secuestrar sesiones de usuarios autenticados, lo que permitiría a los atacantes realizar acciones en nombre de la víctima.

La vulnerabilidad de XSS almacenado en LiteSpeed Cache permite a los atacantes inyectar y ejecutar código JavaScript arbitrario. Nuestro script de detección se centra en identificar las instancias vulnerables comprobando la versión del plugin. Veámoslo:

VULNERABLE_VERSIONS = [
    r"(?P<major_0_to_5>[0-5])\.(?P<minor>[0-9]+)(\.(?P<patch>[0-9]+))?",
    r"(?P<major_6_0_to_4>6)\.(?P<minor_0_to_4>[0-4])(\.(?P<patch>[0-9]+))?",
    r"(?P<major_6_5>6)\.5\.0(\.(?P<patch_0_to_2>[0-2]))$",
]

class CVE202447374Exploit(webexploit.WebExploit):
    accept_request = definitions.Request(
        method="GET", path="/wp-content/plugins/litespeed-cache/readme.txt"
    )
    check_request = definitions.Request(
        method="GET", path="/wp-content/plugins/litespeed-cache/readme.txt"
    )
    accept_pattern = [re.compile("LiteSpeed Cache")]
    match_pattern = [
        re.compile(f"Stable tag: {version}") for version in VULNERABLE_VERSIONS
    ]

Análisis de la identificación de versiones

La lista VULNERABLE_VERSIONS contiene tres expresiones regulares, cada una diseñada para coincidir con rangos específicos de versiones vulnerables:

r"(?P<major_0_to_5>[0-5])\.(?P<minor>[0-9]+)(\.(?P<patch>[0-9]+))?":

  • Esta expresión regular coincide con todas las versiones de 0.0.0 a 5.x.x.
  • (?P<major_0_to_5>[0-5]) captura el número de versión principal de 0 a 5.
  • \.(?P<minor>[0-9]+) coincide con el número de versión secundaria.
  • (\.(?P<patch>[0-9]+))? coincide opcionalmente con una versión de parche, ya que algunas versiones podrían no tenerla.

r"(?P<major_6_0_to_4>6)\.(?P<minor_0_to_4>[0-4])(\.(?P<patch>[0-9]+))?":

  • Esta expresión regular cubre las versiones de 6.0.x a 6.4.x.
  • (?P<major_6_0_to_4>6) coincide específicamente con la versión principal 6.
  • \.(?P<minor_0_to_4>[0-4]) coincide con las versiones secundarias de 0 a 4.
  • La versión de parche vuelve a ser opcional.

r"(?P<major_6_5>6)\.5\.0(\.(?P<patch_0_to_2>[0-2]))$":

  • Esta expresión regular es muy específica y solo apunta a las versiones 6.5.0, 6.5.0.1 y 6.5.0.2.
  • (?P<major_6_5>6)\.5\.0 coincide exactamente con la versión 6.5.0.
  • (\.(?P<patch_0_to_2>[0-2]))$ garantiza que solo coincidan las versiones de parche 0, 1 o 2 y nada más.

Esta coincidencia precisa de versiones es crucial porque la vulnerabilidad afecta a todas las versiones hasta la 6.5.0.2 incluida. Gracias a estas expresiones regulares cuidadosamente elaboradas, el script puede identificar con exactitud las versiones vulnerables y excluir la versión corregida 6.5.1 y cualquier versión futura.

Identificación del objetivo y comprobación de la vulnerabilidad

El script utiliza dos componentes clave para identificar el objetivo y comprobar la vulnerabilidad:

  1. Identificación del objetivo: python accept_pattern = [re.compile("LiteSpeed Cache")] Este patrón comprueba la presencia de LiteSpeed Cache en el archivo readme.txt. Es una forma sencilla pero eficaz de confirmar que el plugin LiteSpeed Cache está instalado en el sitio de WordPress objetivo.

  2. Comprobación de la vulnerabilidad: python match_pattern = [ re.compile(f"Stable tag: {version}") for version in VULNERABLE_VERSIONS ] Este enfoque crea una lista de patrones de expresión regular, cada uno de los cuales busca la línea Stable tag: en el archivo readme.txt con un número de versión que coincida con una de las versiones vulnerables. Al iterar por estos patrones, el script puede determinar rápidamente si la versión instalada es vulnerable.

Estrategia de solicitud del archivo

El script apunta al archivo readme.txt ubicado en /wp-content/plugins/litespeed-cache/readme.txt. Este archivo se elige porque:

  1. Normalmente es de acceso público en las instalaciones de WordPress.
  2. Contiene la información de versión en un formato estandarizado (Stable tag: X.X.X).
  3. Acceder a este archivo no es intrusivo y no supone ningún riesgo de modificar o dañar el sistema objetivo.

Al combinar estos elementos, el script ofrece un método robusto, preciso y seguro para detectar las instancias de LiteSpeed Cache vulnerables a CVE-2024-47374.

Mitigación

Para mitigar esta vulnerabilidad, los administradores de sitios web deben actualizar el plugin LiteSpeed Cache a la versión 6.5.1 o posterior. Esta versión se publicó el 25 de septiembre de 2024 y corrige la vulnerabilidad CVE-2024-47374.

Pruebas de la vulnerabilidad con OXO

Si le preocupa que su instancia pueda ser vulnerable, siga estos pasos para ejecutar una prueba con la herramienta OXO:

Instale OXO mediante pip:

pip install -U ostorlab

Instale el agente asteroid desde la tienda de agentes de OXO:

oxo agent install agent/ostorlab/asteroid

Ejecute el escaneo con el agente asteroid mediante el siguiente comando:

oxo scan run --agent agent/ostorlab/asteroid link --url <target-URL> --method GET

Etiquetas:

xss, kev, security, wordpress