Blind SQL Injection: Are Your Web Applications Vulnerable?
Blind SQL Injection: Are Your Web Applications Vulnerable?
By SPI Labs
Start Secure. Stay Secure.™
Table of Contents
Introduction 3
Solutions 9
Parameterized Queries 10
Stored Procedures 12
Data Sanitization 15
Database Considerations 16
Contact Information 18
Introduction
SQL Injection occurs when an application does not properly validate user-
supplied input and then includes that input as part of a SQL statement. In a
large measure, SQL Injection depends on an attacker discovering and
verifying portions of the original SQL query using information gained from
error messages. However, web applications can still be vulnerable to Blind
SQL Injection attacks even when error messages are not presented, or when
they only reveal generic information. By altering the input parameters, an
attacker can pose various “true-false” statements to the application to gather
information about the database and then ultimately reconstruct the SQL
statement by gauging its behavior. Are different pages displayed as a result
of changed input? Does an inserted “wait” command cause the application to
pause before responding? The “blind” portion of this comes from the fact that
no significant error was presented, yet the application is still vulnerable. Blind
SQL Injection is no less dangerous than SQL Injection, and can have the
same devastating consequences. The objective of this paper is to educate
security professionals and developers on the techniques that can be used to
take advantage of a web application that is vulnerable to Blind SQL Injection,
and to make clear the correct mechanisms that should be put in place to
protect against Blind SQL Injection and similar input validation problems.
In many aspects, SQL Injection and Blind SQL Injection are exactly the
same. Blind SQL and SQL Injection are both facilitated by a common coding
error: the application accepts data from a client and executes SQL queries
without first validating the client’s input. The attacker is then free to extract,
modify, add, or delete content from the database. In some circumstances, he
may even penetrate past the database server and into the underlying
operating system. An in-depth guide to SQL Injection can be found here:
http://www.spidynamics.com/whitepapers/WhitepaperSQLInjection.pdf.
Since Blind SQL Injection attacks do not rely on error messages, there are no
specific patterns or strings to look for in the web server’s response. Instead,
an attacker will look to see if two requests with different parameter values
will return the same information. In essence, Blind SQL Injection attacks are
an attempt to recreate the query in a such a way that the meaning stays the
same, but its content differs.
For instance, many companies allow Internet access to archives of their press
releases. A URL for accessing the company’s fifth press release might look
like this:
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5
The SQL statement the web application would use to retrieve the press
release might look like this (client-supplied input is underlined):
The database server responds by returning the data for the fifth press
release. The web application will then format the press release data into an
HTML page and send the response to the client.
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
1=1
. . . and if this query also returns the same press release, then the
application is susceptible to Blind SQL injection. Part of the user’s input was
interpreted as SQL code.
A secure application would reject this request because it would treat the
user’s input as a value, and the value “5 AND 1=1” would cause a type
mismatch error. The server would not display a press release.
It is also important to make sure that inserting “1=1” does not yield results
based on a flaw in the application as opposed to Blind SQL Injection. You can
do this by inserting “1=2”, an untrue condition, into the SQL query. If the
results for each query are the same , then SQL Injection has not been shown
to exist.
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
USER_NAME() = 'dbo'
USER_NAME() is a SQL Server function that returns the name of the current
user. If the current user is dbo (administrator), the fifth press release will be
returned. If not, the query will fail and no press release will be displayed.
By combining subqueries and functions, more complex questions can be
posed. The following example attempts to retrieve the name of a database
table, one character at a time.
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE
xtype='U'), 1, 1))) > 109
The subquery (SELECT) is asking for the name of the first user table in the
database (which is typically the first thing to do in SQL injection
exploitation). The substring() function will return the first character of the
query’s result. The lower() function will simply convert that character to
lower case. Finally, the ascii() function will return the ASCII value of this
character.
If the server returns the fifth press release in response to this URL, we know
that the first letter of the query’s result comes after the letter “m” (ASCII
character 109) in the alphabet. By making multiple requests, we can
determine the precise ASCII value.
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE
xtype='U'), 1, 1))) > 116
If no press release is returned, the ASCII value is greater than 109 but not
greater than 116. So, the letter is between “n” (110) and “t” (116).
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE
xtype='U'), 1, 1))) > 113
Another false statement. We now know that the letter is between 110 and
113.
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE
xtype='U'), 1, 1))) > 111
False again. The range is narrowed down to two letters: ‘n’ and ‘o’ (110 and
111).
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE
xtype='U'), 1, 1))) = 111
The server returns the press release, so the statement is true! The first letter
of the query’s result (and the table’s name) is “o.” To retrieve the second
letter, repeat the process, but change the second argument in the
substring() function so that the next character of the result is extracted:
(change underlined)
http://www.thecompany.com/pressRelease.jsp?pressReleaseID=5 AND
ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE
xtype='U'), 2, 1))) > 109
Repeat this process until the entire string is extracted. In this case, the result
is “orders.”
Solutions
Fundamentally, Blind SQL and SQL Injection are an attack upon the web
application, not the web server or the operating system itself. In as such,
any fixes other than those implemented in the application code will be
stopgap measures and short term solutions, at best. Most methods of
preventing Blind SQL and SQL Injection also have their own set of unique
Parameterized Queries
SQL Injection arises from an attacker’s manipulation of query data to modify
query logic. Therefore, the best method of preventing both Blind SQL and
SQL Injection attacks is to separate the logic of a query from its data. This
will prevent commands inserted from user input from being executed. The
downside of this approach, albeit slight, is that it can have an impact on
performance, and that each query on the site must be structured in this
method for it to be completely effective. If one query is inadvertently
bypassed, that could be enough to leave the application vulnerable. The
following code shows a sample SQL statement that is SQL injectable.
The following example utilizes parameterized queries, and is safe from SQL
Injection attacks.
The application will send the SQL statement to the server without including
the user’s input. Instead, a parameter-@LocationID- is used as a placeholder
for that input. In this way, user input never becomes part of the command
that SQL executes. Any input that an attacker inserts will be effectively
negated. An error would still be generated, but it would be a simple data-
type conversion error, and not something which an attacker could exploit.
The following code samples show a product ID being obtained from an HTTP
query string, and used in a SQL query. Note how the string containing the
“SELECT” statement passed to SqlCommand is simply a static string, and is
not concatenated from input. Also note how the input parameter is passed
using a SqlParameter object, whose name (“@pid”) matches the name used
within the SQL query.
C# sample:
string connString =
WebConfigurationManager.ConnectionStrings["myConn"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
VB.NET sample:
Stored Procedures
Another method of separating query logic from its data is by using stored
procedures to isolate the web application from SQL altogether. To secure an
application against Blind SQL injection, developers must prevent client-
supplied data from modifying the syntax of SQL statements. All SQL
statements required by the application can be sequestered in stored
procedures and kept on the database server. Be aware that simply moving all
SQL statements into stored procedures will not solve Blind SQL and SQL
Injection problems if you use input parameters without first validating the
data. Stored procedures allow programmers to build dynamic SQL
statements using string concatenation which can then be executed using
EXEC commands. However, this will defeat using stored procedures for
security purposes if you use input parameters without sanitizing the data
The first step toward securing this code is to take the SQL statement out of
the web application and put it in a stored procedure on the database server.
CallableStatement cs = dbConnection.prepareCall(“{call
getPressRelease(?)}”);
cs.setInt(1,
Integer.parseInt(request.getParameter(“pressReleaseID”)));
ResultSet rs = cs.executeQuery();
command.CommandType = CommandType.Text;
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@PressReleaseID",SqlDbType.Int);
command.Parameters[0].Value =
Convert.ToInt32(Request["pressReleaseID"]);
Data Sanitization
The vast majority of Blind SQL Injection vulnerabilities can be prevented by
properly validating user input for both type and format. All client-supplied
data needs to be cleansed of any characters or strings that could possibly be
used maliciously. This should be done for all applications, not just those that
use SQL queries. The best method of doing this is via “white listing”. This is
defined as only accepting specific data for specific fields, such as limiting user
input to account numbers or account types for those relevant fields, or only
accepting integers or letters of the English alphabet for others. Many
developers will try to validate input by “black listing” characters, or
“escaping” them. Basically, this entails rejecting known bad data, such as a
single quotation mark, by placing an “escape” character in front of it so that
the item that follows will be treated as a literal value. Stripping quotes or
putting backslashes in front of them is not enough, and is not as effective as
white listing because it is impossible to know all forms of bad data ahead of
time.
s/[^0-9a-zA-Z]//\
Make your filter narrow and specific. Whenever possible, use only numbers.
After that, numbers and letters only. If you need to include symbols or
punctuation of any kind, make absolutely sure to convert them to HTML
substitutes, such as "e; or >. For instance, if the user is submitting an
e-mail address, allow only the “at” sign, underscore, period, and hyphen in
addition to numbers and letters, and allow them only after those characters
have been converted to their HTML substitutes.
Database Considerations
Limit the rights of the database user. Any successful Blind SQL Injection
attack would run in the context of the user’s credential. While limiting
privileges will not prevent SQL Injection attacks outright, it will make them
significantly harder to enact. Don’t give that user access to all of the system-
stored procedures if that user needs access to only a handful of user-defined
ones.
Contact Information