Skip to content

OWASP Juice Shop

OWASP Juice Shop

It is a web application that has been developed by the OWASP consortium for practicing web application security testing. It contains many vulnerabilities of different nature. It also contains detection code that displays a “vulnerability found” banner whenever it detects that the user has managed to exploit one of the numerous vulnerabilities of the webapp.

OWASP has made the sources available and the webapp itself has to be installed and deployed on a web server in order to be actually used. An almost one-click and free deployment on a cloud platform can be obtained quite easily (see below).

Running the webapp

I suggest to use Gitpod:

  • Create an account (no credit card needed)
  • Click the "one-click deployment above"
  • Go on your dashboard, run the workload, click share, copy the URL (do not forget to click "share", otherwise the URL will not be visible).
  • Paste the URL on a browser (or in the Burp browser, as indicated below).

Gitpod has a free tier that is renewed each month automatically. Remember to “stop” the workspace while not in use (to not consume your credits) and to “pin” the workspace (otherwise it will be deleted after 30 days of inactivity).

Heroku is a platform analogous to Gitpod but it requires a small subscription fee. If you use Heroku, remember to deactivate the webapp on Heroku while it is not used, in order to not use Heroku resources unnecessarily (Resources section of your Heroku account; just stop the corresponding “Dyno”).

Tutorials

Many videos and tutorials are available on the web that describe this webapp. Keep in mind that these resources:

  • Do not describe the internal architecture of the webapp.
  • Assume some basic knowledge of web application security.
  • Do not provide a full step-by-step guide for detecting all the vulnerabilities of the webapp.
  • Have an extremely (excessive) practical orientation: the focus is not “understand what you are doing”; it is “let the application display the “vulnerability found” banner”.

Useful resources:

Authentication in OWASP Juice Shop

Bearer Tokens Explained

Authentication in the OWASP Juice Shop webapp is managed differently from how we described it in “Computer Networks 1” and is based on JSON Web Tokens. This is a widely used mechanism specified by a web standard called OAuth and briefly summarized below.

Many web applications manage authentication with cookies. Once a session is authenticated (i.e., a correct username-password pair has been provided in the login form), a webapp that receives a request determines the identity of the sending user based on the value of the Cookie header (this value is used as a key for accessing a table stored in the webapp that contains the full session state, including the username).

Many other webapps encode instead the authentication information in an additional Request header called Authorization. The value of this header is Bearer followed by a bearer token. A bearer token is a “very long JSON string” constructed by a webapp upon receiving a successful login request. The exchange of the bearer token between webapp and browser (e.g., its inclusion in HTTP requests) usually involves Javascript code executed by the browser. Whenever the webapp receives an HTTP request, it determines the identity of the user from the web token in the Authorization header, rather than from the value of the Cookie header.

Of course, bearer tokens must be managed so that users cannot forge or modify them arbitrarily (i.e., bearer tokens must have authenticity and integrity; and, the webapp must be able to verify these security properties of each received bearer token).

Based on this description, one might think of bearer tokens as just cookies sent with a different header. There are actually many deep but complex differences between the two mechanisms that are beyond the scope of this document.

Bearer Tokens in OWASP Juice Shop

The OWASP Juice Shop webapp uses bearer tokens for authentication. However, this use does not follow the OAuth specification completely and is insecure. In detail:

  1. Data inserted in the login form are not sent with the standard HTML mechanism, i.e., as an entity that consists of query string encoded as Content-Type: x-www-form-urlencoded; the entity consists instead of JSON data (Content-Type: application/json). This is managed by Javascript code in the browser, i.e., clicking on the form button results in invoking some Javascript code; this code will collect the user-provided data, assemble it in a JSON entity and send this entity to the webapp.
  2. Bearer tokens are returned in the response to the request containing the credentials inserted in the login form (rather than with a standard, more complex OAuth flow).
  3. Bearer tokens contain the username of the token owner and the corresponding password hash.

Regarding point 2, there is no reason whatsoever for including username and password hash in the token. At first glance, this might not seem a risk: the token for a user will be visible only to that user, because the traffic is on HTTPS and the bearer token is only sent in response to a login form.

However, if the webapp had a vulnerability allowing a user A to login as user B, then user A would access the bearer token for user B. Thus, user A would obtain the password hash for user B and would thus be able to impersonate user B forever.

