CVE-2025-11970
Executive Summary
CVE-2025-11970 is a Server-Side Request Forgery (SSRF) vulnerability discovered in the Emplibot – AI Content Writer with Keyword Research, Infographics, and Linking | SEO Optimized | Fully Automated WordPress plugin. This plugin is designed to automate content creation and SEO tasks. The vulnerability affects all versions up to and including 1.0.9.
The flaw exists in two functions: emplibot_call_webhook_with_error() and emplibot_process_zip_data(). These functions allow an authenticated attacker with Administrator-level privileges to make arbitrary HTTP requests from the web server to internal or external resources. This can be used to probe internal network services, read sensitive data from cloud metadata endpoints (e.g., AWS, GCP), or interact with internal APIs.
Given the high privileges required (Administrator), the CVSS score is 4.4 (Medium). However, the impact can be severe in cloud environments where SSRF can lead to credential theft or lateral movement. No public proof-of-concept (PoC) or exploit code is currently available, and the patch status is unknown.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability is specific to the WordPress plugin ecosystem. The plugin is distributed via the official WordPress Plugin Directory and is installed as a standard WordPress plugin. No third-party package manager (NPM, PyPI, Go, Ruby) is involved. Administrators should monitor the plugin's update channel for a patched version.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:L
- Base Score: 4.4 (Medium)
- Attack Vector (AV): Network – The attack is carried out over the network.
- Attack Complexity (AC): Low – No special conditions are required.
- Privileges Required (PR): High – The attacker must have Administrator-level access to the WordPress site.
- User Interaction (UI): None – No user interaction is required.
- Scope (S): Unchanged – The vulnerable component and the affected resource are in the same security authority.
- Confidentiality (C): Low – Limited information disclosure (e.g., internal service banners).
- Integrity (I): Low – Limited ability to modify data (e.g., write to internal services).
- Availability (A): Low – Limited ability to cause denial of service.
Threat Level: Medium. While the privileges required are high, the potential for lateral movement and data exfiltration in cloud environments elevates the risk. The lack of a patch makes this a persistent threat for affected sites.
Deep Dive & Mechanics
The vulnerability is a classic Server-Side Request Forgery (SSRF) (CWE-918). The plugin's functions emplibot_call_webhook_with_error() and emplibot_process_zip_data() accept user-supplied URLs without proper validation or sanitization.
emplibot_call_webhook_with_error(): This function is likely used to send error notifications to a webhook URL. The URL is passed as a parameter and used in an HTTP request (e.g.,wp_remote_post()orwp_remote_get()). An attacker can manipulate this URL to point to internal IP addresses (e.g.,http://169.254.169.254/for cloud metadata) or internal services (e.g.,http://localhost:9200/for Elasticsearch).emplibot_process_zip_data(): This function processes ZIP data, possibly downloading a ZIP file from a URL. The URL is again attacker-controlled, allowing the same SSRF attack vector.
The lack of a URL allowlist or blocklist, combined with the absence of validation for private IP ranges, enables the attack. The functions use WordPress's HTTP API, which does not inherently block internal requests.
Vulnerable vs. Patched Code
Since no patch is available, we can only describe the vulnerable pattern.
Vulnerable Code (Conceptual):
function emplibot_call_webhook_with_error($webhook_url, $error_data) {
// No validation of $webhook_url
$response = wp_remote_post($webhook_url, array(
'body' => $error_data
));
// ...
}
function emplibot_process_zip_data($zip_url) {
// No validation of $zip_url
$response = wp_remote_get($zip_url);
// ...
}
Patched Code (Expected):
function emplibot_call_webhook_with_error($webhook_url, $error_data) {
// Validate URL against an allowlist
if (!in_array(parse_url($webhook_url, PHP_URL_HOST), $allowed_webhook_hosts)) {
return new WP_Error('invalid_url', 'Webhook URL not allowed.');
}
// Also block private IPs
if (filter_var($webhook_url, FILTER_VALIDATE_URL) && is_private_ip($webhook_url)) {
return new WP_Error('private_ip', 'Cannot request internal resources.');
}
$response = wp_remote_post($webhook_url, array(
'body' => $error_data
));
// ...
}
The patch should include:
- An allowlist of permitted external hosts.
- A blocklist for private IP ranges (e.g.,
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8,169.254.0.0/16). - Validation that the URL scheme is
httpsonly.
Exploitation & Lab Setup
Red Team
Prerequisites:
- Administrator-level access to a WordPress site running Emplibot <= 1.0.9.
- Network access to the WordPress server.
Exploitation Steps:
Identify the vulnerable endpoint. The functions are likely called via AJAX actions or admin-ajax.php. For example:
admin-ajax.php?action=emplibot_call_webhook&webhook_url=http://169.254.169.254/latest/meta-data/admin-ajax.php?action=emplibot_process_zip&zip_url=http://169.254.169.254/latest/meta-data/
Craft the request. Using a tool like
curl:curl -X POST "http://target.com/wp-admin/admin-ajax.php" \ -d "action=emplibot_call_webhook&webhook_url=http://169.254.169.254/latest/meta-data/" \ --cookie "wordpress_logged_in_xxx=..."Read the response. The response may contain the metadata from the cloud provider.
Escalate. Use the SSRF to access internal services (e.g., Redis, Elasticsearch) or perform port scanning.
Lab Setup:
- Install WordPress and the Emplibot plugin (version 1.0.9) on a local server.
- Create an Administrator account.
- Use a tool like Burp Suite or Postman to send crafted requests.
- Set up a simple HTTP server (e.g.,
nc -lvp 8080) to simulate an internal service.
Blue Team
Detection:
- Monitor for outbound HTTP requests from the web server to internal IP addresses (e.g., 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.0.0.1, 169.254.x.x).
- Alert on requests to cloud metadata endpoints (e.g.,
169.254.169.254). - Monitor for unusual patterns in
admin-ajax.phprequests, especially those with parameters likewebhook_urlorzip_url.
Prevention:
- Implement a Web Application Firewall (WAF) rule to block requests containing private IP addresses in URL parameters.
- Restrict outbound traffic from the web server using a firewall or security group (e.g., AWS Security Groups).
- Disable the plugin until a patch is available.
Indicators of Compromise (IOCs)
Network IOCs:
- Outbound HTTP requests to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, 169.254.0.0/16).
- Requests to cloud metadata endpoints (e.g.,
http://169.254.169.254/). - Unusual
User-Agentstrings in outbound requests (default WordPress HTTP API user agent).
File IOCs:
- Log entries in
wp-content/debug.logshowing failed requests to internal IPs. - Modified plugin files (check file integrity of
emplibotplugin directory).
- Log entries in
Process IOCs:
wp_remote_get()orwp_remote_post()calls with suspicious URLs in PHP error logs.
Detection Engineering
YARA Rule
rule CVE_2025_11970_SSRF_Emplibot {
meta:
description = "Detects SSRF attempts targeting internal IPs via Emplibot plugin"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-11970"
strings:
$ssrf_param1 = "webhook_url="
$ssrf_param2 = "zip_url="
$private_ip = /(10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3})/
condition:
any of ($ssrf_param*) and $private_ip
}
Sigma Rule
title: CVE-2025-11970 Emplibot SSRF Detection
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects outbound HTTP requests to private IP addresses from WordPress admin-ajax.php
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
service: access_log
detection:
selection:
cs-uri-query|contains:
- "webhook_url="
- "zip_url="
cs-uri-stem|endswith: "/admin-ajax.php"
sc-status: 200
filter:
cs-uri-query|contains:
- "webhook_url=http://"
- "zip_url=http://"
condition: selection and filter
falsepositives:
- Legitimate use of webhook functionality with external URLs
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"CVE-2025-11970 Emplibot SSRF Attempt to Private IP";
flow:to_server,established;
content:"admin-ajax.php"; http_uri;
content:"webhook_url="; http_uri;
pcre:"/(10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3})/Ri";
sid:10000001;
rev:1;
reference:cve,2025-11970;
metadata:created_at 2025-04-10, updated_at 2025-04-10;
)
Remediation & Mitigation
Immediate Actions:
- Disable the plugin until a patched version is released.
- Restrict outbound traffic from the web server using a firewall or security group. Block all traffic to private IP ranges and cloud metadata endpoints.
- Review server logs for any signs of exploitation (e.g., requests to internal IPs from
admin-ajax.php).
Long-term Fix:
- Apply the official patch once released by the vendor.
- Implement a Web Application Firewall (WAF) rule to block SSRF attempts.
- Conduct a security audit of all WordPress plugins for similar vulnerabilities.
Workaround:
- If the plugin must remain active, modify the plugin code to add URL validation (as shown in the patched code section). However, this will be overwritten on plugin updates.
Valters IT
Securing Digital Frontiers
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.