Hack a Web App with OWASP Juice Shop
Exploit real SQL Injection, XSS, and access control flaws in a safe lab.
Introduction
30 Second Summary
Every time you log in to a website, search for a product, or fill out a form, you trust that the application handles your data safely. Attackers exploit that trust by turning everyday features like login forms and search bars into doorways for stealing data.
In this project, you will hack a deliberately vulnerable web application called OWASP Juice Shop. You will exploit real-world vulnerabilities across SQL Injection, Cross-Site Scripting, and Broken Access Control using nothing but your browser's Developer Tools.
What You'll Build
You will run OWASP Juice Shop locally using Docker, then systematically exploit vulnerabilities from the OWASP Top 10 while tracking your progress on a built-in Score Board.
By the end of this project, you'll have:
- A locally running OWASP Juice Shop instance you can safely attack and learn from.
- Solved hacking challenges across three OWASP Top 10 categories: Injection, XSS, and Broken Access Control.
- A completed Score Board tracking your successful exploits and proving hands-on security experience.
- Secret Mission: Extract user credentials from the database using advanced SQL Injection.
Do I need any hacking experience?
No prior security or hacking experience is needed. This project walks you through each attack step by step, using only your browser and Docker Desktop.
Launch OWASP Juice Shop
OWASP Juice Shop is a deliberately insecure web application built for security training. It contains real vulnerabilities that you can safely exploit on your own machine without affecting anyone else.
In this step, you'll use Docker to download and run Juice Shop locally. Then you'll open Chrome DevTools to start inspecting the app's network traffic.
In this step, get ready to:
- Pull and run the OWASP Juice Shop Docker container.
- Explore the Juice Shop storefront in Google Chrome.
- Open Chrome DevTools to inspect network traffic.
Pull and run the Juice Shop Docker container
Docker lets you run applications inside isolated containers. You'll download the official Juice Shop image and start it as a container on your machine.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal and press Enter to open it.
- Download the Juice Shop Docker image by running this command:
docker pull bkimminich/juice-shop
You should see download progress for each image layer. The pull is complete when you see a line starting with Status: Downloaded newer image for.
Pull command not working?
- Make sure Docker Desktop is running. Look for the Docker whale icon in your menu bar.
- If you see a network error, check your internet connection and try the command again.
- Help me troubleshoot this Docker pull error.
- Start the Juice Shop container by running this command:
docker run --rm -p 127.0.0.1:3000:3000 bkimminich/juice-shop
What does this command do?
- --rm automatically removes the container when you stop it. No leftover containers to clean up.
- -p 127.0.0.1:3000:3000 maps port 3000 on your machine to port 3000 inside the container. This is how you'll access the app in your browser.
- bkimminich/juice-shop is the official Juice Shop image you just downloaded.
Wait for the startup logs to finish. The container is ready when you see output indicating the server is listening.
Why is my terminal stuck?
The terminal is not stuck. The container runs in the foreground, so your terminal will display the server's logs. Leave this terminal window open for the rest of the project.
Seeing a port conflict error?
- If something else is already using port 3000, you'll see an error about the address being in use. Close any other application running on that port and try the command again.
- If Docker Desktop is not running, the command cannot connect to the Docker engine. Open Docker Desktop first.
- Help me fix this Docker run error.
Explore the Juice Shop storefront
Before you start hacking anything, you need to understand how the application works. Exploring the app like a normal user is your first act of reconnaissance.
- Open Google Chrome and navigate to http://localhost:3000.
- You should see the Juice Shop storefront displaying various juice products.
- Browse the app like a normal user. Check the navigation bar, open a few product pages, and click on the Account menu to see Login and Registration options.
Why explore first?
Reconnaissance is the first phase of any penetration test. You are mapping out the application's features, navigation, and user flows. Every page, form, and button is a potential entry point for an attack.
As you browse, you are building a mental model of how the app works. This helps you identify where vulnerabilities might hide.
Not seeing the storefront?
- Check that your terminal is still showing the Juice Shop logs. If you closed the terminal, the container stopped. Run the docker run command again.
- Make sure the URL is exactly http://localhost:3000 with no trailing slash or extra characters.
- Help me get Juice Shop loading in my browser.
Open Chrome DevTools
Chrome DevTools is your primary hacking instrument for this project. It lets you inspect every network request the app makes, revealing the API endpoints you will target.
- Press Cmd+Option+I (macOS) or Ctrl+Shift+I (Windows) to open Chrome DevTools.
- Click the Network tab at the top of the DevTools panel.
- Navigate around the app. Click on products, open different pages, and watch the network requests appear in the panel.
Notice the API calls being made to endpoints under /api/ and /rest/. These API endpoints are the attack surface you will target throughout this project.
What am I looking at?
Each row in the Network tab represents an HTTP request the app makes. Click any row to see the full request URL, headers, and response data.
The /api/ and /rest/ endpoints reveal how the app communicates with its server. Understanding this traffic is how you will find vulnerabilities to exploit.
- Keep DevTools open for the rest of the project.
Not seeing network requests?
- Make sure you opened DevTools before navigating. DevTools only records requests that happen while the Network tab is open. Try refreshing the page after opening DevTools.
- Check that the filter bar at the top of the Network tab is not filtering out requests. Clear any active filters.
- Help me see network requests in Chrome DevTools.
Your hacking lab is live. Next up, you'll find a hidden page that tracks your progress through every challenge you solve.
Find the Hidden Score Board
Your Juice Shop is running and you have Chrome DevTools open to observe its network traffic. Now it is time to start hacking.
Every vulnerability you exploit in Juice Shop is tracked on a hidden Score Board. Your first challenge is to find it by digging through the application's JavaScript source code.
In this step, get ready to:
- Discover the hidden Score Board by searching the app's JavaScript source files.
- Explore the challenge categories and filter by difficulty level.
- Conduct deeper reconnaissance on the app's API structure and session management.
Discover the Score Board
The Score Board is a hidden page that tracks your hacking progress across all 113 challenges. Finding it is actually the very first hacking challenge in Juice Shop.
- In Chrome DevTools, click the Sources tab.
- Press Cmd+Option+F (macOS) or Ctrl+Shift+F (Windows) to open the Search panel.
- Type score-board into the search field and press Enter.
- Browse the search results and look for a route path that maps to the Score Board component.
- Once you find the route path, type http://localhost:3000/#/score-board into your browser's address bar and press Enter.
Why search the JavaScript files?
Juice Shop is an Angular application. Its route definitions (the URL paths that load each page) are bundled into the JavaScript files the browser downloads.
Searching these files reveals hidden pages that are not linked anywhere in the navigation menu. This is a common reconnaissance technique used by penetration testers to discover unprotected endpoints.
The Score Board page loads, displaying a grid of all hacking challenges organized by difficulty and category.
Don't see the Score Board?
Make sure you include the # in the URL. Juice Shop uses hash-based routing, so the path is /#/score-board, not just /score-board.
If you cannot find the route path in the search results, try searching for just score instead. Click through the matching results until you find one that looks like a URL path definition.
Help me find the Score Board route in Juice Shop's JavaScript files.
Explore the Score Board and understand challenge structure
The Score Board displays all 113 challenges organized by difficulty (1 to 6 stars) and by vulnerability category.
- Use the difficulty filter to show only 1-star and 2-star challenges. These are the challenges you will tackle in this project.
- Notice the category tags on each challenge. Categories like Injection, XSS, and Broken Access Control map directly to the OWASP Top 10 vulnerabilities.
- Look for challenges with a graduation cap icon. These have built-in tutorial scripts that provide step-by-step guided hints.
- Check that the "Score Board" challenge itself is now marked as solved with a green checkmark. Finding the Score Board was the challenge.
What are these vulnerability categories?
The OWASP Top 10 is a standard ranking of the most critical web application security risks. It is maintained by the OWASP Foundation and updated every few years based on real-world breach data.
Each Juice Shop challenge is tagged with the OWASP Top 10 category it belongs to. In this project, you will focus on three of them: Injection, XSS, and Broken Access Control.
Conduct deeper reconnaissance
Before launching any attacks, real penetration testers spend time understanding an application's architecture, APIs, and data flows. This reconnaissance phase helps you identify where vulnerabilities are most likely to exist.
- Switch back to the Network tab in Chrome DevTools.
- Browse the app further. Click on individual products, add items to your basket, and interact with the Account menu.
- Click on individual network requests in the panel and inspect the response data. Pay attention to REST API calls to endpoints under /api/ and /rest/.
- Check the request headers for authentication tokens or session cookies. These reveal how the app tracks logged-in users.
Why does reconnaissance matter?
The API endpoints you just observed are the attack surface you will target throughout this project. Knowing which endpoints exist, what data they return, and how authentication is handled gives you a map for your attacks.
This mirrors what professional penetration testers do. They never jump straight to exploits. Understanding the system first makes every attack more targeted and effective.
You have found the Score Board and mapped out the application's attack surface. Next up, you will exploit your first major vulnerability by using SQL Injection to bypass the login page and gain admin access.
SQL Injection - Bypass Login
You've uncovered the hidden Score Board and mapped out Juice Shop's API endpoints using Chrome DevTools. That reconnaissance gave you a map of the application's attack surface.
Now it's time for your first real attack. SQL Injection lets attackers manipulate database queries by injecting malicious input through ordinary form fields. In this step, you'll target the login page to bypass authentication entirely and log in as the admin.
In this step, get ready to:
- Probe the login form to identify a SQL Injection vulnerability.
- Exploit the vulnerability to log in as the admin user without knowing the password.
- Learn the defense that prevents SQL Injection attacks.
Attempt a normal login and probe for vulnerabilities
Before launching an attack, penetration testers probe for weaknesses. A common technique is to enter special characters into form fields and watch how the application responds.
- Navigate to the Login page by entering http://localhost:3000/#/login in your browser's address bar.
- In the Email field, type test@test.com.
- In the Password field, type test.
- Click Log in.
You should see the error message Invalid email or password. This is the application's normal response to bad credentials.
- Clear the Email field and type a single quote character '.
- Type anything in the Password field.
- Click Log in again.
- Switch to the Network tab in DevTools and compare the server response for this attempt with the previous one.
If the server returns a different error (like a 500 status code instead of the normal login failure), that's a strong signal the input reached the database without any filtering.
Why does a single quote matter?
A single quote is a special character in SQL. It marks the start and end of text strings in database queries.
If the application passes your input directly into a query, a lone single quote breaks the query's syntax and causes a database error. A different error response from the server tells you the input was not filtered before reaching the database.
Exploit the login form with SQL Injection
The application builds its database query by inserting whatever you type directly into the query string. Here is what that query looks like behind the scenes:
SELECT * FROM Users WHERE email = 'your_email_input' AND password = 'your_password_input'
What does this query do?
This query searches the Users table for a row where both the email and password match what you typed. The problem is that your input is dropped directly into the query string without any filtering.
If you type SQL syntax instead of a normal email address, the database treats your input as part of the query itself. That means you can change how the query behaves.
When you type ' OR 1=1-- as the email, the query becomes:
SELECT * FROM Users WHERE email = '' OR 1=1--' AND password = 'anything'
How does the injection work?
- The ' closes the email string in the original query.
- OR 1=1 makes the entire WHERE condition always true, so the query matches every user in the database.
- -- is a SQL comment that discards everything after it, including the password check.
The database returns all users, and the application logs you in as the first one in the table: the admin.
- Go back to the Login page at http://localhost:3000/#/login.
- In the Email field, type ' OR 1=1--.
- In the Password field, type anything.
- Click Log in.
You should now be logged in as admin@juice-sh.op. You can confirm this by looking at the account name displayed in the navigation bar.
Still seeing the login page?
- Make sure you typed the payload exactly as shown: ' OR 1=1--. The single quote at the very beginning is essential.
- Check that you typed the payload in the Email field, not the Password field.
- Confirm your Juice Shop container is still running. If the page is unresponsive, restart the container with the docker run command from Step 1.
Why can't I log in as admin using SQL Injection in Juice Shop?
Understand the defense
This attack works because the application concatenates user input directly into SQL queries. Your input is treated as executable code instead of plain data.
What are parameterized queries?
The fix for SQL Injection is called parameterized queries (also known as prepared statements). Instead of inserting user input directly into the query string, the database engine receives the query structure and the user data separately.
No matter what you type, parameterized queries always treat your input as a data value to match against. Your input can never alter the query's logic.
- Navigate to the Score Board at http://localhost:3000/#/score-board.
- Confirm that the Login Admin challenge (Injection, 2 stars) is marked as solved with a green checkmark.
Why is SQL Injection so important?
SQL Injection has been responsible for some of the largest data breaches in history. It is consistently ranked as one of the most critical web application vulnerabilities in the OWASP Top 10.
Understanding this single vulnerability, both how to exploit it and how to prevent it, is foundational knowledge for any security role.
You just exploited your first vulnerability and logged in as the admin without knowing the password. Next up, you'll inject malicious scripts into the application using Cross-Site Scripting (XSS).
Cross-Site Scripting (XSS)
In the last step, you bypassed Juice Shop's login with SQL Injection and gained admin access. That attack targeted the server's database layer.
Now you will target the other side of the application: the browser. Cross-Site Scripting (XSS) attacks inject malicious scripts into web pages, letting attackers execute code inside other users' browsers. In this step, you will turn the search bar and URL bar into attack vectors.
In this step, get ready to:
- Perform a DOM XSS attack through the search bar.
- Perform a Reflected XSS attack through a crafted URL.
- Understand the defenses against XSS vulnerabilities.
Perform a DOM XSS attack
XSS attacks inject malicious scripts into web pages that other users visit. There are three types: DOM-based, Reflected, and Stored. In this step, you will perform the first two.
What is XSS?
Cross-Site Scripting lets attackers inject JavaScript into a trusted website. When another user visits the page, their browser runs the attacker's script as if it came from the site itself.
This can steal session cookies, redirect users to malicious sites, or completely alter what they see on screen.
- Click the search icon (magnifying glass) in the top navigation bar of Juice Shop.
- Copy the following XSS payload and paste it into the search field, then press Enter:
<iframe src="javascript:alert(`xss`)">
Watch the quote characters
The xss inside the alert() is wrapped in backticks (`), not single quotes. The outer attribute already uses double quotes, so backticks prevent the browser from breaking the string early.
What does this payload do?
- The <iframe> tag tells the browser to embed a frame inside the page.
- The src attribute points to javascript:alert(...), which makes the browser execute JavaScript instead of loading a URL.
- If the application renders your search input without sanitizing it, the browser treats the payload as real HTML and runs the script.
An alert popup with the text "xss" should appear. This is DOM XSS. The vulnerability exists in client-side JavaScript that takes your search query and inserts it directly into the page's DOM without escaping it.
- Dismiss the alert popup by clicking OK.
- Return to the Score Board at http://localhost:3000/#/score-board.
- The "DOM XSS" challenge (XSS, 1 star) should now show a green checkmark.
No alert popup appeared?
- Make sure you pasted the payload into the search bar on the page, not into the browser's URL bar. The search field is what injects your input into the DOM.
- Double-check that xss is wrapped in backticks (`), not single quotes ('). Single quotes will break the payload.
- Check that your browser is not blocking popups. Look for a popup-blocked icon in the address bar and allow popups for localhost.
Why isn't my DOM XSS payload triggering an alert in Juice Shop's search bar?
Perform a Reflected XSS attack
Reflected XSS happens when user input sent to the server is reflected back in the response without sanitization. Instead of injecting directly into the DOM, this attack embeds the payload inside a URL.
- Navigate to the order tracking page at http://localhost:3000/#/track-order.
- Enter any text (like test123) as an order ID and click Track.
- Notice the URL changes to something like /#/track-result?id=test123. The id parameter value appears directly on the results page.
Why does this matter?
If the id parameter's value is rendered on the page without sanitization, an attacker can craft a malicious URL and trick a victim into clicking it. The victim's browser then executes the attacker's script.
- Copy the following URL and paste it directly into your browser's address bar, then press Enter:
http://localhost:3000/#/track-result?id=%3Ciframe%20src%3D%22javascript:alert(%60xss%60)%22%3E
What does this URL do?
This is the same <iframe> payload from the DOM XSS attack, but URL-encoded. Characters like < become %3C and spaces become %20 so the payload can travel inside a URL.
The id parameter's value is reflected onto the page without sanitization, causing the browser to execute the embedded script.
An alert popup saying "xss" confirms the Reflected XSS attack succeeded.
- Dismiss the alert popup by clicking OK.
- Return to the Score Board at http://localhost:3000/#/score-board.
- The "Reflected XSS" challenge (XSS, 2 stars) should now show a green checkmark.
No alert popup for Reflected XSS?
- Make sure you pasted the URL into the browser's address bar, not into the Juice Shop search bar. This payload targets the id parameter in the track-result page.
- Confirm the URL was not modified when you pasted it. Some text editors or messaging apps alter special characters. Copy it directly from this page.
- If you see the track-result page but no popup, try pressing Enter in the address bar again to reload the URL with the full payload.
Why isn't the Reflected XSS URL triggering an alert popup in Juice Shop?
Understand the defenses against XSS
Both attacks worked because the application inserted user input into the page without escaping it. The browser had no way to tell the difference between legitimate HTML and your injected payload.
How do developers prevent XSS?
- Escape and sanitize all user input before inserting it into the DOM. Characters like < and > should be converted to safe equivalents so the browser treats them as text, not HTML.
- Implement Content Security Policy (CSP) headers to restrict what scripts the browser is allowed to execute. Even if an attacker injects a payload, CSP can block it from running.
- Use front-end frameworks that auto-escape output by default. Modern frameworks like React and Angular escape user input automatically, but developers can still bypass this protection with unsafe APIs.
Why should designers care about XSS?
You have likely built forms and search interfaces. Every one of those input fields is a potential XSS vector if the application does not sanitize what users type into them. Understanding this changes how you think about every text field you design.
With SQL Injection and XSS under your belt, you have covered two of the three OWASP Top 10 categories in this project. Next up, you will tackle the number one risk on the list: Broken Access Control.
Broken Access Control
You have successfully injected scripts into the Juice Shop through two different XSS attacks. Now it is time to target the most critical vulnerability category on the OWASP Top 10.
Broken Access Control means users can act outside their intended permissions. In this step, you will access a hidden admin panel and spy on another user's shopping basket. Both attacks exploit the same fundamental flaw: the application checks if you are logged in, but never checks what you are allowed to access.
In this step, get ready to:
- Access the hidden administration panel by finding its route in the app's JavaScript source code.
- View another user's shopping basket by manipulating API request parameters.
Access the administration section
In Step 2, you searched through the JavaScript source files to find the Score Board route. You are going to use that same technique to discover a hidden administration page.
- Switch to the Sources tab in Chrome DevTools.
- Press Cmd+Shift+F (macOS) or Ctrl+Shift+F (Windows) to open the search-across-all-files panel.
- Search for administration in the JavaScript bundles.
- Look through the search results for a route mapping that contains path:"administration". This is the Angular route definition for the admin panel.
- Navigate to this route by typing http://localhost:3000/#/administration into your browser's address bar and pressing Enter.
You should see the admin panel displaying a list of all registered users and their email addresses, along with customer feedback submissions.
Why did this work?
The admin panel exists as a fully built page inside the application. The only thing "protecting" it is that there is no visible link to it in the navigation menu.
This is called security through obscurity. Hiding a button does not protect the page behind it. Anyone who reads the JavaScript source code can find the route and navigate there directly.
- Return to the Score Board at http://localhost:3000/#/score-board.
- Confirm that the "Admin Section" challenge (Broken Access Control, 2 stars) is now marked as solved with a green checkmark.
Don't see the admin panel?
- Make sure you are still logged in as admin@juice-sh.op. Check the top right corner of the page for the account name. If you were logged out, log back in using the SQL Injection technique from Step 3.
- Double check the URL includes the # symbol before /administration. Juice Shop uses Angular's hash-based routing, so the # is required.
- Still stuck? Help me figure out why I can't access the Juice Shop administration page.
View another user's shopping basket
The admin panel was a vertical privilege escalation. You accessed a page meant for a higher permission level. Now you will perform a horizontal privilege escalation: accessing another user's data at the same privilege level as your own.
- Navigate back to the Juice Shop storefront at http://localhost:3000.
- Click on any product and add it to your shopping basket using the Add to Basket button.
- Switch to the Network tab in Chrome DevTools.
- Click the shopping basket icon in the top right corner of the page to navigate to your basket.
- In the Network tab, look for a GET request to a URL that includes /rest/basket/ followed by a number (your basket ID).
What is the basket ID?
The basket ID is a sequential integer that identifies which user's basket to load. The server uses this number from the URL to look up the basket in the database. It does not verify whether the logged-in user actually owns that basket.
- Type a modified version of the basket URL directly into your browser's address bar, changing the basket ID to a different number. For example, type http://localhost:3000/rest/basket/2 and press Enter.
If the server returns another user's basket contents without any authorization error, you have successfully exploited Broken Access Control. You should see JSON data showing products that belong to a different user.
- Return to the Score Board at http://localhost:3000/#/score-board.
- Confirm that the "View Basket" challenge (Broken Access Control, 2 stars) is now marked as solved.
Basket not showing another user's data?
- Make sure you are changing the number at the end of the URL, not the port number. The URL should be http://localhost:3000/rest/basket/2, not http://localhost:2/rest/basket/.
- Try different basket ID numbers (2, 3, 4, 5). Some baskets may be empty, so the response looks blank even though the exploit worked.
- Still stuck? Help me figure out why I can't view another user's basket in Juice Shop.
Understand the defense
Why did these attacks work?
Both attacks exploit the same root cause: the application checks whether you are logged in, but never checks whether you are authorized to access the specific resource you requested.
For the admin panel, the navigation link was hidden from the menu, but the route itself was left wide open. For the basket, the server accepted any basket ID without verifying that the logged-in user owned that basket.
The fix for both is the same: implement proper server-side authorization checks on every request.
- Every API endpoint should verify that the logged-in user is permitted to access the specific resource they are requesting.
- Hiding a button or link in the UI is never a substitute for server-side access control.
- These Broken Access Control patterns appear in almost every web application penetration test. Understanding them is essential for any security role.
Secret mission
Extract User Credentials via SQL Injection
In Step 3, you used SQL Injection to bypass login. But what if you could go further and pull actual usernames and password hashes straight out of the database? This is exactly how real-world data breaches happen.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally using Docker, so there are no ongoing costs. Decide whether to keep the Docker image for more practice or delete it to free up disk space.
Resources you used:
- Docker image bkimminich/juice-shop stored locally on your machine (approximately 125 MB).
- Docker container started with the --rm flag (automatically removed when you stop it).
Keep everything running
No action needed. Choose this if you want to keep practicing with Juice Shop.
- The Juice Shop container is still running at http://localhost:3000.
- The app resets to a clean state on every server startup, so you can re-attempt all challenges from scratch.
- There are 113 total challenges across difficulty levels 1 through 6. You solved 7 in this project. Try filtering the Score Board by difficulty or category to find your next target.
- When you are done for the day, press Ctrl+C in the terminal where Juice Shop is running to stop the container.
Pause - I'll come back to this later
Stop the running container to free up system resources. The Docker image stays on your machine so you can start a fresh instance anytime.
- Switch back to the terminal where Juice Shop is running.
- Press Ctrl+C to stop the container. The --rm flag ensures the container is automatically removed.
- To start a fresh Juice Shop instance later, run this command:
docker run --rm -p 127.0.0.1:3000:3000 bkimminich/juice-shop
Delete - I don't want to use this again
Remove the Docker image from your machine to free up approximately 125 MB of disk space.
- Switch back to the terminal where Juice Shop is running.
- Press Ctrl+C to stop the container.
- Remove the Docker image by running:
docker rmi bkimminich/juice-shop
- Confirm the image is removed by running:
docker images
- You should no longer see bkimminich/juice-shop in the output.
Nice Work!
Nice Work!
Nice work! You just hacked a web application across three major vulnerability categories from the OWASP Top 10. You launched OWASP Juice Shop locally using Docker, uncovered its hidden Score Board through JavaScript reconnaissance, and systematically exploited real-world vulnerabilities that appear in penetration tests every day.
You've learned how to:
- Bypass login authentication using SQL Injection to log in as the admin user, exploiting the most critical web application vulnerability.
- Perform both DOM XSS and Reflected XSS attacks by injecting JavaScript payloads through unsanitized inputs, demonstrating how attackers can execute code in other users' browsers.
- Exploit Broken Access Control to access the admin panel and view another user's shopping basket, showing how authorization flaws expose sensitive data.
- Secret Mission: Extract user credentials from the database using a UNION SELECT SQL Injection attack, demonstrating the technique behind real-world data breaches.
Ready to quiz yourself?