CVE-2025-4611 PoC
Mitre Description:
The Slim SEO – Fast & Automated WordPress SEO Plugin plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's slim_seo_breadcrumbs shortcode in all versions up to, and including, 4.5.3 due to insufficient input sanitization and output escaping on user supplied attributes. 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.
CVE publish date: 21 may 2025
https://nvd.nist.gov/vuln/detail/CVE-2025-4611
analysis: i have installed the right version through wordpress
we want to install the vulnarable version 4.5.3 and fixed version 4.5.4 for diffrent comparison
the vulnarable file in /slim-seo/src/Breadcrumbs.php
we will compare the 2 file in diffrent versions

Vulnerable Function
The vulnerable function is:
public function render_shortcode( $atts ): string {
$this->args = wp_parse_args( $atts, $this->args );
$this->parse(); // builds $this->current and $this->links
if ( 'true' === $this->args['display_current'] ) {
$items[] = sprintf(
'<span class="breadcrumb breadcrumb--last" aria-current="page">%s</span>',
$this->current // Vulnerable value
);
}
...
}
The value of $this->current
is output directly into HTML without escaping. If an attacker can influence this value, they can inject JavaScript or HTML into the page.
Call Chain: How It Gets Triggered
1. render_shortcode()
is registered as a shortcode handler
render_shortcode()
is registered as a shortcode handleradd_shortcode( 'slim_seo_breadcrumbs', [ $this, 'render_shortcode' ] );
This means it will execute when a page or post includes:
[slim_seo_breadcrumbs]
2. render_shortcode()
calls parse()
render_shortcode()
calls parse()
Within render_shortcode()
, the method parse()
is called:
$this->parse();
This method sets $this->current
depending on the context post page
How the XSS Works
Case: Search Page Context
Inside the parse()
function:
elseif ( is_search() ) {
$this->current = sprintf( $this->args['label_search'], get_search_query() );
}
The default label_search
is:
'label_search' => __( 'Search Results for “%s”', 'slim-seo' ),
So the final output becomes:
$this->current = 'Search Results for “' . get_search_query() . '”';
If the attacker controls get_search_query()
, they can inject payloads.
No Output Encoding
The HTML is rendered as:
<span class="breadcrumb breadcrumb--last" aria-current="page">
{raw $this->current}
</span>
in older version there’s no esc_html()
, htmlspecialchars()
, or wp_kses()
. This leads directly to XSS.
esc_html()
, htmlspecialchars()
, or wp_kses()
. This leads directly to XSS.Steps:
WordPress detects this as a search query (
is_search()
returns true).get_search_query()
returns the attacker’s input.$this->current
becomes“<script>alert(1)</script>”
.It gets rendered directly into the page.
JavaScript executes in the victim’s browser.
PoC:
on pages tab add a new page

after that go to + and search in patterns for breadcrumb

add the breadcrumb and go to settings for breadcrumb and change the label home to
<img src=x onerror=alert(1)>

save the page and view the XSS :)

Last updated