DiceCTF 2021: Write-up WebIDE Challenge
Table of Contents
tl;dr
- Unintended Solution: Cookie Path Restriction bypass using pop-up windows + JS Sandbox Escape
- Intended Solution: Service Workers + JS Sandbox Escape
#
Challenge Description
Work on JavaScript projects directly in your browser! Make something cool? Send it here.
Source Code: here
#
Analysis
We are given an online JavaScript editor and the aim is to get the cookie token (flag) of the admin.
The application does not allow it to be iframed except for /sandbox.html
. Also, we are not allowed to place an iframe inside /sandbox.html
.
|
|
There’s an /ide/login
end-point which sets the value of cookie token as flag if the username is admin
and password has matched with crypto.randomBytes(16).toString('hex')
. Also, the cookies is set only for path /ide
. This means that the cookie will be only available in /ide
end-point.
|
|
The admin has a feature to save and view saved JavaScript source codes. These end-points are protected by the cookie token which is the flag.
Moving on to the HTML source code we can see that the application is implemented with postMessage and a sandboxed iframe.
|
|
According to MDN Web Docs, if sandbox="allow-scripts"
is set, the frame can run JavaScript and cannot open pop-up windows. Looking at the implementation of sandbox at sandbox.js
, we see that it uses a safeEval
function. The function is invoked whenever the iframe recieves a postMessage. safeEval
function uses JavaScript Proxy to redefine operations on objects.
|
|
With the above implementation, certain methods become inaccessible directly. Here, the console object has been redefined. However, we have access to eval
function.
#
Unintended Solution
The first thing that we tried was to bypass the restrictions put through sandbox.js
. This came out easier than what we thought.
After some experimentation, we found that console.log("".constructor);
gives function String() { [native code] }
as the output. A quick Google search took us here. This was exactly what we were looking for.
We were able to get the payload working. console.log(''.sub.constructor('return btoa("hacker")')());
However, this too had a limitation. We weren’t allowed to access cookies. Trying to access resulted in a SecurityError: The operation is insecure.
This could be because the iframe was sandboxed. We now had to escape the iframe sandbox.
We quickly figured out a solution for this. We put up an iframe to sandbox.html
from another server without sandbox="allow-scripts"
attribute.
|
|
With the above exploit, we were able to read cookies that we set at web-ide.dicec.tf
. But the flag cookie was only available at /ide
. So the plan was to open a popup window at /ide
and read the cookie from it using sandbox.html
. The window took some time to load, so a setTimeout function was used to delay the redirect.
|
|
For some reason we got SyntaxError: missing ) after argument list
for the payload. We then tried encoding our payload into base64 and it worked. So, the final payload was
|
|
If the admin visits this page, a pop-up window at /ide
will be opened and then the admin gets redirected to an attack server with the cookies from /ide
as parameter.
Note: The above solution will only work in headless browsers. In normal browsers, window.open pop up will be blocked by default if there are is user interaction.
#
Intended Solution
We managed to solve the challenge during the CTF using the unintended solution. We came to know about the intended solution only after the CTF.
When the admin visits the attack server, a fetch request is made to /ide/save
end-point with a JavaScript file. The file gets saved due to the lack of CSRF protection. The newly created JS file when recieves a fetch request, sends back an HTML response containing a script to send cookie to the specified callback URL.
|
|
#
Flag:
|
|