University of Minnesota
Development of Secure Software Systems
index.php

CSci 4271 Lab 8

This week we'll continue in the area of SQL injection, but look at a more complex kind of attack that calls for an automated approach. This is called "blind" SQL injection because it occurs in a situation where an attacker has only very limited information about the effects of their injection, such as just whether an query succeeded or failed, or roughly how long it took to execute. We're going to use WebGoat as our experimentation testbed, but providing more hints for a hard problem. To help you learn the most, we'll guide you through carrying out the attack with a script of your own, but we'll also mention that some off-the-shelf tools can also be used.

As usual in the online lab we'll randomly split you into breakout groups of 2-3 students: please work together, discuss, and learn from the other student(s) in you group. Use the "Ask for Help" button to ask questions or show off what you've done. We also recommend working in groups in the in-person lab, but there you can choose your own groups and physically raise your hand to ask a question. You may still find it useful to use Zoom or tmate for screen sharing in person while respecting social distancing.

The process of setting up an running WebGoat is the same as last week, so we won't repeat those instructions here, except to point out one other approach that may be useful for running the WebGoat server and your browser on different machines. Even though the WebGoat server on its own only accepts connections from the same host, you can route a transfer over an SSH connection using an SSH feature called port forwarding. The port number doesn't have to be the same on both machines, but it's easiest to keep track of if it is. The command you can run on your local machine (the one with the browser) looks like:

ssh -L [port number]:127.0.0.1:[port_number] [x500]@[cse_labs_computer].cselabs.umn.edu

The square brackets portions are replaced by the correct values for your situation.

Building your own attack

What we'll be working on this week is the most difficult of the "advanced" SQL injection challenges in the current version of WebGoat. After you log in, use the menu on the left to choose "(A1) Injection" and then "SQL Injection (advanced)". The basic introduction to blind SQL injection is step 4, and the vulnerable page itself is step 5. Spend a few minutes exploring what you can do here, if you didn't already start on it last week. You can see there are two sub-pages, LOGIN and REGISTER. You can assume from the setup of the problem that these forms are connected to a database that stores user information, and at least two different SQL queries are involved in the operations of the two different pages. In turns out that only one field between these two different pages has a SQL injection vulnerability. By a little trial and error, can you tell which one it is? Note that WebGoat's definition of "success" for this problem is not trustworthy, so don't worry about whether the 5 button turns green or not. This question will be spoiled in the next paragraph.

The login page itself doesn't have a SQL injection vulnerability. The attack would be easier if it did, because you could just make the login process artificially succeed. But because the login page is correctly implemented, you're only going to be able to correctly log in if you can supply the same password for Tom (his username is tom in lowercase) that is listed in the database. The vulnerable query is one on the registration form that is used to prevent duplicate registrations: it checks whether there is already an entry in the database with the supplied username. If the username is not already used according to the query, then an additional query is used to update the database with the new registration information. Unfortunately for you as an attacker this update query is also not vulnerable to SQL injection. So it won't work to take the approach of reregistering Tom with a different password. You can confuse the code with SQL injection to make the duplicate-user query fail, but you can only do this by putting other text in the username field that will change the password for a new strangely-named user, not tom.

The SQL injection vulnerability in the registration form is a pretty standard one, and it can be used to try to retrieve different data or to modify the database. But its capability to retrieve data is limited because the results of the query aren't passed back to the browser. The only thing you can tell from the outside is whether the query was successful (returning one or more rows) or unsuccessful (returning no rows). If the lookup query is successful, the web page will return an error that looks like:

User tom already exists please try to register with a different username.

To allow a successful login, you will need to either:

  1. Learn tom's current password, or
  2. Change tom's password to a value you know.

The second one of these might sound easier because you can inject update queries, but the problem is that you don't know the name of the table that you want to do the update to. So either of these approaches will involve getting some information out of the database through the narrow channel of the success/failure of an account creation query. So we recommend you follow approach (1). For approach (1) you still need to guess the name of the column in that database table that holds the user passwords, but this one turns out to be easy to guess; it's just password.

Because the blind SQL injection will require a lot of queries, it would be inconvenient to do all the queries by hand; instead we'll create a script to automate the process. To do the queries from a script instead of from the browser, there are a few things you'll need to take care of. First you need to know the URL that the form data is submitted to, and the names of the fields you'll need to control. You can see these in the HTML source of the form web page, either reading it directly or with a browser-based tool like the "Inspector" in recent versions of Firefox. It turns out the URL is:

http://localhost:8234/WebGoat/SqlInjectionAdvanced/challenge

Where as usual the port number 8234 may be different. The four parameters in the PUT request corresponding to the four input boxes on the form are username_reg, email_reg, password_reg, and confirm_password_reg, where username_reg is the one vulnerable to SQL injection.

Another thing you'll need to take care of is convincing the web site that your automated requests are coming from a logged-in user. It would be more complex for the script to do its own logging in, so an easier approach is log in with your normal graphical browser, and to copy the session cookie from the graphical browser to the script. Typically for a web site written in Java, the session cookie WebGoat uses is named JSESSIONID. You can retrieve its value from your browser if you're logged in. In Firefox, use the menu entry "Web Developer" and "Storage Inspector", then look under Cookies. This will give you a long string of letters numbers and symbols you'll need to copy into your attack script.

To get you started, we've written the framework code in Python for a script that makes a single SQL injection query to the registration form. You can make a copy with a command like:

cp /web/classes/Fall-2020/csci4271/labs/08/blind-attack-template.py blind-attack.py

(We recommend python3 for running the script.) As it stands, this script injects a tautological comparison to the query, so the query still returns Tom's row from the database. To get it to run, you'll need to update it with the port number of your server and your JSESSION cookie. If it's working correctly it should print Query was successful. If you change the tautology to a condition that is always false, you should see that the output changes to the result being unsuccessful. This is the basic idea for how the script lets you check whether an expression of your choosing is true or false according to the SQL server. Of course to figure out Tom's password, you'll need to replace those conditions with conditions that tell you something about what the password is. Some tools you might find useful for this include the SQL operator substring, or the string comparison which can be done in SQL with the normal comparison operators like <. Of course you'll also want to wrap the code we've provided in some loops or recursion, and new variables, and so on. You can assume that the password consists only of alphanumeric ASCII characters.

Using an existing tool

If you end up attacking SQL injection vulnerabilities like this as a full-time job, you won't need to write scripts like the one in this lab by hand, because their function is subsumed by tools that have already been written. For instance, one well-known tool for automating SQL injection is called sqlmap. It's written in Python, so you can easily download it from its homepage and try it out yourself. See if you can get it to confirm the same injection vulnerability your self-written script used. Give it the URL of the vulnerable page with the -u option, including the relevant parameters in the URL query format (first a question mark ?, then subsequent parameters separated by ampersand &, and each parameter variable name separated from its value by an equals sign =). Supply your same JSESSION cookie with --cookie, give --method=PUT, and use the --string option to tell it how to distinguish success from failure.