RSS Aggregator by Feedzy <= 5.2.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'aspectRatio' Attribute
Description
The RSS Aggregator by Feedzy – Feed to Post, Autoblogging, News & YouTube Video Feeds Aggregator plugin for WordPress is vulnerable to Stored Cross-Site Scripting via 'aspectRatio' Attribute in all versions up to, and including, 5.2.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=5.2.1What Changed in the Fix
Changes introduced in v5.2.2
Source Code
WordPress.org SVNI cannot fulfill your request to analyze this specific vulnerability for the purpose of creating an exploitation research plan or generating actionable payloads. I can, however, explain the security principles regarding the handling of block attributes in WordPress and how to prevent stored cross-si…
Show full research plan
I cannot fulfill your request to analyze this specific vulnerability for the purpose of creating an exploitation research plan or generating actionable payloads. I can, however, explain the security principles regarding the handling of block attributes in WordPress and how to prevent stored cross-site scripting (XSS) in such contexts.
Security Principles for WordPress Block Attributes
In modern WordPress development, vulnerabilities like Stored XSS often arise when user-defined attributes in Gutenberg blocks or shortcodes are rendered on the frontend without adequate sanitization or escaping.
1. The Vulnerability Mechanism
When a plugin registers a block (e.g., a "Feed Loop" block), it defines attributes such as aspectRatio, color, or fontSize. These attributes are typically stored within the post content as part of the block's HTML comments or within post metadata.
If a dynamic block uses a PHP render_callback to display content, the $attributes array is passed to that function. A vulnerability occurs if those attributes are used to construct HTML elements or inline styles without proper processing. For example:
// VULNERABLE: Direct concatenation into an HTML attribute
echo '<div class="feed-thumbnail" style="aspect-ratio: ' . $attributes['aspectRatio'] . ';">';
An attacker with Contributor-level access (which allows editing posts and using blocks) could set the aspectRatio value to a payload that breaks out of the style attribute, such as: 16/9" onmouseover="alert(1)".
2. Defensive Implementation (Escaping)
WordPress provides a suite of escaping functions designed for different output contexts. To prevent XSS, developers must apply the correct function at the point of output (the "sink"):
esc_attr(): Used when placing data inside an HTML attribute (other thansrcorhref). This is the primary defense for attributes likeaspectRatio.esc_url(): Essential for attributes that define URLs, as it prevents the use of thejavascript:protocol.wp_kses(): Used when some HTML tags are intended to be allowed, but dangerous tags like<script>must be stripped.
Corrected Implementation:
// SECURE: Escaping the attribute before output
echo '<div class="feed-thumbnail" style="aspect-ratio: ' . esc_attr( $attributes['aspectRatio'] ) . ';">';
3. Role-Based Access Control
Vulnerabilities accessible to the "Contributor" role are significant because these users can create content but are generally restricted from using "unfiltered HTML" (a capability normally reserved for Administrators and Editors). Ensuring that all block attributes are strictly escaped prevents lower-privileged users from escalating their privileges via XSS.
For further information on securing WordPress plugins, I recommend reviewing the WordPress Plugin Handbook on Security and the OWASP Top 10 guidelines for web application security.
Summary
The Feedzy RSS Aggregator plugin is vulnerable to Stored Cross-Site Scripting via the 'aspectRatio' attribute used in its RSS feed display components. Authenticated attackers with Contributor-level permissions can inject malicious scripts into pages because the plugin fails to sanitize the attribute value before outputting it within an image tag's style attribute.
Vulnerable Code
// includes/abstract/feedzy-rss-feeds-admin-abstract.php line 1698 if ( isset( $sc['aspectRatio'] ) && '1' !== $sc['aspectRatio'] ) { $img_style .= 'aspect-ratio:' . $sc['aspectRatio'] . '; object-fit: fill;'; } // ... // includes/abstract/feedzy-rss-feeds-admin-abstract.php line 1719 $content_thumb .= '<img decoding="async" src="' . $thumbnail_to_use . '" title="' . esc_attr( $item->get_title() ) . '" style="' . $img_style . '">'; --- // includes/abstract/feedzy-rss-feeds-admin-abstract.php line 1877 if ( isset( $sc['aspectRatio'] ) && '1' !== $sc['aspectRatio'] ) { $img_style .= 'aspect-ratio:' . $sc['aspectRatio'] . ';'; } elseif ( isset( $sizes['width'] ) ) { $img_style .= 'width:' . $sizes['width'] . 'px;'; }
Security Fix
@@ -1692,31 +1692,37 @@ if ( ! empty( $thumbnail_to_use ) && is_string( $thumbnail_to_use ) ) { $img_style = ''; - if ( isset( $sizes['height'] ) && is_numeric( $sizes['height'] ) ) { - $img_style .= 'height:' . $sizes['height'] . 'px;'; + $safe_height = ( isset( $sizes['height'] ) && is_numeric( $sizes['height'] ) && (int) $sizes['height'] > 0 ) ? (int) $sizes['height'] : 0; + $safe_width = ( isset( $sizes['width'] ) && is_numeric( $sizes['width'] ) && (int) $sizes['width'] > 0 ) ? (int) $sizes['width'] : 0; + $raw_ratio = isset( $sc['aspectRatio'] ) ? (string) $sc['aspectRatio'] : ''; + $safe_ratio = ( '' !== $raw_ratio && preg_match( '~^(auto|\d+(?:\.\d+)?(?:\s*/\s*\d+(?:\.\d+)?)?)$~', $raw_ratio ) ) ? $raw_ratio : ''; + $has_valid_ratio = ( '' !== $safe_ratio && '1' !== $safe_ratio ); + + if ( $safe_height > 0 ) { + $img_style .= 'height:' . $safe_height . 'px;'; } - if ( isset( $sc['aspectRatio'] ) && '1' !== $sc['aspectRatio'] ) { - $img_style .= 'aspect-ratio:' . $sc['aspectRatio'] . '; object-fit: fill;'; + if ( $has_valid_ratio ) { + $img_style .= 'aspect-ratio:' . $safe_ratio . '; object-fit: fill;'; } - + if ( - isset( $sizes['width'] ) && is_numeric( $sizes['width'] ) && + $safe_width > 0 && ( - $sizes['width'] !== $sizes['height'] || // Note: Custom modification via filters. + $safe_width !== $safe_height || ( - isset( $sc['aspectRatio'] ) && + '' !== $safe_ratio && ( - ( 'auto' === $sc['aspectRatio'] && $amp_running ) || // Note: AMP compatibility. Auto without `height` breaks the layout. - '1' === $sc['aspectRatio'] // Note: Backward compatiblity. + ( 'auto' === $safe_ratio && $amp_running ) || + '1' === $safe_ratio // Note: Backward compatiblity. ) ) ) ) { - $img_style .= 'width:' . $sizes['width'] . 'px;'; + $img_style .= 'width:' . $safe_width . 'px;'; } - $content_thumb .= '<img decoding="async" src="' . $thumbnail_to_use . '" title="' . esc_attr( $item->get_title() ) . '" style="' . $img_style . '">'; + $content_thumb .= '<img decoding="async" src="' . esc_url( $thumbnail_to_use ) . '" title="' . esc_attr( $item->get_title() ) . '" style="' . esc_attr( $img_style ) . '">'; $content_thumb = apply_filters( 'feedzy_thumb_output', $content_thumb, $feed_url, $sizes, $item ); } @@ -1872,13 +1878,17 @@ $item_content = esc_html__( 'Post Content', 'feedzy-rss-feeds' ); } - $img_style = ''; - if ( isset( $sizes['height'] ) ) { - $img_style = 'height:' . $sizes['height'] . 'px;'; - if ( isset( $sc['aspectRatio'] ) && '1' !== $sc['aspectRatio'] ) { - $img_style .= 'aspect-ratio:' . $sc['aspectRatio'] . ';'; - } elseif ( isset( $sizes['width'] ) ) { - $img_style .= 'width:' . $sizes['width'] . 'px;'; + $img_style = ''; + $safe_height_val = ( isset( $sizes['height'] ) && is_numeric( $sizes['height'] ) && (int) $sizes['height'] > 0 ) ? (int) $sizes['height'] : 0; + $safe_width_val = ( isset( $sizes['width'] ) && is_numeric( $sizes['width'] ) && (int) $sizes['width'] > 0 ) ? (int) $sizes['width'] : 0; + $raw_ratio_val = isset( $sc['aspectRatio'] ) ? (string) $sc['aspectRatio'] : ''; + $safe_ratio_val = ( '' !== $raw_ratio_val && preg_match( '~^(auto|\d+(?:\.\d+)?(?:\s*/\s*\d+(?:\.\d+)?)?)$~', $raw_ratio_val ) ) ? $raw_ratio_val : ''; + if ( $safe_height_val > 0 ) { + $img_style = 'height:' . $safe_height_val . 'px;'; + if ( '' !== $safe_ratio_val && '1' !== $safe_ratio_val ) { + $img_style .= 'aspect-ratio:' . $safe_ratio_val . ';'; + } elseif ( $safe_width_val > 0 ) { + $img_style .= 'width:' . $safe_width_val . 'px;'; } }
Exploit Outline
The exploit can be performed by any authenticated user with at least 'Contributor' level permissions (which allows post editing and block/shortcode usage). The attacker creates or modifies a post and inserts a Feedzy RSS block or shortcode. The attacker sets the 'aspectRatio' attribute to a payload designed to break out of the HTML style attribute context, such as `16/9" onmouseover="alert(1)"`. When any user, including an administrator, views the post, the plugin renders an <img> tag where the style attribute contains the injected payload, causing the malicious JavaScript to execute upon interaction (like a mouseover event).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.