10,000 WordPress Sites Affected by Critical Vulnerabilities in HT Contact Form WordPress Plugin


📢 Calling all Vulnerability Researchers and Bug Bounty Hunters! 📢

🌞 Spring into Summer with Wordfence! Now through August 4, 2025, earn 2X bounty rewards for all in-scope submissions from our ‘High Threat’ list in software with fewer than 5 million active installs. Bounties up to $31,200 per vulnerability. Submit bold. Earn big!


On June 24th, 2025, we received a submission for an Arbitrary File Upload and an Arbitrary File Deletion vulnerability in HT Contact Form, a WordPress plugin with more than 10,000 active installations. The arbitrary file upload vulnerability can be used by unauthenticated attackers to upload arbitrary files to a vulnerable site and achieve remote code execution, which is typically leveraged for a complete site takeover. The arbitrary file deletion vulnerability can be used by unauthenticated attackers to delete arbitrary files, including the wp-config.php file, which can also make a site takeover possible.

On July 4th, 2025, we also received a submission for an Arbitrary File Move vulnerability in HT Contact Form. This vulnerability can be used by unauthenticated attackers to move arbitrary files, including the wp-config.php file, which can also make a site takeover possible.

All Wordfence Premium, Wordfence Care, and Wordfence Response customers, as well as those using the free version of our plugin, are protected against any exploits targeting these vulnerabilities by the Wordfence firewall’s built-in Malicious File Upload and Directory Traversal protections.

Props to vgo0 and Phat RiO – BlueRock, who discovered and responsibly reported these vulnerabilities through the Wordfence Bug Bounty Program. These researchers earned bounties of $1,431.00, $991.00 and $675.00 for their discoveries. Our mission is to secure WordPress through defense in depth, which is why we are investing in quality vulnerability research and collaborating with researchers of this caliber through our Bug Bounty Program. We are committed to making the WordPress ecosystem more secure through the detection and prevention of vulnerabilities, which is a critical element to the multi-layered approach to security.

We contacted the HasTech IT team on July 8, 2025, and they registered on our Wordfence Vulnerability Management Portal for WordPress vendors on July 9, 2025. After receiving the full disclosure details instantly through the portal, the developer released the patch on July 13, 2025. We would like to commend the HasTech IT team for their prompt response and timely patch.

We urge users to update their sites with the latest patched version of HT Contact Form, version 2.2.2 at the time of this writing, as soon as possible.

Vulnerability Summary from Wordfence Intelligence

CVSS Rating
9.8 (Critical)
Affected Versions
<= 2.2.1
Patched Version
2.2.2
Bounty
$1,431.00

The HT Contact Form Widget For Elementor Page Builder & Gutenberg Blocks & Form Builder plugin for WordPress is vulnerable to arbitrary file uploads due to missing file type validation in the temp_file_upload() function in all versions up to, and including, 2.2.1. This makes it possible for unauthenticated attackers to upload arbitrary files on the affected site’s server which may make remote code execution possible.

CVSS Rating
9.1 (Critical)
Affected Versions
<= 2.2.1
Patched Version
2.2.2
Bounty
$991.00

The HT Contact Form Widget For Elementor Page Builder & Gutenberg Blocks & Form Builder plugin for WordPress is vulnerable to arbitrary file deletion due to insufficient file path validation in the temp_file_delete() function in all versions up to, and including, 2.2.1. This makes it possible for unauthenticated attackers to delete arbitrary files on the server, which can easily lead to remote code execution when the right file is deleted (such as wp-config.php).

CVSS Rating
9.1 (Critical)
Affected Versions
<= 2.2.1
Patched Version
2.2.2
Bounty
$675.00

The HT Contact Form Widget For Elementor Page Builder & Gutenberg Blocks & Form Builder plugin for WordPress is vulnerable to arbitrary file moving due to insufficient file path validation in the handle_files_upload() function in all versions up to, and including, 2.2.1. This makes it possible for unauthenticated attackers to move arbitrary files on the server, which can easily lead to remote code execution when the right file is moved (such as wp-config.php).

Technical Analysis : Arbitrary File Upload

Examining the code reveals that the plugin uses the temp_file_upload() function in the Ajax class to handle temporary file upload via AJAX.

public function temp_file_upload() {
    check_ajax_referer('ht_form_ajax_nonce', '_wpnonce');

    $file = $_FILES['ht_form_file'];

    // Check if file is present
    if (!isset($file)) {
        wp_send_json_error('No file uploaded.');
    }
    return $this->file_manager->temp_file_upload($file);
}

This function invokes the temp_file_upload() function in the FileManager class, which uploads the file to the temp folder using the move_uploaded_file() function.

