Skip to content

REDAXO coordinated fifteen-GHSA wave: forgeable login tokens, parameter-keyed CSRF guards, arbitrary-table export, and one-sided move authorization (GHSA, 2026-10-27)

Sources: GitHub Security Advisories published 2026-10-27T12:30Z (one coordinated REDAXO wave, 15 advisories, bands spread across fixed 5.21.0/5.21.3/5.21.4/5.21.5 — mixed per-leg fix bands, so "patched" version claims need per-leg checking). REDAXO is a German-market PHP CMS; the product matters less than the shapes, which generalize to any session-token + feature-permission + move/copy architecture.

Why this is worth an operator pattern

  • A token derived from a secret is forgeable whenever the secret has a forceable predictable state. The critical leg is not "weak randomness" — the temporary login token's session-tied secret can be forced into a predictable state by the request flow itself, then tokens are forged offline for arbitrary accounts (admin), landing on backend access with code execution via template/module editing outside live mode. Audit rule for every token/signature scheme: list the input secret's full state space, including unset/empty/default/failed-session states, and check whether an attacker-reachable path pins the secret to any of them before derivation. This is the same family as released-slot boot-value keys (Oct 27 Supermicro RAKP page) and empty-container config gates — the secret's unconfigured or evicted state is part of the scheme.
  • CSRF guards keyed on a request parameter are guards with a bypass parameter. The user-administration page checks the CSRF token only when save is set; the update path is selected by a different verb-ish parameter (FUNC_UPDATE) and never consults the token — a crafted GET from a logged-in users[] holder grants admin rights, deactivates accounts, or locks out the sole admin. Sweep: on every admin form, enumerate all action/selecting parameters (save|update|delete|func|task|do) and replay each mutation WITHOUT the token; the guard often covers only the parameter named in the original fix.
  • A feature permission is not an entity allowlist. The backup addon's export honors the backup[export] permission but accepts any table name — exporting rex_user / rex_user_session yields plaintext session ids, stay-logged-in keys (admin ATO without password), and password hashes. Reusable probe: for every export/dump/backup/report/print/download feature at your lowest tier, request the credential tables (*user*, *session*, *token*, *key*) by name. The export tool is a credential oracle whenever the allowlist is the feature flag.
  • A CSRF token is not an authorization check — and demoted admins keep live sessions. rex_api_package lets any backend user holding a valid CSRF token uninstall system addons (dropping their database tables), deactivate/install packages. The advisory's own threat model names demoted admins with active sessions as the attacker. Two rules: (1) treat token possession as identity, never capability, and diff which API endpoints gate on role vs merely on token validity; (2) pair with the session-lifecycle leg (5q2r: sessions survive password change for three months) — revocation rarely covers every carrier, so run the demote/detach-then-replay probe against every management API family.
  • Preview/version/history render paths are a second authorization surface. rex_version and rex_history_date with arbitrary article_id read unpublished working versions and historic/deleted content because the preview modes never re-check category/clang permissions (one leg even reachable with only history[article_rollback]). Same rule as the WebSocket-route and alternate-transport parity sweeps: any parameter that selects a different render mode of the same object (preview, version, revision, timestamp, deleted=true, draft, export) must be probed with a foreign object id.
  • Move/copy/pair operations check one side. Bulk media move validates the target category only; slice status/move APIs validate the article you name while the slice id points elsewhere. Rule: every operation with two locators (move, copy, link, attach, merge, re-parent) needs both endpoints authorized — force each side to a foreign object from a principal permitted on only one.
  • Backend-only XSS is a token-read primitive, price it at the victim's capability. The opener_link reflected XSS needs no auth at all beyond luring a media-permission user, reads the CSRF token, and completes as code execution when the victim is an admin (template/module edit). Stored value-attribute breakouts in form widgets (34c3) are the editor→admin carrier. In any CMS with an admin-side editor that can save executable templates, a backend XSS is RCE-adjacent — chain it explicitly in the report.

Validation workflow (authorized scope only)

Lab CMS, disposable instances, no production edits

Install a lab REDAXO (or map the same axes onto the target CMS in scope). All proofs below are marker/state-level: forge-check with a second disposable account, export a canary table you created, move only files you own. Never export real user/session tables, never uninstall live addons, never exercise the history-wipe GET against retained content.

  1. Token-state probe (lab): identify what the temporary-login token derives from; test the derivation with no session, an expired session, and a freshly-installed instance; compare your offline-computed token against a legitimately issued one. A match on any forced state = the critical class.
  2. Parameter-keyed guard battery: for each admin endpoint, replay every save/update/delete/func variant as a plain GET without token (browser-context lure in a lab, or two-browser CSRF harness). Record which selecting parameters skip the token check.
  3. Export-oracle probe: with the lowest feature-holder account, request export for a synthetic canary table first, then confirm the response shape for rex_user-class tables WITHOUT extracting live credentials — presence of rows/columns is the finding.
  4. Token-vs-role diff: list package/addon/config management endpoints; attempt each with a token held by a role that should not have it (demo with a demoted disposable admin).
  5. Render-mode foreign-object probe: request version/history preview parameters against an article your principal has no category/clang permission for; a 200 with body content is the finding.
  6. Two-sided move probe: two categories A (permitted) and B (restricted), two disposable media sets; attempt move B→A and slice operations pairing an A article with a B-owned child.
  7. Session-carrier lifecycle: change a lab account's password, then re-test the old session cookie and stay-logged-in cookie for survival (this is also the recovery-resilience check in your own report narrative).

Reporting checklist

  • Price the token forge by its endpoint (backend → template/module edit → code execution), not by its auth-bypass label.
  • For the CSRF legs, name the exact parameter asymmetry (save gated, FUNC_UPDATE not) — it preempts "the page has CSRF protection" triage dismissal.
  • For the export leg, state the table names that returned data and redact all values; never include live session ids or hashes in evidence.
  • Mixed fix bands (5.21.0/5.21.3/5.21.4/5.21.5) mean a "current" version can still be missing the newest band — fingerprint per leg, not by product version.

Related pages: Supermicro BMC wave (secret-state-forcing family), Sept 19 WP alternate-surface drift (guard-exists-but-not-on-executed-path), Sept 16 identity-provider authority page (session lifecycle and revocation gaps).