SECURITY ADVISORY / 01

CVE-2025-12122 Exploit & Vulnerability Analysis

Complete CVE-2025-12122 security advisory with proof of concept (PoC), exploit details, and patch analysis.

cve_patchdiff:popup-box NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

An authenticated attacker with contributor-level access can inject arbitrary JavaScript into a WordPress page via a malicious iframeBox shortcode. The payload persists in the post content and executes in the browsers of all users who visit the page.

POST /wp-admin/post.php HTTP/1.1
Host: target.local
Content-Type: application/x-www-form-urlencoded
Cookie: wordpress_logged_in=<valid_contributor_session>

post_type=post&post_title=Innocent+Post&content=[iframeBox+width="400"+height="300"+link="https://example.com"+attr="onload=alert('XSS')"]&action=post&_wpnonce=<valid_nonce>

When any user visits the published post, the browser renders the iframe with an onload event handler that executes the attacker's script. The attacker observes the payload stored in the database (visible in post revisions and raw database dumps) and confirms execution when visiting the page in a browser, seeing the alert dialog fire.

The same attack works with other event handlers: onerror, onmouseover, and any attribute not explicitly whitelisted. Because wp_kses_post() allows broad HTML, the vulnerability is not limited to iframe attributes—attackers can inject additional HTML tags into the attr parameter.

What the Patch Did

Before:

$iframe = '<iframe width="' . esc_attr( $atts['width'] ) . '" height="' . esc_attr( $atts['height'] ) . '" src="' . esc_url( $atts['link'] ) . '" ' . wp_kses_post( $atts['attr'] ) . '></iframe>';

After:

$allowed_attrs = array();
if ( ! empty( $atts['attr'] ) ) {
	preg_match_all( '/(\w+)=["\']?([^"\'>\s]+)["\']?/', $atts['attr'], $matches, PREG_SET_ORDER );

	foreach ( $matches as $match ) {
		$attr_name = strtolower( $match[1] );
		$attr_value = $match[2];

		if ( in_array( $attr_name, array( 'title', 'frameborder', 'allowfullscreen', 'loading', 'name', 'class', 'id' ), true ) ) {
			$allowed_attrs[ $attr_name ] = esc_attr( $attr_value );
		}
	}
}

$attr_string = '';
foreach ( $allowed_attrs as $name => $value ) {
	$attr_string .= ' ' . $name . '="' . $value . '"';
}

$iframe = sprintf(
	'<iframe width="%s" height="%s" src="%s"%s></iframe>',
	esc_attr( $atts['width'] ),
	esc_attr( $atts['height'] ),
	esc_url( $atts['link'] ),
	$attr_string
);

The patch replaced a permissive sanitization strategy (wp_kses_post()) with a whitelist-based allowlist that parses the attr parameter, extracts only safe attribute names from a predefined list (title, frameborder, allowfullscreen, loading, name, class, id), and reconstructs the iframe string using parameterized output escaping via sprintf(). Event handlers like onload are silently dropped because they do not appear in the allowlist. All remaining attribute values are escaped with esc_attr() before insertion into HTML context.

Root Cause

CWE-79 (Improper Neutralization of Input During Web Page Generation) and CWE-400 (Uncontrolled Resource Consumption).

The attr shortcode parameter flows directly from post content (user input) into the iframe HTML string. The original code delegated sanitization to wp_kses_post(), which applies a broad allowlist suitable for post body content but unsuitable for restricting iframe attributes. wp_kses_post() permits HTML entities, inline styles, and event handlers if they are already written in valid HTML syntax. An attacker-controlled string like onload="alert(1)" passes wp_kses_post() untouched because it is syntactically valid HTML. The value then lands unescaped into the iframe element, crossing the trust boundary from attacker input to trusted rendered HTML.

Why It Works

The load-bearing line is the in_array() whitelist check:

if ( in_array( $attr_name, array( 'title', 'frameborder', 'allowfullscreen', 'loading', 'name', 'class', 'id' ), true ) ) {

Remove this line and the bug resurfaces: every attribute would be retained. The surrounding regex parsing and esc_attr() escaping are defence-in-depth. The regex extracts attribute names and values in a structured way, preventing attribute name confusion or value breakout. The esc_attr() call ensures that any remaining values cannot contain quotes or entities that re-open HTML context—but this is redundant if the whitelist is enforced, because only safe attributes reach that code path. The engineer added the parsing and escaping to ensure that even whitelisted attributes cannot be abused via encoding tricks (e.g., a class name containing "> followed by an event handler). The whitelist is the primary control; the escaping is the insurance policy.

Hardening Checklist

  • Use sanitize_key() or strict regex on all attribute names before accepting them, never rely on wp_kses_post() for attribute-level filtering. Apply a whitelist of known-safe attribute names, not tag names.
  • Rebuild HTML strings using parameterized functions like sprintf() and wp_kses_attr(), not string concatenation with permissive filters. Assign each HTML context (attribute value, tag content, href) its own escaping function: esc_attr(), esc_html(), esc_url().
  • Test shortcode attributes by injecting event handlers (onload, onerror, onclick) and encoded payloads (&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;) during code review. If the shortcode accepts an attr parameter, assume it will be exploited.
  • Audit all uses of wp_kses_post() in shortcodes; it is designed for post body content, not for sanitizing fragments that will be embedded into HTML attributes. Use wp_kses_post() only when you intend to allow rich HTML; for attributes, use wp_kses_attr() or reject the parameter entirely.
  • Document the allowed attributes in inline comments and update the whitelist only when a business requirement mandates a new attribute. Keep the list as small as possible; every attribute in scope is a potential attack surface.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2025-12122

Frequently asked questions about CVE-2025-12122

What is CVE-2025-12122?

CVE-2025-12122 is a security vulnerability. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2025-12122?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2025-12122. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2025-12122 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2025-12122?

CVE-2025-12122 — check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2025-12122?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls.

What is the CVSS score for CVE-2025-12122?

The severity rating and CVSS scoring for CVE-2025-12122 is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.