SQL Injections are possible when improperly validated/escaped input is interpreted by a SQL Parser (i.e., included in a SQL statement). Dangerous consequences, such as data leakage and unauthorized access, can result.
In this exercise, we provide a very simple program that checks for a given username and password in a portable SQLite database. If a correct username and password is given, then the program will display a welcome message. If an incorrect password or nonexistent username is supplied, the program will display a failure message. Your objective is to exploit a SQL injection vulnerability and trigger a "successful" login without a correct password (or even without a correct username).
Note: This program violates several best practices regarding password storage, credential management, authentication, etc., but you should focus on the SQL Injection vulnerability for this exercise.
Prepared Statements protect an application from SQL Injections by parsing
the SQL query separate from the runtime input. For example, a prepared
statement may look like
SELECT * FROM USERS WHERE username == ?,
and this is the string that is parsed by the SQL database management
The parser reserves a place for the input (string in this case) in place
? and does the comparison on the input without ever
parsing SQL metacharacters from the input.
We will fix the SQL Injection vulnerability in Main.java using prepared statements. If implemented correctly, the prepared statement will force the database to compare real passwords with the entire input string and thus return false unless the user's password is exactly what is input to the program.
This exercise will be completed entirely on the command line terminal of the provided virtual machine. To open the terminal, right-click on the "EXERCISES" directory and select "Open in Terminal". Enter the following command to change into the SQL Injection exercise directory:
We provide a Makefile that will compile the program. Every time you change
Main.java file, you must recompile the program before
running it again.
Enter the following command to compile the program:
The next step is to run the program and test some inputs. To execute the program after compiling, enter the following command:
You should see output from the program prompting for a username. Type a username and press enter. The program will then prompt you for a password. Type a password and press enter. To ease exploitation, the password field will not be hidden. The program will check the SQLite database for the username/password combination and tell you if the login was successful. The following is an example of a correct username/password input:
username: some_guy password: his_password Login Successful! Welcome some_guy
Try this a few times with different usernames and passwords to see how the
To exit the program, type
exit in place of a username.
All of the "correct" username and password combinations can be
create_db.sql, which the Makefile uses to generate
Now that you understand the basic behavior of the program, it's time to
look at the implementation.
This program is implemented in
Main.java. Use your favorite
text editor to open this file.
Enter the following command to open the file in Nano:
Spend some time looking at the code and tracing the flow of execution. You're looking for an attack surface and corresponding attack vector. In other words, how can an attacker's input reach the database?
Exit the text editor. Run the program again in the same way as
Enter a username like "some_guy" and then a password that, when inserted
into the SQL query, will ensure that the
WHERE clause is
Once you manage to get a "Login Successful" message without a correct password, it's time to fix the vulnerability!
Let's go back to our text editor and open
(see Step 3).
This time, we're going to make some changes.
Using what you know about
make some changes to the vulnerable code sample so the user input is
never interpreted as part of the SQL query.
Once you have a potential fix implemented, save and exit the file. Now recompile and run the program as we did in the compilation step and the run step. Try your exploit again. Make sure to test a good password so you know the intended function is not broken.
Repeat the process of changing the program, compiling, and testing until you are convinced that the vulnerability is mitigated and the original program intent remains functional.