Database Fingerprinting for SQL Injection
Identifying the underlying DBMS
There are small differences between database management systems and those can have a huge impact on the feasibility and the result of an SQL injection attack. It is especially important for the attacker to identify the underlying DBMS since it will allow him to fine tune the injected segment and fully exploit the vulnerability. This article exposes the different techniques that can be used to fingerprint a database from an SQL injection.
A common practice to extract information about the tested system is to intentionally generate errors. This technique can be useful in order to fingerprint the database, especially when database errors are returned to the end user. The message returned by the DBMS may contain the name and version, but in most cases this is the unique error message structure that will help identify the database. In fact, each DBMS has its own error message template and retracing which DBMS generated the error is quite easy. Here is a classic error:
Error returned to the attacker.
ORA-01789: query block has incorrect number of result columns.
The example above indicates that Oracle is the underlying database. The error prefix "ORA" and the 5 digit error number is characteristic to this specific DBMS. If you want more information about the classic structure of database error messages, take a look at this article.
Extract Database Version
The easiest and most accurate way to identify which database is used is to ask the database to identify itself. However, this will require the attacker to know how to inject a complete UNION SELECT statement. Before going any further, here is how the database software and version can be obtained with the most popular DBMS.
- Retrieve Oracle version: SELECT banner FROM v$version WHERE rownum=1
- Retrieve SQL Server or MySQL version: SELECT @@version
Let’s now see how such SELECT statement could be injected in the main query with the use of the UNION operator. Note that a minimal SELECT structure is used in the example below.
Injected parameter that retrieves database version.
1 AND 1=2 UNION SELECT 1, 2, @@version
SELECT id, qty, name FROM products WHERE id=1 UNION SELECT 1, 2, @@version
Result – The page contains values 1, 2 and the following string.
Microsoft SQL Server 2008 (SP1) - 10.0.2531.0 (X64)
Mar 29 2009 10:11:52
Copyright (c) 1988-2008 Microsoft Corporation
Express Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)1
As you can see in the example, not only the attacker can retrieve information about the database software, but the details about the version are also returned. If the underlying database is not up to date, new vectors of attack such as buffer overflows could be explored by the tester.
Inference Database Fingerprinting
Given no information is returned to the end user, the attacker could still identify the database by using inference testing. The basic idea is to submit SQL segments which are only valid for one DBMS. If the injected segment is correctly executed, we can conclude that we have discovered which database is used. The process is slightly different depending of the vulnerable parameter type (numeric or string), but the principle is the same.
Any function that returns a number and exists in only one database system can be used to achieve the fingerprint. If the function injected in the vulnerable parameter is not recognised by the DBMS, an error will be thrown. Otherwise, the function will be executed and the returned value will be integrated in the query. Here is a list of numeric functions that could be used to achieve your tests (all functions listed return 1).
- MySQL numeric function example: POW(1,1)
- Oracle numeric function example: BITAND(1,1)
- SQL Server numeric function example: SQUARE(1)
Given the attacker would like to know if the underlying database is MySQL, he would do the following test.
Vulnerable URL example.
Inference fingerprint test on numeric value (MySQL).
If both requests show the same page, we can conclude that MySQL is probably the backend database. Otherwise the same test should be done with Oracle and MSSQL functions.
Here again, any function returning predictable text could be used but since concatenation operators are different from a DBMS to another, it is an excellent alternative to functions. Below is a reminder of concatenation operators for the most popular DBMS (all the following example is equivalent to "abcdef").
- Oracle concatenation example: 'abc' || 'def'
- MySQL concatenation example: 'abc' 'def'
- SQL Server concatenation example: 'abc' + 'def'
The inference test is pretty similar to what was presented for numeric input. Here is a classic example for text input.
Vulnerable URL example.
Inference fingerprint test on numeric value (MySQL). Space character is not URL-Encoded to simplify example.
If the same page is returned, MySQL is most likely the DBMS used by the application.
The inference approach could be generalized. In fact the same result would be obtained, by using blind SQL injection in conjunction with any function that exists in only one DBMS. In most cases, a well-crafted SQL segment containing a time-based test will do the job.
When none of the techniques presented earlier works, assumptions can be made about the database used. The good news is that there is a strong correlation between some technologies and DBMS. For example, a Web application built using ASP.NET is likely to be interacting with an SQL Server database, a PHP website will probably extract its data from MySQL, etc. Common sense will give you a rough idea of how you should orient your SQL injection testing.
The last option would be to fingerprint the database with network scanning tools like nmap, however this technique does not rely on SQL injection and is out of the scope of this article. For more information about this subject take a look at this nmap version detection article.