This writeup is to help NKCyber Club Members walk through the steps for the first 5 solutions to the XSS section of OWASP Juice Shop. See this page for instructions on administering OWASP’s Juice Shop.
Solutions
Score Board
This challenge is a prerequisite for all others, as this challenge ensures you are able to see the score board.
Note that there are many challenges available on this platform, but we are only doing a small selection today.
Your first view of this wonderful website should be something like this:
There are so many juice products that we could buy. However, beyond this innocent first page, this is secretly a platform for honing your hacking expertise.
If you look at the URL, you should see something like http://localhost:3000/#/
, ending with a #
. This gives us an important piece of information about the way the website is built: they must be using a Single Page App framework.
Anything that follows a #
in a URL is not part of the server path, but is rather a ‘fragment identifier’, originally intended to jump to a specific element id in your webpage.
However, a certain age of web frameworks developed before the browser history API was standardized rely on taking the data from the fragment identifier, and routing you to the correct page on the client. As such, we know that this Juice Shop must have a list of routes that you can access shipped to the client, so that the web framework knows what to do with any data that comes after the #
. (For reference, Juice Shop is running Angular 9.)
As such, we can open up the developer tools with Right Click -> Inspect
(or however your prefer), and then we can navigate to the sources tab.
From there, we can search for files that specify paths to navigate to:
From there, we can learn about some paths that might not have been intended to view:
From there, we can navigate to /#/score-board
to view the secret score board and get the flag.
It will now be listed in your sidebar for easy access.
DOM XSS
First, let’s get on the same page with some definitions from the creators of the Juice Shop:
Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser side script, to a different end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user within the output it generates without validating or encoding it.
DOM Based XSS […] is an XSS attack wherein the attack payload is executed as a result of modifying the DOM “environment” in the victim’s browser used by the original client side script, so that the client side code runs in an “unexpected” manner. That is, the page itself (the HTTP response that is) does not change, but the client side code contained in the page executes differently due to the malicious modifications that have occurred in the DOM environment.
So, we know that we’re going to have to:
- Give the web framework a request with a payload
- Get make it directly insert that payload into the webpage, and execute it.
Let’s take a look at the most accessible text entry field, the search bar:
When we search for something, the result is prominently displayed at the top of the screen:
So, the server will take whatever we text give it, and insert it into the page. Let’s see if we can give it HTML tags, with something like:
Woah, would you look at that. We can enter in raw HTML tags.
Now, we just need to enter in the code indicated in the problem description:
Bonus XSS
In a very similar solution, you should be able to enter the specified code:
into the search bar:
and see the result pop up on the page to get the flag:
The takeaway here is that iframe
elements can be used to load dynamic content, potentially including malicious scripts.
Troubleshooting:
If you’re not receiving the flag, ensure you’re navigating to the correct url in the iframe. You might want to directly copy this from the Juice Shop Score Board.
Reflected XSS Attack
Reflected attacks are those where the injected script is reflected off the web server, such as in an error message, search result, or any other response that includes some or all of the input sent to the server as part of the request. Reflected attacks are delivered to victims via another route, such as in an e-mail message, or on some other website. When a user is tricked into clicking on a malicious link, submitting a specially crafted form, or even just browsing to a malicious site, the injected code travels to the vulnerable web site, which reflects the attack back to the user’s browser. The browser then executes the code because it came from a “trusted” server. Reflected XSS is also sometimes referred to as Non-Persistent or Type-I XSS (the attack is carried out through a single request / response cycle).
So, we know that we’re going to have to:
- Give the server a request with a payload
- Get a response that directly inserts that payload into the webpage, and executes it.
Note that we don’t need the server to actually store the data.
I don’t know the solution yet, so let’s just try a few injection locations to start.
Let’s create an account at /#/login
. Then, we navigate to /#/register
, because we’re not a customer.
Now that we’re logged in, we can add items to our basket:
Now, we can review products:
And it’ll show up!
Can you do HTML formatting?
Unfortunately not :(
At this point, I checked out this solution on GitHub, and found that you need to order an item in the Juice Shop. I’ve added this as a hint for the competition, because I think there’s a lot of surface area on the application, that makes this attack vector less than obvious.
Basically, you need to add an item to your cart.
You’ll be prompted to add an address:
And then you can add your payment information:
Now we can place our order:
And we’ll see that the “Thank you for your purchase screen” is at a URL with a tracking ID: /#/order-completion/3f7b-e60171eddcbd36a5
.
From there, we can navigate to /#/track-result/new?id=3f7b-e60171eddcbd36a5
, and see that the text is reflected into the page:
Navigating to /#/track-result/new?id=<i>test</i>
shows us text in italic, so that’s a good indication that the input is not sanitized. Entering the payload:
into the URL: /#/track-result/new?id=%3Ciframe%20src=%22javascript:alert(%60xss%60)%22%3E
Gives us the flag. (Note this URL is represented using URL encoded characters. Your browser will likely do this automatically.)
API Only XSS
So, now we need to perform a persisted XSS attack.
Stored attacks are those where the injected script is permanently stored on the target servers, such as in a database, in a message forum, visitor log, comment field, etc. The victim then retrieves the malicious script from the server when it requests the stored information. Stored XSS is also sometimes referred to as Persistent or Type-II XSS.
And, the free hint for this problem is an Angular hook.
View full code
So, we can see that the HTML is trusted in the table description by trustProductDescription
. This is a good place to attack.
Looking at the page /#/order-history
, we search for /api
in the main.js
sources, and we can find that /api/Products
is where all of the products come from.
I personally solved this using cURL, as shown below, but I also saw people use Burp Suite, which looked pretty powerful. That’s definitely something I need to practice more. John Hammond has a good introduction to Burp Suite on YouTube.
So, I first got the results of the endpoint:
ok, looks good. Can we post to it?
I assume the data will take the form:
So we get the command:
We get back a response that includes:
<em>401</em> UnauthorizedError: No Authorization header was found</h2>
At this point, we get the flag for “Error Handling (Provoke an error that is neither very gracefully nor consistently handled.)”. If only that’s what I wanted.
We can look in our network tab under “headers”, and see the authorization header that we’re currently using:
We see that we have something like “Authorization: Bearer {token}”
We can copy this into our cURL command, and add a header just like our --header "Content-Type: application/json"
:
and we get back something like:
So, now we can enter the payload into the description (remembering to escape the quotes):
<iframe src="javascript:alert(`xss`)">
And we get our flag.
Conclusion
These are the solutions for the first 5 problems out of 10. If I have more time, I’ll solve the other 50% on my own.
If you’re looking for solutions, you might want to check out these writeups: