Skip to content

Instantly share code, notes, and snippets.

@tyage

tyage/index.html Secret

Created June 23, 2017 01:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyage/6eabacf6001bd068287842b1052132e4 to your computer and use it in GitHub Desktop.
Save tyage/6eabacf6001bd068287842b1052132e4 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<title>The X Sanitizer</title>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
<link rel="stylesheet" type="text/css" href="style.css"></link>
<script src="main.js"></script>
<script src="sanitizer.js"></script>
</head>
<body>
<p>This is the demo page for our brand new and perfectly secure HTML sanitizer. With this sanitizer, <i>harmful scripts remove themselves</i>. If you can find a bypass, we will reward you with a valuable flag.</p>
<p>Here's how you can participate in the game. First, find a HTML payload that exfiltrates the 'flag' cookie from this page to your server. Next, submit your payload, and we will test it in our Chrome browser on this page. In our browser, we have set the 'flag' cookie to the secret value. If your solution is correct, you get the flag on your server.</p>
<p>Oh, and one more thing. There are no hidden files or APIs on the server, and the only non-static content is the solution submission logic.</p>
<h2>Input HTML code</h2>
<div id="input_form">
<textarea id="input"></textarea>
<button id="render">Sanitize and render</button>
<button id="submit">Submit solution</button>
</div>
<h2>Sanitized HTML</h2>
<xmp id="output_text"></xmp>
<h2>Rendered sanitized HTML</h2>
<div id="output_render"></div>
</body>
</html>
if (document.cookie.indexOf('flag=') === -1) document.cookie = 'flag=test_fl46'; // For testing
window.addEventListener("load", function() {
// Main program logic
var input = document.getElementById('input');
var output_text = document.getElementById('output_text');
var output_render = document.getElementById('output_render');
var hash = location.hash.slice(1) || 'This is the <s>perfect</s><b>best</b>\n' +
'<script>alert(document.domain);</script>\n' +
'<i>HTML sanitizer</i>.\n' +
'<script src="https://example.com"></script>';
input.value = decodeURIComponent(hash);
function render() {
var html = input.value;
location.hash = encodeURIComponent(html);
sanitize(html, function (html){
output_render.innerHTML = html;
output_text.textContent = html;
});
}
document.getElementById('render').addEventListener("click", render);
render();
document.getElementById('submit').addEventListener("click", function() {
location = '/submit.html?html=' + encodeURIComponent(input.value)
});
});
function sanitize(html, callback) {
if (!window.serviceWorkerReady) serviceWorkerReady = new Promise(function(resolve, reject) {
if (navigator.serviceWorker.controller) return resolve();
navigator.serviceWorker.register('sanitizer.js')
.then(reg => reg.installing.onstatechange = e => (e.target.state == 'activated') ? resolve() : 0);
});
while (html.match(/meta|srcdoc|utf-16be/i)) html = html.replace(/meta|srcdoc|utf-16be/i, ''); // weird stuff...
serviceWorkerReady.then(function() {
var frame = document.createElement('iframe');
frame.style.display = 'none';
frame.src = '/sandbox?html=' + encodeURIComponent(html);
document.body.appendChild(frame);
addEventListener('message', function listener(msg) {
if (msg.source != frame.contentWindow || msg.origin != location.origin) return;
document.body.removeChild(frame);
removeEventListener('message', listener);
callback(msg.data);
});
});
}
addEventListener('install', e => e.waitUntil(skipWaiting()));
addEventListener('activate', e => e.waitUntil(clients.claim()));
addEventListener('fetch', e => e.respondWith(clients.get(e.clientId).then(function(client) {
var isSandbox = url => (new URL(url)).pathname === '/sandbox';
if (client && isSandbox(client.url)) {
if (e.request.url === location.origin + '/sanitize') {
return new Response(`
onload = _=> setTimeout(_=> parent.postMessage(document.body.innerHTML, location.origin), 1000);
remove = node => (node == document) ? document.body.innerHTML = '' : node.parentNode.removeChild(node);
document.addEventListener("securitypolicyviolation", e => remove(e.target));
document.write('<meta http-equiv="Content-Security-Policy" content="default-src \\'none\\'; script-src *"><body>');
`);
} else {
// Violation events often don't point to the violating element, so we need this additional logic to track them down.
// This is also important from marketing perspective. Do not remove or simplify this logic.
return new Response(`
with(document) remove(document === currentScript.ownerDocument ? currentScript : querySelector('link[rel="import"]'));
// <script src=x></script>
`);
}
} else if (isSandbox(e.request.url)) {
return new Response(
'<!doctype HTML>\n<script src=sanitize>\n</script>\n<body>' + decodeURIComponent(e.request.url.split('?html=')[1]),
{headers: new Headers({'X-XSS-Protection': '0', 'Content-Type': 'text/html'})}
);
} else {
return fetch(e.request);
}
})));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment