🤯Error-Based SubQuery

Error-Based SubQuery Injection Explanation

Где я?

Это страница с дополнительным материалом, который предназначен для помощи студентам, изучающим курс SQL Injection Master.

Материалы на этой странице подробно объясняют, как работает Error-Based SubQuery инъекции.

circle-exclamation

Статья была скопирована на случай, если оригинальная статья будет удалена или доступ к ней будет потерян.

Суть инъекции

Общая суть инъекции довольно простая и похожа на XPATH инъекции. Мы выводим данные из БД с помощью ошибки MySQL.

Подобных вариацией инъекций довольно много и при желании их можно найти в интернете.

Некоторые позволяют выводить до 475 символов (но это с поправкой, что всё зависит от версии MySQL)

Почему это работает

Максимально упростим запрос и сделаем его нагляднее:

SELECT COUNT(*) FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)x GROUP BY FLOOR(RAND()*2)

Рассмотрим запрос внимательно. В нем 3 строки группируются по полю RAND (при этом FLOOR(RAND()*2) может вернуть либо 1 либо 0).

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

group_key — как уже понятно — является результатом вычисления FLOOR(RAND()*2) для каждой строки. При этом group_key является уникальным индексом в этой таблице, т.е. два значения group_key равным например «1» — недопустимая ситуация.

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

(на самом деле немного не так, возможно связано с какими-то дополнительными оптимизациями, надо лезть в исходники, мне лень)

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

Теперь представим на секунду что FLOOR(RAND()*2)) выдаст следующий порядок значений на каждый вызов 1 1 1 1 0 1, то временная таблица будет заполнятся следующим образом:

В итоге на последнем запросе мы видим ошибку «Duplicate entry«, так как он пытается вставить во временную таблицу второе уже существующее значение уникального индекса. Т.е. MySQL проверил что «group_key = 0» еще нет в таблице, и решил добавить его, но при этом при добавлении вызывал еще раз RAND, которое вернуло другое значение «1«, которое уже существует в таблице.

Вернемся к оригинальному запросу (допустим мы хотим получить VERSION()):

Для RAND здесь указан конкретный seed равный 0 (RAND(0) <----- где "0" это seed). seed — это начальное значение с помощью которого генерируются остальные случайные числа. Т.е. для всех одинаковых реализаций генератора случайных чисел с одинаковым заданным seed должен выдаваться одинаковый набор чисел. В случае MySQL гарантируется что для одинаковых версий будет сгенерирован одинаковый набор чисел.

При выполнении этого запроса group_key будет выглядеть как то так 5.5.251 и заполнятся временная таблица будет следующим образом:

На последнем запросе мы получим желанную ошибку: #1062 - Duplicate entry '5.5.251' for key 'group_key'

Если подобрать другой seed, например 33 (еще подойдут 37,41,45,49,53,57,61,65,…), так чтобы порядок значений выдаваемых RAND сыграл нам на руку раньше, то можно немного сократить запрос:

На таком наборе данных мы избегаем второго шага, и тем самым нам достаточно всего двух строк в подзапросе.

Заключение

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

Если кто-то хочет потренироваться с данным тип инъекций локально, то можно воспользоваться тестовым стендом. [Скачать]arrow-up-right

Если кто-то не доверяет файлам из интернета, то всегда можно проверить код php файла, он предельно простой и в нём нет никаких скрытых закладок!

Всем успехов! BearSec

Last updated