Quite often developers ask me if they should put controls about every single parameter that they receive from users of their web application. My answer is, of course, yes. Couple of weeks ago I worked on a penetration test where we exploited a blind SQL injection vulnerability in a web application that used Oracle as the backend database.
The vulnerability was not easy to exploit due to extensive use of stored procedures, but with some clever SQL hacking I managed to retrieve everything from the database. Since I haven't seen a lot of papers about this, I thought it's a good idea to do a diary about this so here we go.
First, we will define our test environment so you can see how to exploit it. In our test environment, the developer receives one parameter. We'll call it event and it can have two possible values, true or false. When called, it is used like this:
Now let's see how this can be exploited through some advanced SQL injection.
The simplest test is to enter a ' character in the parameter (event=true'). As we are dealing with SQL injection this will cause the SQL statement to be incorrect in which case the application will just print a message that a database error occurred (no SQL visible).
However, depending on the parameter (true or false or something else), the application will have different output and that allows us to see what's going on behind. In other words, if the parameter is "true" the output will be different from the case when the parameter is "abcd" (or "false"). And this is the basis of blind SQL injection – we want to make a difference between various SQL statements which will allow us to deduce the content of the database.
In typical blind SQL injection examples a timed delay is added to the attacker observes how long it takes for the query to execute. In this case it was not possible because I was dealing with stored procedures and some web application firewalls which prevented me from using UNION statements. But that doesn't mean it's game over.
As I don't know how exactly the stored procedure is called or what's the backend database, the easiest way to determine that is to split the input parameter:
event = tr' || 'ue
This will cause the final input parameter to be 'tr' || 'ue' – the || operator in Oracle means concatenate so the parameter will actually be "true".
This shows that the database is evaluating the SQL statement which allows us to enter some if/then cases that will, in the end, allow us to read data from the database. So let's see how this is done in a bit more complex query:
event = tr' || (select case when substr(banner, 1, 1) = 'A' then 'u' else 'X' end from (select banner from v$version where banner like '%Oracle%')) || 'e
While this maybe looks complex, it really isn't. The query takes the database banner from v$version (where it has string Oracle in it). Then, from that line the first character is examined (specified by the substr() call) and compared to the letter 'A'.
If it is 'A', the query returns 'u', otherwise it returns 'X'.
Finally, this is concatenated so we have the following if/then case:
- If first character of the banner line containing string Oracle is 'A' return 'u' so the final string will be 'true'.
- Otherwise, return 'X' so the final string will be 'trXe'.
Now, by examining the output of the application, I was able to deduce if the query was successful or not. Couple of minutes later, a perl script that traverses through all characters was done and I was able to retrieve data from the database.
How serious is this? Well, it's pretty serious depending on what is in the database. While I wasn't able to modify the data, I was able to retrieve everything from the database. Remember Oracle? It has a handy table called all_sources which contains sources of stored procedures and functions. This allowed me even to retrieve source code!
This example shows why every parameter your application deals with must be verified. In this simple case, all the developer had to do is check if the parameter is true or false by creating a simple white list. Also, the developers should be aware that they can't rely on stored procedures (only), hoping that they will do the job for them as it all depends on the environment.