public function temp_file_upload($file) {
    $destination = "{$this->dir}/temp";
    $this->maybe_create_directories($destination);

    // Validate file
    $validation = $this->validate($file);
    if (!$validation['valid']) {
        wp_send_json_error($validation['message']);
        return;
    }

    // Process the file
    $filename = $this->process_filename($file['name']);
    $file_path = "{$destination}/$filename";

    // Check if directory is writable
    if (!is_writable($destination)) {
        wp_send_json_error("Directory is not writable: {$destination}");
        return;
    }

    // Move file to temporary directory
    if (move_uploaded_file($file['tmp_name'], $file_path)) {
        wp_send_json_success([
            'file_id' => $filename,
            'file_name' => $file['name'],
            'file_size' => $file['size']
        ]);
        return;
    } else {
        $upload_error = error_get_last();
        wp_send_json_error('Failed to save uploaded file. Error: ' . ($upload_error ? $upload_error['message'] : 'Unknown error'));
        return;
    }
}

In addition, the function does not include any file type or extension checks in the vulnerable version. This means that not only safe file types can be uploaded, but it is also possible to upload .php files. The file is uploaded to the WordPress uploads folder, which is publicly accessible by default. This makes it possible for unauthenticated attackers to upload arbitrary malicious PHP code and then access the file to trigger remote code execution on the server.

As with all arbitrary file upload vulnerabilities, this can lead to complete site compromise through the use of webshells and other techniques.

The Patch

The vendor patched this issue by adding a file type check using the wp_check_filetype() function in the temp_file_upload() function. This ensures that only safe file types can be uploaded.

