The DevTeam Alpha News Aggregation service has sourced the following article originally published on WordFence:

A critical security update was recently issued for Duplicator, one of the most popular plugins in the WordPress ecosystem. Over a million WordPress sites were affected by a vulnerability allowing attackers to download arbitrary files from victim sites. We urge all Duplicator users to update to version 1.3.28 as soon as possible.

We are detecting active exploitation of this vulnerability in the wild, and estimate more than half a million sites are still running a vulnerable version. Built-in firewall protection prevents these attacks for all Wordfence users, both Premium and those still on the free version of Wordfence. As always, it’s still important to perform security updates regardless of other protections.

In today’s post, we’ll take a brief look at the vulnerable code, discuss its severity, and share details of the ongoing attacks against it.

File Download Vulnerability Analysis

The Duplicator plugin helps site administrators migrate and copy WordPress sites. Part of this functionality involves exporting database and file content into portable archives. When an administrator creates a new copy of their site, Duplicator lets them download the generated files from their WordPress dashboard.

 

Screenshot of a Duplicator download prompt.

This was implemented as an AJAX request within Duplicator’s admin interface. The download buttons each trigger a call to the WordPress AJAX handler with the action duplicator_download and a file parameter, indicating the location of the file to be downloaded. When clicked, the requested file is downloaded and the user doesn’t need to leave or reload their current page.

public static function duplicator_download() {
        $file = sanitize_text_field($_GET['file']);
        $filepath = DUPLICATOR_SSDIR_PATH.'/'.$file;
        // Process download
        if(file_exists($filepath)) {
            // Clean output buffer
            if (ob_get_level() !== 0 && @ob_end_clean() === FALSE) {
                @ob_clean();
            }

            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($filepath));
            flush(); // Flush system output buffer

            try {
                $fp = @fopen($filepath, 'r');
                if (false === $fp) {
                    throw new Exception('Fail to open the file '.$filepath);
                }
                while (!feof($fp) && ($data = fread($fp, DUPLICATOR_BUFFER_READ_WRITE_SIZE)) !== FALSE) {
                    echo $data;
                }
                @fclose($fp);
            } catch (Exception $e) {
                readfile($filepath);
            }
            exit;
        } else {
            wp_die('Invalid installer file name!!');
        }
    }

Unfortunately the duplicator_download action was registered via wp_ajax_nopriv_ and was accessible to unauthenticated users. To make things worse, no validation limited the filepaths being downloaded. The file parameter is passed through sanitize_text_field and appended to the plugin constant DUPLICATOR_SSDIR_PATH, but directory traversal was still possible. An attacker could access files outside of Duplicator’s intended directory by submitting values like ../../../file.php to navigate throughout the server’s file structure.

In addition to the AJAX action, the same vulnerability existed in Duplicator’s duplicator_init() function, which is called by WordPress’s init hook.

function duplicator_init() {
    if (isset($_GET['action']) && $_GET['action'] == 'duplicator_download') {
        $file = sanitize_text_field($_GET['file']);
        $filepath = DUPLICATOR_SSDIR_PATH.'/'.$file;
        // Process download
        if(file_exists($filepath)) {
            // Clean output buffer
            if (ob_get_level() !== 0 && @ob_end_clean() === FALSE) {
                @ob_clean();
            }

            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($filepath));
            flush(); // Flush system output buffer

            try {
                $fp = @fopen($filepath, 'r');
                if (false === $fp) {
                    throw new Exception('Fail to open the file '.$filepath);
                }
                while (!feof($fp) && ($data = fread($fp, DUPLICATOR_BUFFER_READ_WRITE_SIZE)) !== FALSE) {
                    echo $data;
                }
                @fclose($fp);
            } catch (Exception $e) {
                readfile($filepath);
            }
            exit;
        } else {
            wp_die('Invalid installer file name!!');
        }
    }
}
add_action('init', 'duplicator_init');

Because it was hooked into init, this function was executed on every WordPress page load for logged-in users and unauthenticated visitors alike. This means an attacker could trigger a file download by adding query strings to any path on a vulnerable site, bypassing AJAX-specific monitoring.

Both of these vulnerable cases have been patched as of Duplicator 1.3.28. The AJAX action has been updated to properly validate filenames, and now requires a matching ID and hash to allow the file download. The duplicator_init() function has been removed entirely.

Attackers Stealing Database Credentials

Arbitrary file download vulnerabilities can be a critical issue regardless of the vulnerable site’s platform, but such attacks against WordPress sites largely target one file: wp-config.php.

Depending on the site, wp-config.php can contain any amount of custom code, but attackers target it to access a site’s database credentials. With these credentials, an attacker can directly access the victim site’s database if it allows remote connections. This access can be used by an attacker to create their own Administrator account and further compromise the site, or simply to inject content or harvest data.

Sites with local databases still have cause for concern, however. On shared hosting environments, it’s possible for one user on a shared server to access the local database of another site on the same server. This certainly limits the attack surface of the vulnerable site, but is still a severe issue.

At the time of this writing, Wordfence has blocked more than 60,000 attempts to download wp-config.php files with this vulnerability. About 50,000 of these events took place before Duplicator patched the flaw, making this a zero-day vulnerability.

Nearly all of these attacks were issued from the same IP address: 77.71.115.52. This IP points to a webserver located in Bulgaria, owned by Varna Data Center EOOD. A handful of websites are hosted on this server, suggesting the attacker could be proxying their attacks through a compromised website. We have associated this IP address with other malicious activity against WordPress recently, and research into its activity is ongoing.

Indicators Of Compromise (IOCs)

The following Indicators of Compromise (IOCs) can be used to determine if your site may have been attacked.

  • Traffic logged from the threat actor’s IP address should be considered suspicious:
    • 77.71.115.52
  • Attacks in this campaign are issued via GET requests with the following query strings:
    • action=duplicator_download
    • file=/../wp-config.php
    • Note: Because this vulnerability can be exploited via WP AJAX, it’s possible to exploit via POST request. In this case, it’s possible for the action parameter to be passed in the POST body instead of the query string. This will prevent the action=duplicator_download string from appearing in HTTP logs. The file parameter must be passed as a query string, however, and is a reliable indicator.

Timeline

  • February 10th, 2020 – First attacks against Duplicator vulnerability. Wordfence users already safe due to built-in firewall protection.
  • February 12th, 2020 – Duplicator releases version 1.3.28 to patch the flaw.

Conclusion

Duplicator’s massive install base, combined with the ease of exploiting this vulnerability, makes this flaw a noteworthy target for hackers. It’s crucial that Duplicator’s users update their plugins to the latest available version as soon as possible to remove this risk. All Wordfence users are protected from these attacks, but don’t forget to update despite this. Also, due to the nature of Duplicator’s functionality, it’s likely that it’s no longer required on your site. If you have no intent of using it to migrate or clone your site in the immediate future, you can delete the plugin without worry. It can always be reinstalled later if needed.

If you believe your site was attacked via this vulnerability, it’s critical that you change your database credentials and WordPress salts immediately. If you’re concerned that an attacker may have gained unauthorized access to your site, consider having our expert analysts perform a Site Security Audit to ensure your security is intact.

Learn more about WordFence by visiting their website.