You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Now I am developing a blog service. I'm aware that there is a simple XSS. However, I introduced strong security mechanisms, named Content Security Policy and Trusted Types. So you cannot abuse the vulnerability in any modern browsers, including Firefox, right?
Writeup
Because there is a report feature so we know it's a challenge about XSS.
Apparently, we can control the theme query string to render whatever we want on the page, but we can't do much because of the strict CSP. We can't execute inline script, but at least there is one thing we know: we can inject html.
Then I played around with the website to find where I can do XSS, it's obviously that if we can control the callback params of api.php, we can execute arbitrary JavaScript code:
<?phpheader('Content-Type: application/javascript');
$callback = $_GET['callback'] ?? 'render';
if (strlen($callback) > 20) {
die('throw new Error("callback name is too long")');
}
echo$callback . '(' . json_encode([
["id" => 1, "title" => "Hello, world!", "content" => "Welcome to my blog platform!"],
["id" => 2, "title" => "Lorem ipsum", "content" => "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."]
]) . ')';
For example, api.php?callback=alert(1); will display a popup because the output is alert(1);([...]).
Manipulating the value of window.callback is simple, DOM clobbering works in this case.
But unfortunately it's still not working in the end because of the Trusted Types feature.
Actually I don't even know what is Trusted Types before this chall so I quickly google it and found that it's a new way to prevent DOM XSS, and the developer can write their own policy for filtering malicious payload.
For example, this call registers a default policy and block the url which includes callback and check if trustedTypes.defaultPolicy is successfully registered.
constinit=()=>{// try to register trusted typestry{trustedTypes.createPolicy('default',{createHTML(url){returnurl.replace(/[<>]/g,'');},createScriptURL(url){if(url.includes('callback')){thrownewError('custom callback is unimplemented');}returnurl;}});}catch{if(!trustedTypes.defaultPolicy){thrownewError('failed to register default policy');}}// TODO: implement custom callbackjsonp('/api.php',window.callback);};
For now even we can decide what is the value for window.callback, we can't pass it to api.php.
So our goal is quite clear, if we can bypass this check, we can do XSS. But how?
If you read the description carefully, you should know this problem must be something to do with Firefox because the description mention Firefox particularly.
At first I tried to google the keyword like: firefox trusted type vulnerability, firefox trusted type bug but got no luck.
Later on, I found a js file named trustedtypes.build.js, and from the source code we know it's from Google. But from the resources I read before, Trusted Types is a built-in feature so no need any js file, unless... it's not implemented yet.
If it's not implemented, we need a polyfill to simulate this feature. At that moment I know why this chall choose Firefox, because Firefox has no built-in support for trusted types yet so it needs a polyfill to work properly.
/** * Determines if the enforcement should be enabled. * @return {boolean} */functionshouldBootstrap(){for(constrootPropertyof['trustedTypes','TrustedTypes']){if(window[rootProperty]&&!window[rootProperty]['_isPolyfill_']){// Native implementation existsreturnfalse;}}returntrue;}// Bootstrap only if native implementation is missing.if(shouldBootstrap()){bootstrap();}
The check is common, only polyfill if there is no native support. Actually we can cheated to pretend it has native support via DOM clobbering.
Remember we can inject any HTML payload?
<aid="trustedTypes" />
So window['trustedTypes'] is truthy and bootstrap function won't be execute, there is no Trusted Types anymore!
But it's not finished yet, there is another check fortrustedTypes.defaultPolicy:
constinit=()=>{try{// ignore}catch{// we need to bypass thisif(!trustedTypes.defaultPolicy){thrownewError('failed to register default policy');}}jsonp('/api.php',window.callback);};
Easy, we can use form instead of a to create two-level DOM clobbering:
After bypass Trusted Types, we can execute arbitrary JavaScript via api.php.
But there is another issue, the length of callback can only be 20 char, how can I do XSS and get cookie within this limitation? document.cookie itself is already 15 characters.
I stuck here for so long, try to use eval or setTimeout but it has been block by CSP, for sure.
After trying like an hour, I realized that I can reuse the existed function: jsonp, oh it took me so long
Simple Blog
Description
Now I am developing a blog service. I'm aware that there is a simple XSS. However, I introduced strong security mechanisms, named Content Security Policy and Trusted Types. So you cannot abuse the vulnerability in any modern browsers, including Firefox, right?
Writeup
Because there is a report feature so we know it's a challenge about XSS.
Apparently, we can control the
theme
query string to render whatever we want on the page, but we can't do much because of the strict CSP. We can't execute inline script, but at least there is one thing we know: we can inject html.Then I played around with the website to find where I can do XSS, it's obviously that if we can control the
callback
params ofapi.php
, we can execute arbitrary JavaScript code:For example,
api.php?callback=alert(1);
will display a popup because the output isalert(1);([...])
.Manipulating the value of
window.callback
is simple, DOM clobbering works in this case.But unfortunately it's still not working in the end because of the Trusted Types feature.
Actually I don't even know what is Trusted Types before this chall so I quickly google it and found that it's a new way to prevent DOM XSS, and the developer can write their own policy for filtering malicious payload.
For example, this call registers a default policy and block the url which includes
callback
and check iftrustedTypes.defaultPolicy
is successfully registered.For now even we can decide what is the value for
window.callback
, we can't pass it toapi.php
.So our goal is quite clear, if we can bypass this check, we can do XSS. But how?
If you read the description carefully, you should know this problem must be something to do with Firefox because the description mention Firefox particularly.
At first I tried to google the keyword like:
firefox trusted type vulnerability
,firefox trusted type bug
but got no luck.Later on, I found a js file named
trustedtypes.build.js
, and from the source code we know it's from Google. But from the resources I read before, Trusted Types is a built-in feature so no need any js file, unless... it's not implemented yet.If it's not implemented, we need a polyfill to simulate this feature. At that moment I know why this chall choose Firefox, because Firefox has no built-in support for trusted types yet so it needs a polyfill to work properly.
You can find the original polyfill easily by googling, here is the snippet of the file: https://github.com/w3c/webappsec-trusted-types/blob/main/src/polyfill/full.js
The check is common, only polyfill if there is no native support. Actually we can cheated to pretend it has native support via DOM clobbering.
Remember we can inject any HTML payload?
So
window['trustedTypes']
is truthy andbootstrap
function won't be execute, there is no Trusted Types anymore!But it's not finished yet, there is another check for
trustedTypes.defaultPolicy
:Easy, we can use
form
instead ofa
to create two-level DOM clobbering:After bypass Trusted Types, we can execute arbitrary JavaScript via api.php.
But there is another issue, the length of callback can only be 20 char, how can I do XSS and get cookie within this limitation?
document.cookie
itself is already 15 characters.I stuck here for so long, try to use
eval
orsetTimeout
but it has been block by CSP, for sure.After trying like an hour, I realized that I can reuse the existed function:
jsonp
, oh it took me so longBy calling
jsonp
we can inject our own script instead of doing XSS directly on the page.But the url is still too long(unless you domain is quite short), how can we do?
DOM clobbering again!
The content of self-hosted file
payload.js
is quite simple:By chaining all the vulnerabilities above, mostly DOM clobbering, we can do XSS and get the cookie, which is the flag.
The text was updated successfully, but these errors were encountered: