Why this research?
I stumbled upon this tweet by ixSly about a vulnerability in a code snippet found in Outline
You can try to figure out the vulnerability here by yourself:
|
|
Github Gist JSONP endpoint
I thought this is some bypass to “escape” the src
quote of script
. Found out the hard way that it is in fact not the way to solve due to CSP. Inputting the CSP into Google CSP Evaluator shows that there is no bypass.
The writeup mentions using another site https://cspbypass.com to check for possible CSP bypasses. Sure enough, there is two endpoints we can take advantage of.
I cannot find any documentations from Github that mentions this secret callback
endpoint. The cspbypass
page provides an example payload, which got me wondering whether it is “needed” to use that specific JSON file
|
|
After spending some time trying to figure out the structure of the JSON file, I did not find anything interesting. Desparate, I tried to host my own gist with gibberish content (the .json
extension turns out to be not really related):
I can access my Gist using a link like this https://gist.github.com/IceWizard4902/447312912b1ff7fc88abfd66067e16e0. If I append .json
extension to the link, e.g https://gist.github.com/IceWizard4902/447312912b1ff7fc88abfd66067e16e0.json, then the resultant output is a bit different, but nothing interesting.
However, when I try adding the callback
parameter to the link, e.g https://gist.github.com/IceWizard4902/447312912b1ff7fc88abfd66067e16e0.json?callback=alert, then suddenly the output returned resembles a JSONP endpoint. Even the content type is set to be application/javascript
So we don’t need the specific JSON file from renniepak
to have a JSONP endpoint. Rather, the .json
append trick + callback
parameter will work on any gist.
However, from my quick testing, this JSONP endpoint seems to be quite restrictive. We cannot have opening and closing brackets in the callback
parameter. We can specify any function call though, but the 1st parameter is limited to the JSON structure from Github. There might be some ways to bypass this limitation though.
How to solve the challenge
Here’s the link to the solution of the author. They use Shazzer to solve this challenge alongside the Github JSONP endpoint above. Their payload is the following:
|
|
Looking at this, I have some questions.
-
Why are the “special” URL characters encoded as HTML entities? This is to make the
new URL()
call treat the content followingixSly
as the secondpathname
. -
Can I use other HTML encoded characters, i.e HTML encoded hex? This unfortunately will not work. Say if we want to encode
?
as/
or any other HTML encoded string with#
, the content following the#
will be treated as the hash of the URL. Hence, theuserUrl.pathname.split("/")
will skip the hash portion of the URL. -
Can we make this payload shorter? I think so (?) Here is my own payload
|
|
which essentially does the same trick. The &
is a bit redundant, because the &
is still treated a part of the pathname
when ?
is missing from the URL. Probably there is a reason why the author has to encode /
, ?
and &
character as HTML entity.