Description
The ElementsKit Elementor addons plugin for WordPress is vulnerable to Local File Inclusion in all versions up to, and including, 3.0.6 via the render_raw function. This makes it possible for authenticated attackers, with contributor-level access and above, to include and execute arbitrary files on the server, allowing the execution of any PHP code in those files. This can be used to bypass access controls, obtain sensitive data, or achieve code execution in cases where images and other “safe” file types can be uploaded and included.
Code
Vulnerable code: https://plugins.trac.wordpress.org/browser/elementskit-lite/trunk/widgets/testimonial/testimonial.php?rev=3050248
Analysis
Right at the bottom on line 2458, we see
$style being concatenated to a file path directly. We can add ../ to $style to obtain path traversal2455 $style = isset($ekit_testimonial_style) ? sanitize_text_field($ekit_testimonial_style) : 'default'; 2456 2457 if (is_array($testimonials) && !empty($testimonials)) { 2458 require Handler::get_dir() . 'style/' . $style . '.php'; 2459 } 2460 }
The Fix
$style is now checked to see if it’s one of the existing styles before appending to the file path2456 $styles = [ 2457 'style1', 2458 'style2', 2459 'style3', 2460 'style4', 2461 'style5', 2462 'style6', 2463 ]; 2464 2465 if (in_array($style, $styles) && is_array($testimonials) && !empty($testimonials)) { 2466 require Handler::get_dir() . 'style/' . $style . '.php'; 2467 }
Recreation
- Install WordPress locally
- Download the vulnerable plugin and install it
- Add the testimonial widget to the page
- Edit the style by toggling
Use Fixed Height
- Intercept the request in BurpSuite and we see the data for
ekit_testimonial_styleis being sent over with a valuestyle1
- We create a dummy file in the root folder called
a.phpwith the following contents
<?php echo system('whoami') ?>
- Now we modify the value of
ekit_testimonial_styleto../../../a.phpand see the PHP file being executed, and returns the host name of our machine
Fixed Version
- After we update the plugin to it’s latest version and send the traffic again, there is no more directory traversal because
../../../a.phpis not in the array
Also, they kinda fixed type juggling in PHP 8, so
in_array is pretty safe now!