As it turns out, OWASP Juice Shop has this vulnerability (see Question #1 here): A can login as B (because of this vulnerability) and thus A can impersonate B forever (because of the insecure implementation of bearer tokens).

Example traffic

Login

Credentials are sent as a JSON resource in the request. The response contains a bearer token.

POST /rest/user/login HTTP/1.1
Host: owasp-juice-shop-alberto.herokuapp.com
Cookie: language=en; welcomebanner_status=dismiss; cookieconsent_status=dismiss; continueCode=KQabVVENkBvjq9O2xgyoWrXb45wGnmTxdaL8m1pzYlPQKJMZ6D37neRqyn3x
...
Content-Type: application/json
...

{"email":"1@1.com","password":"11111"}
HTTP/1.1 200 OK
...
Content-Type: application/json; charset=utf-8
Content-Length: 808
Etag: W/"328-+1bdmcgdtIFfafMn3oEmj38Axmo"
...

{
"Authentication":
    {"token":
    "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MjEsInVzZXJuYW1lIjoiIiwiZW1haWwiOiIxQDEuY29tIiwicGFzc3dvcmQiOiJiMGJhZWU5ZDI3OWQzNGZhMWRmZDcxYWFkYjkwOGMzZiIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIwLjAuMC4wIiwicHJvZmlsZUltYWdlIjoiL2Fzc2V0cy9wdWJsaWMvaW1hZ2VzL3VwbG9hZHMvZGVmYXVsdC5zdmciLCJ0b3RwU2VjcmV0IjoiIiwiaXNBY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjEtMDgtMjkgMTc6NDE6MDIuNTY2ICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMjEtMDgtMjkgMTc6NDE6MDIuNTY2ICswMDowMCIsImRlbGV0ZWRBdCI6bnVsbH0sImlhdCI6MTYzMDI1ODg3MiwiZXhwIjoxNjMwMjc2ODcyfQ.vMIu5yD-VJux8FspwslykpET009gEYsIoYhb1azL62dIoeRIYFd2AX8wR6_RJrXDPgluwB9ra0QO2Wk9GA5LhadfCkfxxXB-Kgb7XjXzIWo6sxqDfAjza_Yt8mUevv3yIU84RHHpxDFFn0jtZnIAY90ZE5gmxvChOWIwWvLg3XA",

    "bid":6,
    "umail":"[1@1.com](mailto:1@1.com)"
    }
}
Shopping Basket

The request carries a cookie and a bearer token (encoded in a certain format). The response contains the corresponding shopping basket described in JSON.

GET /rest/basket/6 HTTP/1.1
Host: owasp-juice-shop-alberto.herokuapp.com
Cookie: language=en; welcomebanner_status=dismiss; cookieconsent_status=dismiss; continueCode=KQabVVENkBvjq9O2xgyoWrXb45wGnmTxdaL8m1pzYlPQKJMZ6D37neRqyn3x; ...
Accept: application/json, text/plain, */*
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MjEsInVzZXJuYW1lIjoiIiwiZW1haWwiOiIxQDEuY29tIiwicGFzc3dvcmQiOiJiMGJhZWU5ZDI3OWQzNGZhMWRmZDcxYWFkYjkwOGMzZiIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIwLjAuMC4wIiwicHJvZmlsZUltYWdlIjoiL2Fzc2V0cy9wdWJsaWMvaW1hZ2VzL3VwbG9hZHMvZGVmYXVsdC5zdmciLCJ0b3RwU2VjcmV0IjoiIiwiaXNBY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjEtMDgtMjkgMTc6NDE6MDIuNTY2ICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMjEtMDgtMjkgMTc6NDE6MDIuNTY2ICswMDowMCIsImRlbGV0ZWRBdCI6bnVsbH0sImlhdCI6MTYzMDI1ODg3MiwiZXhwIjoxNjMwMjc2ODcyfQ.vMIu5yD-VJux8FspwslykpET009gEYsIoYhb1azL62dIoeRIYFd2AX8wR6_RJrXDPgluwB9ra0QO2Wk9GA5LhadfCkfxxXB-Kgb7XjXzIWo6sxqDfAjza_Yt8mUevv3yIU84RHHpxDFFn0jtZnIAY90ZE5gmxvChOWIwWvLg3XA
...
Accept-Language: en-US,en;q=0.9
HTTP/1.1 200 OK
...
Content-Type: application/json; charset=utf-8
Content-Length: 154
Etag: W/"9a-1IBRCZ5Q9/JL7raf23dCq8reO7A"
...

{
    "status":"success",
    "data":
    {
    "id":6,
    "coupon":null,
    "createdAt":"2021-08-29T17:41:11.950Z"
    "updatedAt":"2021-08-29T17:41:11.950Z"
    "UserId":21,
    "Products":[]
    }
}
Bearer token in decoded form

By copying the above bearer token on the decoding facility on https://jwt.io/ we obtain the JSON Web Token in decoded form as indicated below. Note the hash of the user password.

8ff0491e41575238670558d2fac6c385.png

IDOR vulnerability

The description of IDOR (Insecure Direct Object Reference) vulnerabilities is provided elsewhere in this website. The OWASP Juice Shop webapp has an IDOR vulnerability: any authenticated user may read the shopping basket of any other user. Of course, this is a mistake in the code of the webapp, i.e., a vulnerability: a user must not be able to see data private of another user.

In order to play with this vulnerability, it is necessary to create at least two shopping baskets of different users:

  1. Register a new user A, login, insert some items in the shopping basket, logout.
  2. Repeat for another new user B.

Discovery

The procedure is as follows:

  1. Set Intercept Off
  2. Login as either user, e.g., A.
  3. Set Intercept On
  4. Read the shopping basket. Before forwarding each HTTP Request, analyze the request and search for parameters that might identify the shopping basket of this user (hint: search for a parameter whose value is a small integer).
  5. Set Intercept Off and make sure the shopping basket has been fully displayed (i.e., there is no pending request)
  6. Set Intercept On
  7. Read the shopping basket. Before forwarding each HTTP Request, modify the value of the parameter(s) identified at step 4 with some "promising" value (hint: increment or decrement that small integer).

There may be essentially these outcomes:

  • The webapp has an IDOR vulnerability: you will see the shopping basket of other users.
  • The webapp implements authorization correctly:

    • The parameter modified at step 7 has a valid value (i.e., it indeed identifies a shopping basket) but the webapp realizes that the basket with that value must not be shown to the user that sent the request. Or,
    • There is no parameter visible to the browser that identifies that shopping basket. The browser only observes cookies or bearer tokens that identify the authenticated user. Shopping baskets are within the session state maintained by the webapp and do not have any specific identifier.

Exploiting in practice

In a practical setting the attacker would not know how many users are logged in and what their corresponding “small integer” values are. The attacker would thus have to repeatedly send GET /rest/basket/small-integer-X, with many different values for small-integer-X.

This procedure can be automated in Burp with the Intruder module, as follows.

  1. Set Intercept Off
  2. Login and click on the shopping basket.
  3. Inspect the HTTP history (Proxy→HTTP history) and identify the HTTP request that shows the shopping basket.
  4. Send this request to Intruder (right-click).
  5. Go to the Intruder module of Burp. Select the Positions tab. A window will appear with the request just sent to this module.
  6. Click clear § (this is because Intruder assumes by default that the iteration should be done on cookie values).
  7. Specify which part of the request has to be modified at each iteration: Select the small integer portion of the URL; replace it with any letter (e.g., s) and click add §. At this point Intruder knows that it is this part of the request that has to be modified at each iteration (Figure Intruder 1).
  8. Specify how to modify the specified part at each iteration: Click on the Payloads tab and insert a set of integer values in the Payload options section (either by preparing a text file in advance or by inserting the values one by one; the professional version of Burp has more options in this respect) (Figure Intruder 2).
  9. Click “start attack”.

8660cc7f4b0a615aaee5712f3c490c84.png

Figure Intruder 1: Request with portion to modified

9fdeebbdd4eb4809f74726c93c8800c8.png

Figure Intruder 2: Payload set configured as a list of integers

At this point Intruder will repeatedly send the selected request to the webapp and will collect the corresponding responses. Important: this traffic will be collected by Burp but it will not reach the browser. Burp shows this traffic in a new window for interactive analysis (Figure Intruder 3). Each message can be saved on a file.

e792d1cde9d3d2018e0d94b7f51fa9b5.png

Figure Intruder 3: Part of the response for payload 5 (shopping basket of user id 16). In this execution, all the responses have returned a status code 200, thus corresponding to an existing shopping basket. Some of those baskets are empty.

Another IDOR vulnerability

The IDOR vulnerability previously discussed allows any authenticated user to read the shopping basket of any other user. The web app has a further IDOR vulnerability: any authenticated user may modify the shopping basket of any other user. The same limitations previously discussed regarding the difference between shopping basket identifier and username apply to this vulnerability as well.

The steps for detecting and exploiting this vulnerability are very similar to those previously discussed, the only difference being this: rather than using the HTTP request that shows a shopping basket, one has to use the HTTP request that adds an item to a shopping basket. We omit the detailed steps for brevity.

The two IDOR vulnerabilities discussed here are associated with different URLs and requests, thus it is not possible to tell whether they are provoked by the same coding mistake or by two different mistakes in different portions of the web app code.

A few further simple vulnerabilities

Online password guessing

The admin username is admin@juice-sh.op (this information can be found with another vulnerability, not discussed in this document for brevity). Online password guessing can be done against this username, as follows.

  • Search “best1050.txt from Seclists” on a search engine and download the corresponding text file (a list of 1050 frequently used passwords).
  • Set Intercept on
  • Attempt to login by inserting the username admin@juice-sh.op and any password.
  • Inspect the HTTP history and identify the HTTP request that sends the credentials (note that these are sent as a JSON resource rather than as a query string).
  • Send this request to Intruder (right-click).
  • Define the password as an Intruder parameter.
  • Click on the Payloads tab and load best1050.txt in the Payload options section.
  • Click “start attack”. At this point Intruder will repeatedly send the selected request to the webapp by changing the password at each iteration and will collect the corresponding responses. It will take some time to try all the passwords in the selected file.

It can be seen in Figure Intruder 4 that the request number 117 obtained a status code 200, meaning that the correct password (admin123) was found.

1cd842992fa632a16765cad64ec42ae0.png Figure Intruder 4: Summary of the traffic sent by Intruder.

This vulnerability is not a mistake in the code. It is a configuration flaw.

SQL Injection in login page

  • Open the login page.
  • Insert ' or 1=1-- as username (note the single quote character 'at the very beginning of the username) and any string as password.

The webapp will show a successful login and the username happens to be the administrator.

This vulnerability can be found and exploited even without using Burp. The reason is because:

  1. The string to be inserted in the username field is very simple: it is one of the basic SQL injection payloads that any pentester would try at the very beginning.
  2. The Javascript code of the login page on the browser does not constrain or modify in any way the string inserted as username.

In a real setting, usage of Burp would be advisable in order to try many SQL injection payloads and to detect whether the string inserted as username indeed corresponds to the string actually sent by the browser to the webapp.

SQL Injection explained

Irrespective of the programming language used for developing the web app, the code that validates the received credentials on the web app must execute a SQL query on its database containing usernames and passwords. To this end, the code typically executes an IF-THEN-ELSE construct in which the IF condition is a boolean expression that must evaluate TRUE only when the username in the request is a valid username and the password in the request is a valid password.

Such an expression is often programmed in a form like this: \ IF \ (SQL_QUERY(SELECT 'username-in-the-request' FROM … AND …)) \ THEN … ELSE ...

The part of the SQL query before the AND operator makes sure the username exists, the part after the AND (not shown for brevity) makes sure the provided password is indeed the password for that username.

The SQL semantics is such that if the SQL query returns an empty result set then it corresponds to the boolean FALSE, otherwise it corresponds to the boolean TRUE. Thus, the boolean expression will behave as expected.

The vulnerability (coding mistake) is when the web app code takes the data inserted in the HTTP request without any prior control or “sanitization”. Without such a control, carefully selected input strings (SQL injection payloads) might change the logical structure of the SQL query and provoke a behavior different from what expected by the developer of the web app.

As it turns out, the OWASP Juice Shop suffers from this vulnerability in the login page. The value ' or 1=1-- for the username has the effect of changing the structure of the the SQL_QUERY:

IF
(SQL_QUERY(SELECT '' or 1=1--' FROM … AND …))
THEN … ELSE ...

The query actually executed by the SQL engine will be:

SELECT '' or 1=1

because characters -- correspond to the line comment in SQL syntax, thus the query portion ' FROM … AND …will be ignored by the SQL interpreter. The query will return the boolean value TRUE irrespective of the actual content of the database, hence the check made by the web app will be satisfied and the session will be authenticated.

The username that will be associated with the session depends on the structure of the code surrounding the query. In this case, the user id turns out to be 0 (probably the default value) and this id happens to be associated with the administrator account.

The appendix of “The Official Companion of the OWASP Juice Shop” (see section Resources) illustrates how to extract the credentials of all users and how to exfiltrate the entire database of the application by means of SQL injection.

More SQL Injection in login page

In case a username is known, it is possible to login with that username as follows:

  • Open the login page.
  • Insert username'-- as username (note the single quote character 'at the end of the username) and any string as password.

The webapp will show a successful login with the selected username.

In this case the SQL injection payload need not contain 1=1. As it turns out, the code of the web app is such that the provided payload suffices to satisfy the check and to impersonate the target username.