CVE-2026-54064: NukeViet CMS Multiple Anti-XSS Filter Bypasses Leading to Stored XSS
Two input-filter flaws in NukeViet CMS let any authenticated news author permanently inject JavaScript into articles, putting every site visitor's session and data at risk.
The problem
NukeViet CMS versions before 4.6.00 contain two independent filter-bypass paths in `NukeViet\Core\Request` (`vendor/vinades/nukeviet/Core/Request.php`).
The first flaw is in `filterAttr()`. It blocks event-handler attributes with a `/^on/i` regex, but PHP's `trim()` does not strip the ASCII Form Feed character (`\x0C`). Prefixing an attribute name with `\x0C` makes the regex miss it, while browsers treat the character as whitespace and still fire the handler.
The second flaw is in `unhtmlentities()`. It stripped the hex tab entity `	` but not the decimal equivalent `	`. The keyword-blocking regex uses `\s*` between letters, which does not match HTML entities, so `jav	ascript:` passes the filter and the browser decodes it into a working `javascript:` URI.
Any account with news-posting permission can exploit either path to store JavaScript that executes for every visitor, including administrators, enabling session theft, credential harvesting, and privilege escalation.
Proof of concept
A working proof-of-concept for CVE-2026-54064 in nukeviet/nukeviet, with the exact payload below.
<!-- Bypass 1: Form Feed (\x0C / %0C) prefix on event handler attribute -->
<!-- Submit via POST body field: bodyhtml -->
<img src="x" onerror="alert('XSS')">
<!-- URL-encoded equivalent in a POST body: -->
bodyhtml=<img+src%3D"x"+%0Conerror%3D"alert('XSS')">
<!-- Bypass 2: Decimal HTML entity tab (	) inside javascript: URI -->
<!-- Submit as a link inside article body -->
<a href="jav	ascript:alert('XSS')">Click me</a>Bypass 1 works because `filterAttr()` matched attribute names against `/^on/i` without first stripping non-printing ASCII control characters. The Form Feed byte (`\x0C`, U+000C) is not in PHP's default `trim()` character set, so `\x0Conerror` passes the regex.
HTML5 parsers treat `\x0C` as a whitespace separator, so the browser still recognizes and fires the event handler.
Bypass 2 works because `unhtmlentities()` used `str_ireplace` to remove only `	` (hex) but missed `	` (decimal) for the tab character. The `javascript:` keyword-blocking regex relied on `\s*` between characters, which matches literal whitespace but not HTML entities.
After the filter passed the encoded URI, the browser decoded `	` back to a real tab, completing a valid `javascript:` scheme.
The fix (4.6.00) addresses both: `filterAttr()` now strips all bytes in `\x00-\x20` from the attribute name before the `/^on/` check (`preg_replace('/[\x00-\x20]/', '', strtolower($attrSubSet[0]))`), and `unhtmlentities()` now also strips decimal HTML entities for all ASCII control characters 0-31 (`preg_replace('/�*(?:3[01]|[12][0-9]|[0-9]);/', '', $value)`).
The fix
Upgrade `nukeviet/nukeviet` to version 4.6.00 or later. There is no workaround for earlier versions. After upgrading, verify that `vendor/vinades/nukeviet/Core/Request.php` contains both the control-character strip in `filterAttr()` and the decimal-entity strip in `unhtmlentities()`.
Reported by g03m0n (WhiteHub #4521).
Related research
- high · 8.2CVE-2026-48118CVE-2026-48118: NukeViet Unauthenticated Reflected XSS via Base64-Encoded Comment Status Parameter
- high · 8.7CVE-2026-49259CVE-2026-49259: NukeViet CMS Stored XSS via Comment Reply Handler
- high · 8.7CVE-2026-54065CVE-2026-54065: NukeViet Path Traversal to Arbitrary File Deletion
- high · 7.2CVE-2026-55372CVE-2026-55372: NukeViet Pre-auth SSRF via X-Forwarded-Host