PoC Archive PoC Archive
High CVE-2026-23499 patched

Saleor Stored XSS via Unrestricted File Upload (CVE-2026-23499)

by Lukasz Rybak (lukasz-rybak) · 2026-07-05

Severity
High
CVE
CVE-2026-23499
Category
web
Affected product
Saleor (saleor/saleor)
Affected versions
Prior to 3.22.27 / 3.21.43 / 3.20.108 (fixed in those releases); Saleor Cloud is not impacted
Disclosed
2026-07-05
Patch status
patched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-04
Author / ResearcherLukasz Rybak (lukasz-rybak)
CVE / AdvisoryCVE-2026-23499
Categoryweb
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagssaleor, stored-xss, unrestricted-file-upload, svg-xss, session-hijack, graphql, javascript
RelatedN/A

Affected Target

FieldValue
Software / SystemSaleor (saleor/saleor)
Versions AffectedPrior to 3.22.27 / 3.21.43 / 3.20.108 (fixed in those releases); Saleor Cloud is not impacted
Language / PlatformPython/Django backend, browser-rendered media
Authentication RequiredYes — authenticated staff user or App
Network Access RequiredYes

Summary

Saleor allowed authenticated staff users or Apps to upload arbitrary file types through its media-upload functionality, including HTML and SVG files containing embedded JavaScript. In deployments where uploaded media is served from the same origin as the admin dashboard, and no Content-Disposition: attachment header forces a download, an uploaded malicious file executes in the browser when opened, giving an attacker a stored XSS against other dashboard users. Because the payload runs in the dashboard’s origin, it can be used to steal session or refresh tokens from other staff members.


Vulnerability Details

Root Cause

The media/file upload path does not restrict or sanitize uploaded content types (notably HTML and SVG containing <script>), and affected deployments serve that media same-origin with the dashboard without a forced-download Content-Disposition header — allowing the browser to execute the uploaded content as if it were first-party.

Attack Vector

  1. An attacker with staff or App-level access to the Saleor dashboard uploads a malicious HTML or SVG file containing embedded JavaScript via the media/file upload functionality.
  2. If the target deployment serves media from the same domain as the dashboard (e.g. example.com/media/ alongside example.com/dashboard/) without a Content-Disposition: attachment header, the file is stored and directly browsable.
  3. The attacker gets another staff member to open the uploaded file’s URL (directly, via a shared link, or through normal dashboard workflows).
  4. The victim’s browser renders/executes the embedded script in the dashboard’s origin, letting the attacker exfiltrate the victim’s session or refresh tokens.

Impact

Stored cross-site scripting against Saleor dashboard users, enabling theft of staff session/refresh tokens and potential account takeover or privilege escalation between staff accounts. Deployments that host media on a separate domain, or that send Content-Disposition: attachment for media, are not affected.


Environment / Lab Setup

Target:   Saleor instance < 3.22.27 / 3.21.43 / 3.20.108, media hosted same-origin as the dashboard
Attacker: Authenticated staff or App account, standard web browser

Proof of Concept

PoC Script

The upstream source repository is advisory-only: it documents the vulnerability, impact, and a defensive scanning command, but does not publish a standalone exploit script or malicious sample file. No script is included in this folder for that reason.

The advisory instead documents a defensive Django management command that ships with fixed Saleor versions to audit existing uploads for files that would now be rejected:

1
2
./manage.py remove_invalid_files            # scan only
./manage.py remove_invalid_files --apply    # scan and delete

Detection & Indicators of Compromise

Signs of compromise:

  • Unexpected .html or .svg files in the media storage (filesystem, S3 bucket, etc.) uploaded by staff/App accounts.
  • Staff session/refresh token misuse shortly after a suspicious media file was accessed.

Remediation

ActionDetail
Primary fixUpgrade to Saleor 3.22.27, 3.21.43, or 3.20.108 (or later), and run ./manage.py remove_invalid_files to audit existing uploads.
Interim mitigationServe media from a separate domain from the dashboard, configure Content-Disposition: attachment for media responses, and/or set a restrictive Content-Security-Policy on the media host.

References


Notes

Mirrored from https://github.com/lukasz-rybak/CVE-2026-23499 on 2026-07-05.