The case study focuses on the CVE-2023–6553 vulnerability, discovered by the NEX Team , which exploits the WordPress Backup Migration plugin for version 1.3.7 and below. The CVSS score is 9.8 , making this flaw critical for the target software. It provides an opportunity for unauthenticated remote code execution.
The problem can be traced back to an LFI flaw, which can be leveraged further to achieve a complete RCE via PHP filters.
Within this case study, we will look into how the flaw works internally and explore the data flow in terms of user input processing, reaching the vulnerable sink, and finally leading to code execution.
The Backup Migration plugin by BackupBliss is a WordPress plugin designed to facilitate website backups, restoration, and migration. It provides functionality such as scheduled backups and data transfer between environments.
From a security perspective, the plugin exposes an attack surface through its internal request handling mechanisms, within the file:
This file processes incoming requests and utilizes user-controlled input to construct file paths. Due to insufficient validation and sanitization, this behavior introduces a Local File Inclusion (LFI) vulnerability.
An attacker can manipulate this input to control the included file path, which under certain conditions can be escalated to Remote Code Execution (RCE) through techniques such as PHP filter chain abuse.
The vulnerability originates in the file:
At the top of this file, the code checks whether the getallheaders() function is available and, if so, uses it to populate a $fields array from all incoming HTTP request headers:
getallheaders() is an alias for apache_request_headers() and returns an associative array of every header in the HTTP request — meaning all header values are entirely attacker-controlled.
The code then uses these header values to define several PHP constants:
The critical line here is:
The content-dir header value flows directly into BMI_ROOT_DIR , which is then concatenated with the string includes to form BMI_INCLUDES . No sanitization or validation is performed at any point.
Further down in the file, within a try block, the constant reaches a require_once call:
This is the vulnerable sink. The final included path is constructed as:
Step Value Attacker sends header Content-Dir: hello/ BMI_ROOT_DIR hello/ BMI_INCLUDES hello/includes Final require_once path hello/includes/bypasser.php
This is a classic Local File Inclusion (LFI) vulnerability — attacker-controlled input reaches a file inclusion function without sanitization.
A naive exploitation attempt (e.g., pointing content-dir to a known sensitive file) is blocked by the appended suffix includes/bypasser.php . Since modern PHP versions have removed null byte ( %00 ) support in file paths, the traditional null-byte truncation technique is not viable here.
This shifts the exploitation strategy to PHP Filter Chains .
PHP’s php://filter stream wrapper allows chaining multiple transformations on a data stream. By chaining iconv encoding transformations with base64-decode operations, it's possible to construct a filter chain that generates arbitrary PHP code at runtime — without needing to write any file to disk.
The key insight is that php://temp can serve as an in-memory resource. When a crafted filter chain is passed to require_once , the output of the chain is interpreted and executed as PHP code, achieving Remote Code Execution (RCE) .
Reference: Synacktiv’s PHP Filter Chain research
Using the php_filter_chain_generator :
This generates a long filter chain string starting with:
At runtime, PHP processes these chained transformations and produces the payload:
which is then executed by the PHP engine.
2. The file enforces POST-only access:
3. Change the request method to POST in Burp Suite.
4. Add the Content-Dir header with the generated filter chain as its value.
5. Send a POST parameter 0 with the OS command to execute (e.g., id , whoami ).
Note: The response from backup-heart.php does not return command output directly, since the file doesn't echo responses. Command execution can be confirmed via time-based techniques (e.g., sleep 10 ) or by uploading a dedicated webshell.
I developed it’s full exploit script automates the above process and provides a stable webshell shell by:
The script also includes a --check flag to safely verify whether a target is vulnerable without executing any commands, and formats output using rich tables for readability.
Here’s the Patch provided by official developers.
Analzing this Patch reveals that they included a security check function which they used on other headers too including the vulnerable header Content-Dir
It looks secure, first checking the content is it string or not then length check because filterchains are usually too lengthy on 3rd check it’s checking that the string doesn’t contain php:// checking if the value contains | or not then final check is directory exist or not then it will return the value
then the filterChainFix checking function is implemented on headers too like content-abs
The root cause is the absence of any validation on HTTP header values before they are used in file inclusion. The fix, released in version 1.3.8 , moves these values away from attacker-controlled headers and adds strict path validation. Users should update to 1.3.8 or later immediately.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
