SQL / Command Injection Lab: Part 1 - Attacks

SQL Injection

Damn Vulnerable Web App (DVWA) from Ryan Dewhurst is a great target to use in the lab for practising penetration testing and detection techniques.

Let's start from the beginning, with a little nmap action to find our DVWA host which is behind a firewall.


OK, there is only a single host visible behind the firewall. The next nmap command tries to discover versions.

As can be seen nmap provides plenty of detailed information about the host. The 'sV' option is a version scan and the '-A' uses the appropriate scripts to discover the OS type and version and runs a few other extra tests that provide useful information like the traceroute data. The firewall is blocking everything but port 80 (HTTP), but from the version information nmap provides it can be seen that we have here a Linux host running Apache and PHP and it's highly likely that it's also running MySQL in the standard LAMP configuration.

Next I will browse to DVWA and login so I can test try some SQL injection. First I will need to login in to DVWA with the default credentials 'admin' and 'password', then I will set the security level to Low in the DVWA Security section.
Then click on the SQL Injection section where you are presented with a single input form field. Entering a single quote ' causes a SQL error to be displayed. This is because the input was passed directly into the query that went to the database and caused a syntax error when combined with the select statement in the PHP code. This indicates that SQL injection is possible. It's also possible if the field was numeric that a quote isn't even necessary to test for SQLi.  
Before progressing further it's worth having a look at the regular function of the form to see what it does. Entering number 1 and clicking submit will bring back the first account in the database. Similarly entering a number 2 will bring the second. It should follow then that entering 2+1 will bring back the third, but it doesn't. We can't do sums here. Just to be certain I'll encode the + this time entering 2%2b1.  It still does not bring back the third account. It's not a numeric field and although it's using numbers they are represented as strings.
Now I know that the the web app is vulnerable to SQL injection (although I think there was a clue in it's name!), I can use a short select statement to return data from the table: 1' or '1'='1' #

The first single quote terminates the string input so that the 'or' clause can be added, the test '1'='1' is always true and finally the # denotes that everything that follows is a comment. The # allows the attacker to push back anything in the original select statement in the PHP code to come after the comment thus allowing complete control over the ending of the select statement. Testing for something that is always true with an 'or' will always result in effectively ignoring the preceding condition. As can be seen from the screen shot the result of the query in this case is to display all records because anything 'and true' is true for all values.

I can use a select statement to understand how many fields will be displayed. This is important because I want to know how many fields I can use for output.
1' and 1=2 union select 1 #

1' and 1=2 union select 1,1 #

I can keep adding ,1 until I stop getting errors and get the result shown in the screen shot. After that adding 1,1,1 causes the same error regarding a different number of columns shown above.
Now I know how many columns I need to use for output.  Using the false condition 'and 1 = 2, means the table query won't return any data which leaves the field free to output some other data that I want to see.

It is useful to find the database name and the user it's running under and I can use a couple of SQL functions to give us that information and use the two columns for the output. The user() function returns the MySQL user that the database is running under and the database() function returns the database name.
1' and 1 = 2 union select database(), user() #

ID: 1' and 1 = 2 union select database(), user() #
First name: dvwa
Surname: root@localhost

The output shows that this database is being run under the root user in MySQL, which isn't a good idea, although this is DVWA remember. There could be further risk if the actual mysqld process is also running under the OS root user. To check this use:



If I browse to the cmd4.php file I can see the answer.
This is clearly a misconfiguration as well. If the MySQL daemon had been running under the mysql user I wouldn't have been able to write my php file to the web root.

In the next select statement I can use a sub-select statement using the group_concat function of MySQL to show all the columns for this table in the database as a single string using only one column for output. You can see the ',1' at the end which puts that value into the Surname field so that we don't have a mismatch in columns being output causing an error.  Using the group_concat() function allows me to output all the columns from the table in single output field shown as 'First name:' on the screenshot.
1' and 1 = 2 union select (select group_concat(column_name) from information_schema.columns where table_schema=database()), 1 #

Next, I want to see what other tables are available:
1' and 1 = 2 union select (select group_concat(table_name) from information_schema.tables where table_schema!='mysql' AND table_schema != 'information_schema'), 1 #
This select returns everything except the 'mysql and 'information_schema' tables. Now I know which table and what columns are available I can use that information to view data that I want to see.

In this case the 'users' table looks like it might hold interesting information. I can use another select statement to see the contents of the 'users' table including all the columns I identified earlier.

1' and 1 = 2 union select (select group_concat(0x0a,user_id,0x2c,first_name,0x2c,last_name,0x2c,user,0x2c,password,0x2c, avatar) from users),1 #
In this select statement I can use group_concat function again to show all the columns in a single output field and include some encoded formatting to make it more readable on the web page.
Now I have all the users details from the table including the password hashes.

Now I could use a password cracking program to see if I can crack the MD5 hashed passwords and use these in further attacks.


Command Injection

Now I will turn my attention to command injection. I selected the 'Command Execution' item from the DVWA menu. When it loads you have a single form input box where you are prompted to enter an address that you want to ping. If I do that I get the ping output back to the page. To run a command of the attackers choosing it's a simple matter of adding a semi-colon after entering the IP address and adding a command of your choice on the end. In the example in the screen shot I used a simple 'ls' to return a directory listing.

From the output we get back you can see the files present in the directory including the source code for the command execution contained in the index.php. An attacker might be interested in stealing that source code to better understand the application. I could try this:


172.31.254.34 ; cat index.php
Oops, that won't work because the code is coming back through the web server and being interpreted. 
I can do this instead:



172.31.254.34 ; cat index.php | openssl enc -base64 -e




Now I have the source code in base64 encoded form and I just need to turn it back into code by unencoding it:














The openssl program is a veritable 'swiss army knife' of functionality and I can use to decode the base64 block by simply pasting the encoded block in and hit ctrl-d twice and now I have the source code.


In part 2 of this post I will be looking at these same attacks from from a detection perspective using Security Onion.

Comments

Popular posts from this blog

Squid Proxy with SOF-ELK Part 1

Netflow analysis with SiLK - Part 1 Installation

CI/CD Pipeline Security & Shifting Left