Duplicator < 1.3.28 — Directory Traversal
CVE-2020-11738Published · Updated Action summary
Duplicator – Backups & Migration Plugin – Cloud Backups, Scheduled Backups, & More: Affected: <1.3.28Update to 1.3.28 or newer
Duplicator Pro: Affected: <3.8.7.1Update to 3.8.7.1 or newer
All remediation steps1M+ active installs
Watch plugin- Severity
- High7.5
- CVSS
- 7.5CVSS 3.1 · CNA (Wordfence)
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NWhat does this vector mean?
AV:NAttack vector- Network
AC:LAttack complexity- Low
PR:NPrivileges required- None
UI:NUser interaction- None
S:UScope- Unchanged
C:HConfidentiality- High
I:NIntegrity- None
A:NAvailability- None
- Weakness
- CWE-22 Path Traversal
- Required access
- No account needed
- Exploit prediction (EPSS)
- No score
- CISA KEV
- Listed in CISA KEV
On this page9
Am I affected?
Duplicator – Backups & Migration Plugin – Cloud Backups, Scheduled Backups, & More duplicator
- Affected< 1.3.28
- Patched in1.3.28
- Latest5.1.1
Update to 1.3.28 or newer.
You are affected if you run < 1.3.28. Version 1.3.28 and later are not affected by this issue.
Duplicator Pro duplicator-pro
- Affected< 3.8.7.1
- Patched in3.8.7.1
Update to 3.8.7.1 or newer.
You are affected if you run < 3.8.7.1. Version 3.8.7.1 and later are not affected by this issue.
- Check your version: in wp-admin go to Plugins → Installed Plugins and read the version under Duplicator – Backups & Migration Plugin – Cloud Backups, Scheduled Backups, & More.
- No admin access? Run a passive check of your site. It reads public files only.
- No account is needed to exploit this. Treat any exposed site on an affected version as at risk.
- You are affected if the Duplicator plugin is installed and active at version 1.3.26 or earlier.
- You are affected regardless of whether any backup package has been created — the vulnerable handler is registered on every request.
- You are affected even if you are not logged in: the handler is reachable unauthenticated via `?action=duplicator_download` and via `admin-ajax.php` (`nopriv`).
- You are NOT affected if you run Duplicator 1.3.28 or later, where the handler requires a valid package id/hash and a whitelisted file selector.
What to do
- Update Duplicator to version 1.3.28 or later.
- If you cannot update immediately, block requests containing `action=duplicator_download` at the WAF/reverse-proxy level, and deny direct access to the `wp-snapshots` directory.
- Rotate database credentials and WordPress salts if you suspect `wp-config.php` was read.
- Audit web-server access logs for requests to `?action=duplicator_download` or `admin-ajax.php?action=duplicator_download` containing `../` or `%2e%2e`.
- Because this CVE is listed in CISA KEV, review the site for signs of compromise: unknown administrator accounts, new PHP files under wp-content/uploads, and unexpected redirects.
- Rotate administrator passwords and application secrets if the site ran a vulnerable version while publicly reachable.
- Watch Duplicator – Backups & Migration Plugin – Cloud Backups, Scheduled Backups, & More to get an email when the next vulnerability is published.
Advisory
Source description from Wordfence Intelligence, credited to nam3lum.
The Duplicator (Free & Pro) plugin for WordPress is vulnerable to Directory Traversal in versions up to 1.3.28 (and Duplicator Pro before 3.8.7.1) via the 'file' parameter through the duplicator_download() or duplicator_init() function. This makes it possible for unauthenticated attackers to read the contents of arbitrary files on the server, which can contain sensitive information.
VulnCrest analysis
Duplicator (WordPress backup/migration plugin) versions up to and including 1.3.26 expose an unauthenticated file-download handler, `duplicator_download`, that concatenates the attacker-supplied `file` GET parameter directly onto the plugin's snapshot directory path. Because `sanitize_text_field()` does not remove `../` sequences, an unauthenticated attacker can traverse out of the snapshots directory and read arbitrary files readable by the web server (for example `wp-config.php`, which contains database credentials). The handler is reachable both through the `init` hook (`?action=duplicator_download`) and through `admin-ajax.php` (`wp_ajax_nopriv_duplicator_download`). Version 1.3.28 fixes the issue by requiring a valid package `id` and `hash` and by mapping the `file` parameter to a fixed whitelist of package artifacts.
Root cause
The plugin registers an unauthenticated download handler in two places.
In duplicator.php (vulnerable 1.3.26), a function is hooked on init:
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)) {
...
header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
...
try {
$fp = @fopen($filepath, 'r');
...
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');
The same logic exists as an AJAX action in ctrls/class.web.services.php:
add_action('wp_ajax_duplicator_download', array(__CLASS__, 'duplicator_download'));
add_action('wp_ajax_nopriv_duplicator_download', array(__CLASS__, 'duplicator_download'));
...
public static function duplicator_download() {
$file = sanitize_text_field($_GET['file']);
$filepath = DUPLICATOR_SSDIR_PATH.'/'.$file;
...
}
DUPLICATOR_SSDIR_PATH is defined in define.php as <ABSPATH>/wp-snapshots:
define('DUPLICATOR_SSDIR_NAME', 'wp-snapshots');
define('DUPLICATOR_SSDIR_PATH', duplicator_get_abs_path() . '/' . DUPLICATOR_SSDIR_NAME);
The file parameter is only passed through sanitize_text_field(), which strips tags and invalid UTF-8 but does not strip path traversal sequences such as ../. The value is then concatenated onto the snapshots directory and handed to file_exists() / fopen() / readfile(). There is no realpath() containment check, no basename() on the input, and no whitelist. Consequently a request such as ?action=duplicator_download&file=../wp-load.php resolves to <ABSPATH>/wp-snapshots/../wp-load.php = <ABSPATH>/wp-load.php, and the file is streamed back to the caller.
Both entry points are unauthenticated: the init hook runs for every request, and the AJAX action is registered with the nopriv variant. No nonce or capability check guards the download.
The fix
Version 1.3.28 removes the init-hooked duplicator_init() function from duplicator.php entirely and rewrites the AJAX handler in ctrls/class.web.services.php so that the caller can no longer supply an arbitrary path.
The patched handler requires three parameters and validates them:
public static function duplicator_download()
{
$error = false;
if (!isset($_GET['id']) || !isset($_GET['hash']) || !isset($_GET['file'])) {
$error = true;
}
$packageId = (int) $_GET['id'];
$hash = sanitize_text_field($_GET['hash']);
$file = sanitize_text_field($_GET['file']);
if ($error || ($package = DUP_Package::getByID($packageId)) == false) {
$error = true;
}
if ($error || $hash !== $package->Hash) {
$error = true;
}
switch ($file) {
case 'sql':
$fileName = "{$package->NameHash}_database.sql";
break;
case 'archive':
$format = strtolower($package->Archive->Format);
$fileName = "{$package->NameHash}_archive.{$format}";
break;
case 'installer':
$fileName = $package->NameHash.'_installer.php';
break;
default:
$error = true;
}
$filepath = DUPLICATOR_SSDIR_PATH.'/'.$fileName;
...
}
Key changes:
- The
fileparameter is no longer a path — it is a selector restricted tosql,archive, orinstaller. Any other value sets$errorand the request is rejected. - The actual filename is derived server-side from the package's
NameHash, so no attacker-controlled string reaches the filesystem path. - The request must reference a real package (
id) and present the matching packagehash, which is a secret value not exposed to unauthenticated users. - On failure the handler now
sleep(2)s beforewp_die('Invalid request')to slow brute-force attempts, and the error message no longer leaks the internal filename.
The corresponding view (views/packages/details/detail.php) was updated to build download links with id, hash, and the new file selector instead of a raw filename.
How the attack works
- Attacker sends an unauthenticated GET request to the site root with `action=duplicator_download` and a `file` value containing `../` traversal (or the equivalent request to `/wp-admin/admin-ajax.php`).
- The `init` hook (or the `wp_ajax_nopriv_duplicator_download` action) invokes the download handler.
- The handler applies only `sanitize_text_field()` to `file`, which leaves `../` intact, and concatenates it onto `DUPLICATOR_SSDIR_PATH` (`<ABSPATH>/wp-snapshots`).
- `file_exists()` resolves the traversal, and `fopen()`/`readfile()` stream the target file back to the attacker.
- The attacker reads sensitive files such as `wp-config.php` (database credentials, salts) or `/etc/passwd`, enabling further compromise.
How to check if you are affected
Detection is version-based plus a benign behavioural probe.
Version check: fetch
/wp-content/plugins/duplicator/readme.txtand read theStable tag. Any value<= 1.3.26is vulnerable.Behavioural probe (benign): request the download handler with a traversal that points at a harmless, known plugin file, e.g.
GET /?action=duplicator_download&file=../wp-content/plugins/duplicator/readme.txtOn a vulnerable build the response body contains the raw file contents (the plugin readme). On a patched build the handler rejects the request (missing id/hash) and the normal WordPress page is returned instead.Log signatures: look for
action=duplicator_downloadcombined withfile=values containing../,%2e%2e, or absolute paths, on both/and/wp-admin/admin-ajax.php.
The detection spec shipped with this report performs the version check and the benign readme probe and reports vulnerable when the plugin version is <= 1.3.26 or when the benign probe returns the plugin readme content.
Proof of execution
VulnCrest does not publish proof-of-concept code for KEV-listed vulnerabilities.
Exploits & references
Proof-of-concept & detection
This CVE is listed in CISA KEV. VulnCrest does not publish proof-of-concept code for KEV-listed vulnerabilities.
Third-party code, not reviewed by VulnCrest. Fake PoC repositories regularly contain malware. Read the code before running anything, and only run it against a lab you own.
Advisories & patches
- Advisorywordfence.com
Related CVEs
More in Duplicator – Backups & Migration Plugin – Cloud Backups, Scheduled Backups, & More
All 20| Severity | Vulnerability | CVE | Published |
|---|---|---|---|
| Medium5.3 | Unauthenticated Full Path Disclosure <5.1.0Update to 5.1.0 | CVE-2026-112244 | |
| Medium6.5 | Authenticated (Subscriber+) SQL Injection <5.1.0Update to 5.1.0 | CVE-2026-115262 | |
| Medium6.1 | Reflected Cross-Site Scripting <5.1.0Update to 5.1.0 | CVE-2026-112243 | |
| High7.2 | Authenticated (Multisite Subsite Admin+) Arbitrary Code Execution ≤1.5.17Update to 5.0.0 | CVE-2026-115251 | |
| High8.8 | Unauthenticated PHP Object Injection ≤1.5.17Update to 5.0.0 | CVE-2026-82814 |
- Medium5.3Unauthenticated Full Path Disclosure
<5.1.0Update to 5.1.0
- Medium6.5Authenticated (Subscriber+) SQL Injection
<5.1.0Update to 5.1.0
- Medium6.1Reflected Cross-Site Scripting
<5.1.0Update to 5.1.0
- High7.2Authenticated (Multisite Subsite Admin+) Arbitrary Code Execution
≤1.5.17Update to 5.0.0
- High8.8Unauthenticated PHP Object Injection
≤1.5.17Update to 5.0.0
Same weakness (Path Traversal)
All Path Traversal| Severity | Vulnerability | CVE | Published |
|---|---|---|---|
| High7.2 | Unauthenticated Stored Cross-Site Scripting via Comment User-Agent Header | CVE-2026-120688 | |
| Critical9.8 | Unauthenticated Arbitrary File Upload via File Upload via Public Form Submission | CVE-2026-125055 | |
| Critical9.8 | Unauthenticated PHP Object Injection to Remote Code Execution via 'form' Parameter | CVE-2026-119077 | |
| Medium5.3 | Unauthenticated Sensitive Information Exposure in 'search' Parameter | CVE-2026-121518 |
- High7.2Unauthenticated Stored Cross-Site Scripting via Comment User-Agent Header
EZ SQL Reports Shortcode Widget and DB Backup
≤5.25.25Update to 5.26.45
- Critical9.8Unauthenticated Arbitrary File Upload via File Upload via Public Form Submission
Chatterbug Forms – Fast, Flexible WordPress Form Builder
≤1.0.2Update to 1.1
- Critical9.8Unauthenticated PHP Object Injection to Remote Code Execution via 'form' Parameter
ELEX WordPress HelpDesk & Customer Ticketing System
≤3.3.9Update to 3.4.0
- Medium5.3Unauthenticated Sensitive Information Exposure in 'search' Parameter
Customer Reviews for WooCommerce
≤5.127.0Update to 5.128.0
Timeline
CVE published
Severity high, CVSS 7.5
Record last updated
VulnCrest analysis published
Sources & licenses
Wordfence record- Vulnerability data
- Wordfence Intelligence. Copyright 2012-2026 Defiant Inc. Licensed under the CC BY-SA 4.0.
- CVE® identifiers
- Copyright 1999-2026 The MITRE Corporation. Used under the CVE Program. CVE® is a registered trademark of The MITRE Corporation.
- CVSS / CWE
- National Vulnerability Database (NVD), shown unmodified. This product uses the NVD API but is not endorsed or certified by the NVD.
- KEV status
- CISA Known Exploited Vulnerabilities catalog (CC0 1.0).
- VulnCrest content
- Analysis, summary, remediation and page layout © VulnCrest. Not sourced from NVD.
