# NukeViet: Multiple Anti-XSS Filter Bypasses Leading to Stored XSS in News Module

Two anti-XSS filter bypasses in NukeViet's Request class let a news-posting user store JavaScript that runs for any visitor, including admins.

- Payout: Research
- Severity: high
- Program: NukeViet
- Found by: g03m0n
- Disclosed: 2026-07-13
- CVE: CVE-2026-54064
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-465g-4q99-5x86
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-465g-4q99-5x86
- Tags: area:web domain:web impact:ato impact:integrity kind:research platform:ghsa sev:high tech:php technique:parser-differential technique:source-review vuln:xss

**Target:** PHP CMS news module HTML attribute and URI sanitizer (Request::filterAttr / unhtmlentities)
**Root cause:** filterAttr() only checks /^on/ after PHP trim(), which does not strip \x0C, so a form-feed prefix hides event handlers; unhtmlentities() strips hex tab 	 but not decimal 	, so javascript: URIs survive keyword regex.
**Steps:**
1. Authenticate as a user with news post permission.
2. Submit POST body field `bodyhtml` containing `<img src="x" %0Conerror="alert('XSS')">` (bypass 1) or a Markdown link `[Click me](jav	ascript:alert('XSS'))` (bypass 2).
3. Filter passes because \x0C is not trimmed and 	 is not stripped.
4. Payload stored in DB; browser parses \x0C as whitespace and decodes 	 into a working javascript: URI, executing on page view.
**How found:** Compared PHP trim()/str_ireplace behavior against HTML5 parser whitespace and entity decoding rules, spotting that \x0C and decimal entities were not normalized before the /^on/ and javascript: checks.
**Exploitation:** Persistent payload stored in a news article executes in the browser of any viewer including administrators, enabling session cookie theft, credential harvesting and privilege escalation.
**Escalation:** Low-privileged news poster escalates to admin-context script execution via stored XSS.
**Insight:** When auditing sanitizers, diff the language's trim/whitespace set and entity handling against the browser parser's: control chars like \x0C and decimal vs hex entities are classic gaps between filter and parser.

## Proof of concept (copied exactly from the original report)

**Form Feed prefix bypass payload in bodyhtml**

```
<img src="x" %0Conerror="alert('XSS')">
```

**Decimal entity tab javascript URI payload**

```
[Click me](jav&#9;ascript:alert('XSS'))
```