public function temp_file_upload($file) {
    $destination = "{$this->dir}/temp";
    $this->maybe_create_directories($destination);

    // Validate file
    $validation = $this->validate($file);
    if (!$validation['valid']) {
        wp_send_json_error($validation['message']);
        return;
    }

    // Process the file
    $filename = $this->process_filename($file['name']);
    $file_path = "{$destination}/$filename";

    // File type validation check
    $validate = wp_check_filetype( $filename );
    if ($validate['type'] === false) {
        wp_send_json_error('Invalid file type.');
        return;
    }

    // Check if directory is writable
    if (!is_writable($destination)) {
        wp_send_json_error("Directory is not writable: {$destination}");
        return;
    }

    // Move file to temporary directory
    if (move_uploaded_file($file['tmp_name'], $file_path)) {

Technical Analysis : Arbitrary File Deletion

Examining the code reveals that the plugin uses the temp_file_delete() function in the Ajax class to delete temporary files via AJAX.

public function temp_file_delete() {
    check_ajax_referer('ht_form_ajax_nonce', '_wpnonce');

    $file_id = isset($_POST['ht_form_file_id']) ? sanitize_text_field(wp_unslash($_POST['ht_form_file_id'])) : '';
    if (!$file_id) {
        wp_send_json_error('No file ID provided');
    }
    if ($this->file_manager->temp_file_delete($file_id)) {
        wp_send_json_success();
    } else {
        wp_send_json_error('Failed to delete file');
    }
}

This function invokes the temp_file_delete() function in the FileManager class, which deletes temporary files from the temp folder.

public function temp_file_delete($file_id) {
    $file = "{$this->dir}/temp/$file_id";
    if (file_exists($file) && is_writable($file)) {
        @unlink($file);
        return true;
    }
    return false;
}

Unfortunately, the file_id parameter is not properly validated so arbitrary paths can be supplied.

This makes it possible for unauthenticated attackers to delete any arbitrary file on the server, including the site’s wp-config.php file. Deleting the wp-config.php forces the site into a setup state, allowing an attacker to take control by redirecting it to a database under their control. This ultimately provides access to the site’s server where further infection can take place.

The Patch

The vendor patched this issue by applying the sanitize_file_name() function to the ht_form_file_id parameter in the temp_file_delete() function.

public function temp_file_delete() {
    check_ajax_referer('ht_form_ajax_nonce', '_wpnonce');

    $file_id = isset($_POST['ht_form_file_id']) ? sanitize_file_name(wp_unslash($_POST['ht_form_file_id'])) : '';
    if (!$file_id) {
        wp_send_json_error('No file ID provided');
    }
    if ($this->file_manager->temp_file_delete($file_id)) {
        wp_send_json_success();
    } else {
        wp_send_json_error('Failed to delete file');
    }
}

Technical Analysis : Arbitrary File Move

Examining the code reveals that the plugin uses the upload_file() function in the Submission class to handle file uploads for form submissions.

public function handle_files_upload($form_data, $form) {
    foreach ($form['fields'] as $field) {
        if ($field['type'] === 'file_upload') {
            $destination = $field['settings']['upload_location'] ?? 'ht_form_default';
            $files = $form_data[$field['settings']['name_attribute']];
            if (!empty($files)) {
                foreach ($files as $key => $file) {
                    $form_data[$field['settings']['name_attribute']][$key] = $this->upload_file($file, $destination);
                }
            }
        }
    }
    return $form_data;
}
public function upload_file($file_name, $destination) {
    $upload_dir = wp_upload_dir();
    $temp_file = $upload_dir['basedir'] . '/ht_form/temp/' . $file_name;
    if($destination === 'media_library') {
        // === ATTACH TO MEDIA LIBRARY ===
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/media.php';
        require_once ABSPATH . 'wp-admin/includes/image.php';

        $file = [
            'name' => basename($file_name),
            'tmp_name' => $temp_file,
            'type' => mime_content_type($temp_file),
            'error' => 0,
            'size' => filesize($temp_file),
        ];
        $attachment_id = media_handle_sideload($file, 0);
        if(is_wp_error($attachment_id)) {
            return $attachment_id->get_error_message();
        } else {
            @unlink($temp_file);
            return wp_get_attachment_url($attachment_id);
        }
    } elseif($destination === 'ht_form_default') {
        $destination = $upload_dir['basedir'] . '/ht_form';
        if (!file_exists($destination)) {
            wp_mkdir_p($destination);
        }
        $file_name = wp_unique_filename($destination, $file_name);
        $file_path = "$destination/$file_name";
        if (rename($temp_file, $file_path)) {

Unfortunately, the file name parameter is not properly validated so arbitrary paths can be supplied. This file data is then passed to the rename() function which moves the source file into the uploads directory.

This means that attackers can specify any file on the server to be moved, which effectively results in its deletion. This makes it possible for unauthenticated attackers to move arbitrary files on the server, including the site’s wp-config.php file. Moving wp-config.php forces the site into a setup state, allowing an attacker to initiate a site takeover by connecting it to a database under their control.

The Patch

The vendor patched this issue by applying the sanitize_file_name() function to the file variable in the handle_files_upload() function.

public function handle_files_upload($form_data, $form) {
    foreach ($form['fields'] as $field) {
        if ($field['type'] === 'file_upload') {
            $destination = $field['settings']['upload_location'] ?? 'ht_form_default';
            $files = $form_data[$field['settings']['name_attribute']];
            if (!empty($files)) {
                foreach ($files as $key => $file) {
                    $file = sanitize_file_name($file);
                    $form_data[$field['settings']['name_attribute']][$key] = $this->upload_file($file, $destination);
                }
            }
        }
    }
    return $form_data;
}

Disclosure Timeline

June 24, 2025 – We received submissions for both an Arbitrary File Upload vulnerability and an Arbitrary File Deletion vulnerability in HT Contact Form via the Wordfence Bug Bounty Program.
July 4, 2025 – We received the submission for the Arbitrary File Move vulnerability in HT Contact Form via the Wordfence Bug Bounty Program.
July 7, 2025 – We validated the reports and confirmed the proof-of-concept exploits.
July 8, 2025 – We initiated outreach to the vendor with the security contact information on file, letting them know we had a new vulnerability to disclose and offering them access to the Wordfence Vulnerability Management Portal to manage the disclosure.
July 9, 2025 – The vendor registered on our Wordfence Vulnerability Management Portal for WordPress vendors to manage their reports.
July 9, 2025 – The full disclosure details were sent instantly to the vendor upon registering and verifying ownership of their software. The vendor acknowledged the report and began working on a fix.
July 13, 2025 – The fully patched version of the plugin, 2.2.2, was released.

Conclusion

In this blog post, we detailed an Arbitrary File Upload, an Arbitrary File Deletion, and an Arbitrary File Move vulnerability within the HT Contact Form plugin affecting versions 2.2.1 and earlier. The Arbitrary File Upload vulnerability allows unauthenticated threat actors to execute malicious code on the server. The Arbitrary File Deletion and the Arbitrary File Move vulnerabilities allows unauthenticated threat actors to delete arbitrary files, including the wp-config.php file, which can make site takeover possible. The vulnerabilities have been addressed in version 2.2.2 of the plugin.

We encourage WordPress users to verify that their sites are updated to the latest patched version of HT Contact Form as soon as possible considering the critical nature of these vulnerabilities.

All Wordfence users, including those running Wordfence Premium, Wordfence Care, and Wordfence Response, as well as sites running the free version of Wordfence, are fully protected against these vulnerabilities by the Wordfence firewall’s built-in Malicious File Upload and Directory Traversal protections.

If you know someone who uses this plugin on their site, we recommend sharing this advisory with them to ensure their site remains secure, as these vulnerabilities pose a significant risk.

The post 10,000 WordPress Sites Affected by Critical Vulnerabilities in HT Contact Form WordPress Plugin appeared first on Wordfence.

Leave a Comment