The app (defined elsewhere) will match all routes starting with /ftp, and then serve an index of all of the files to the user. Then, it will call some function serveIndex with the parameter "ftp". We can look up that function, and find where it came from.
The path is based off the req.url value, so a req.url of '/some/dir with a path of 'public' will look at 'public/some/dir'. If you are using something like express, you can change the URL “base” with app.use (see the express example).
So, great! Let’s try going to /ftp on the server, and seeing what happens.
Woah, neat! It creates an index of all of the files on the FTP server.
Some of them are pretty boring. Here’s legal.md, for example:
# Legal Information
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
... [document continues]
However, one document in particular stands out, acquisitions.md:
# Planned Acquisitions
> This document is confidential! Do not distribute!
... [document continues]
This seems like the confidential document.
Once we have requested this file, we can return to the home page at /.
From there, we should get our flag for the challenge Confidential Document.
Exposed Metrics
Find the endpoint that serves usage data to be scraped by a popular monitoring system. (Difficulty Level: 1)
Looking at the problem description, I saw that there was a link to the Prometheus monitoring tool, which I had never heard of before.
I searched “where is prometheus default endpoint” on Google, and got the featured snippet:
Starting Prometheus
You can also verify that Prometheus is serving metrics about itself by navigating to its own metrics endpoint: http**://localhost:9090/metrics**.
Once we see that the server routes to that path, we know we can visit it.
From there, we can navigate back to the homepage to get the flag.
Meta Geo Stalking
Determine the answer to John’s security question by looking at an upload of him to the Photo Wall and use it to reset his password via the Forgot Password mechanism. (Difficulty Level: 2)
To start this challenge, I went to the Photo Wall:
Looking through the gallery of happy juice connoisseurs, we stumble upon a post by j0hNny, who very well might be the John from the problem description:
Okay, so let’s go to the Forgot Password page, and see what information we need to find from this image.
First, we can enter John’s email: john@juice-sh.op.
It looks like John has selected “What’s your favorite place to go hiking?” as his security question, and then conveniently posted a picture of him going hiking.
Now we just need to figure out where this photo was taken.
Unfortunately, Rainbolt seems to be too busy to figure this one out, and I’m not very good at GeoGuessr. We’re going to have to inspect the image he’s posted more carefully.
Luckily, there’s more to an image than just the visuals. Many common file types (including .jpg, .png, .webp, and more) include something called Exif data which stores additional information about the image, including camera settings, image metrics, date and time information, and potentially the geolocation where the image was taken.
Using your provided Kali VM, you may note that exiv2 is installed, which is a special tool for managing image metadata.
You can use it to analyze more information about the image John posted:
$ # First, ensure that exiv2 is installed$ which exiv2/usr/bin/exiv2$ # Then, download John's image$ wget http://localhost:3000/assets/public/images/uploads/favorite-hiking-place.png[...output ommitted...]$ # Finally, get all of the Exif data from the image$ exiv2 -g GPS favorite-hiking-place.pngExif.Image.GPSTag Long 1 50Exif.GPSInfo.GPSVersionID Byte 4 2.2.0.0Exif.GPSInfo.GPSLatitudeRef Ascii 2 NorthExif.GPSInfo.GPSLatitude Rational 3 36deg 58' 0"Exif.GPSInfo.GPSLongitudeRef Ascii 1 WestExif.GPSInfo.GPSLongitude Rational 3 84deg 21' 0"Exif.GPSInfo.GPSMapDatum Ascii 6 WGS-84
$ # ensure identify is installed$ which identify/usr/bin/identify$ identify -verbose favorite-hiking-place.png | grep GPS exif:GPSInfo: 50 exif:GPSLatitude: 36/1, 57523/1000, 0/1 exif:GPSLatitudeRef: N exif:GPSLongitude: 84/1, 20893/1000, 0/1 exif:GPSLongitudeRef: W exif:GPSMapDatum: WGS-84 exif:GPSVersionID: ....
Either way works, and it gives us the coordinates 36°58'0"N 84°21'0"W (view on Google Maps).
This is part of Daniel Boone National Forest. (Super beautiful place btw. I can totally recommend it.)
I got lucky that John had the same taste in capitalization as Google Maps, but I imagine there are quite a number of possible locations that one might want to enter.
Either way, this is a good reminder of how security questions rely on information that can be deduced or leaked, and are not very secure.
Also, it’s important to automatically strip exif location data from user images before hosting them publicly. Almost all social media websites do this automatically.
Poison Null Byte
Bypass a security control with a Poison Null Byte to access a file not meant for your eyes. (Difficulty Level: 4)
From the linked definition:
A null byte (NUL character) can have different meanings across representations or languages. For example, it is a string terminator in standard C libraries, but Perl and PHP strings do not treat it as a terminator. When two representations are crossed - such as when Perl or PHP invokes underlying C functionality - this can produce an interaction error with unexpected results. Similar issues have been reported for ASP. Other interpreters written in C might also be affected.
The poison null byte is frequently useful in path traversal attacks by terminating hard-coded extensions that are added to a filename. It can play a role in regular expression processing in PHP.
Because basically, when we go to a URL, we are requesting a resource with a certain name. Whether we can see that resource depends on if it ends in .md or .pdf. So, the null byte is a tool we can use to create a separation between the resource we’re requesting and what the string ends with, such that we can bypass checks for file extensions.
Here are two examples:
Forgotten Sales Backup
Access a salesman’s forgotten backup file. (Difficulty Level: 4)
It’s great to keep backups! It’s not great for random people to be able to steal your backups.
This begins similarly to the Confidential Document challenge, shown above.
Let’s look back at our ftp server:
In particular, we’re looking for a sales backup.
Out of all the files here, it looks like two could be backups:
coupons_2013.md.bak
package.json.bak
Of those, the package.json.bak doesn’t look like something related to a salesperson, so let’s take a look at coupons_2013.md.bak.
Ah, it looks like we’re not allowed to view .bak files. So, we somehow need to convince the server that we’re accessing a .md or .pdf file, when we’re actually requesting the .md.bak file.
Using the technique from the Poison Null Byte challenge, we can start by identifying the resource we want to download. In this case, that’s /ftp/coupons_2013.md.bak.
Now, we append a file extension that is allowed, and join it with a null byte: /ftp/coupons_2013.md.bak%00.pdf
Next, we URL encode the percent sign: /ftp/coupons_2013.md.bak%2500.pdf.
Finally, we can download the file to get our flag: