This writeup is to help NKCyber Club Members walk through the steps for the first few solutions to the Sensitive Data Exposure section of OWASP Juice Shop. Similar articles include instructions for administering OWASP’s Juice Shop and last week’s XSS solutions.
Solutions
Confidential Document
Access a confidential document. (Difficulty Level: 1)
The description alone doesn’t give a lot to work with, so it’s worth taking a look at the first hint:
Here, we see a lot of routes defined on an Express.js router, with app.use.
For each line that we see app.use
, we can imagine we’re taking some part of the URL, and doing something special when we navigate to that path.
Let’s take a look at the first line, for example:
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.
Looking at the npm page for serve-index:
The
path
is based off thereq.url
value, so areq.url
of'/some/dir
with apath
of'public'
will look at'public/some/dir'
. If you are using something likeexpress
, you can change the URL “base” withapp.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**.
So, I went to /metrics
, and got the flag.
Looking through the code they provided in the hint, we can find the route defined on line 4.
See full code
```typescript /* Serve metrics */ let metricsUpdateLoop: any; const Metrics = metrics.observeMetrics(); app.get("/metrics", metrics.serveMetrics()); errorhandler.title = `${config.get( "application.name" )} (Express ${utils.version("express")})`;const registerWebsocketEvents = require(”./lib/startup/registerWebsocketEvents”); const customizeApplication = require(”./lib/startup/customizeApplication”);
export async function start(readyCallback: any) { const datacreatorEnd = startupGauge.startTimer({ task: “datacreator” }); await sequelize.sync({ force: true }); await datacreator(); datacreatorEnd(); const port = process.env.PORT ?? config.get(“server.port”); process.env.BASE_PATH = process.env.BASE_PATH ?? config.get(“server.basePath”);
metricsUpdateLoop = Metrics.updateLoop();
server.listen(port, () => {
logger.info(
colors.cyan(Server listening on port ${colors.bold(
{port}`)}`)
);
startupGauge.set({ task: "ready" }, (Date.now() - startTime) / 1000);
if (process.env.BASE_PATH !== "") {
logger.info(
colors.cyan(
`Server using proxy base path {colors.bold(
${process.env.BASE_PATH}
)} for redirects`
)
);
}
registerWebsocketEvents(server);
if (readyCallback) {
readyCallback();
}
});
}
export function close(exitCode: number | undefined) { if (server) { clearInterval(metricsUpdateLoop); server.close(); } if (exitCode !== undefined) { process.exit(exitCode); } }
</details>
```typescript
app.get("/metrics", metrics.serveMetrics());
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:
You can also use identify
from ImageMagick:
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.
— Null Byte Interaction Error (Poison Null Byte) - Martello Security
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:
Note that you will have to go back to the Juice Shop index to see your flag for this challenge.
You should see the flag for the Poison Null Byte challenge as well.
Forgotten Developer Backup
Using the exact same technique, we can simply do:
to complete this challenge.
Conclusion
Congratulations! 🎉
I hope you learned something new today.
Come back next week for our foreign exchange meeting, where we’ll be learning more about other injection techniques.
As always, feel free to reach out to me with feedback about this week’s lesson. Thanks! 😊