Оригинал: https://www.kalitutorials.net/2015/02/blind-sql-injection.html

Продолжайте только если уже познакомились с основами SQL инъекций. Если нет, сначала прочитайте эти посты:

  1. SQL-инъекции: простое объяснение для начинающих

  2. Ручные SQL инъекции (с помощью одного только браузера)

  3. Автоматические SQL инъекции с помощью SQLMap (Kali Linux)

  4. Как запустить sqlmap на Windows

Что нам уже известно

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

Теперь вспомним, что мы делали в руководстве по ручным SQL инъекциям:

  1. Нашли потенциально уязвимый веб-сайт https://testphp.vulnweb.com

  2. Использовали звёздочку ( * ), чтобы подтвердить уязвимость.

  3. Выяснили число строк и столбцов, внеся небольшие изменения в URL (что в итоге изменило запрос, выполняющийся на сервере).

  4. Затем мы получили названия таблиц, их колонки и, в конце концов, извлекли данные.

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

Что я вам не рассказал

Я подробно объяснил каждый этап. Но я не стал говорить о том, зачем нужен тот или иной шаг (об этом немного написано в посте основы SQL инъекций).

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

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

SQL инъекции промежуточного уровня (В Википедии есть отличная статься по SQLi, поэтому я несколько сократил её и опубликовал здесь с точки зрения хакера).

Пример SQL инъекции с объяснениями (Этот пост не слишком полезен для реального взлома, но очень хорошо поясняет концепцию с примерами. PS: Это внешняя ссылка. Поскольку содержимое статьи не подпадает под лицензию Creative Commons, я не мог просто скопировать важные моменты и опубликовать их здесь, так что вам придётся посетить данный сайт).

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

Поиск подходящего сайта

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

Возникает проблема. На слепые SQLi уходит очень много времени. Всегда пытайтесь воспользоваться классическими атаками и переходите к слепым SQLi только если они не дали результатов. Я не могу найти сайт, хозяева которого были бы не против взлома, так что придётся воспользоваться старым добрым testphp.vulnweb.com. URL, который мы собираемся атаковать, уязвим к классическим SQLi. Тем не менее, слепые SQLi включат немало гаданий и тот факт, что я могу использоваться SQL инъекции на базе оператора union (классические инъекции, которые мы уже прошли), чтобы выяснить названия таблиц и другие данные, значительно облегчает написание данного руководства. Приступим.

Как понять, уязвима ли наша цель

Нашей целью в данном практическом примере будет следующая страница:

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

Но теперь возникает другая проблема.

Если сайт не возвращает никаких ошибок, как понять, что он уязвим? Решение довольно элегантно. Эта атака основана на булевой алгебре. Она интуитивна и удивительно проста.

Базовая концепция заключается в следующем:

Кроме этого,

Теперь взгляните на следующие ссылки:

Основным условием для определения уязвимости веб-сайта к инъекциям, является выяснение того, исполняет ли он отправляемый нами код или просто его игнорирует. Ранее мы использовали звёздочку, и появление ошибки сигнализировало о том, что код действительно обрабатывается. На этот раз ошибки не отображаются, поэтому мы воспользуемся логикой. В первом URL условие является истинным, и страница отображается как обычно. Фактически, мы просим таблицу показать страницу, если «выбрана категория 2» и «1 равно 1». Оба условия выполняются и страница загружается. Во втором случае «выбрана категория 2», но «1 не равно 2», поэтому условие не выполняется и ничего не отображается. Какой вывод можно из этого сделать? Мы заключаем, что код, добавленный к URL, обрабатывается программным обеспечением СУБД (обычно MySQL).

Поиск других подробностей

Процесс выяснения остальных подробностей будет идентичен. Теперь мы знаем, что если вставить после and истинное выражение, страница будет отображаться, а иначе — нет. Мы можем просто продолжать угадывать параметры, пока не окажемся правы. То есть, пока страница не загрузится нормально.

Определение версии

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

Слепая sql инъекция

Однако нам не обязательно знать точную версию. Достаточно понять, что это MySQL 4 или MySQL 5. Для этого мы можем извлечь подстроку из номера версии, которая в нашем случае будет её первым символом. Это можно сделать, воспользовавшись функцией substr(@@version,1,1). Функция @@version возвращает всю строку полностью, а с параметрами 1,1 оставляет только первый её символ. Далее её можно сравнить с 4 или 5, чтобы определить, какую версию использует веб-сайт.

PS: Я вставил сюда этот скриншот, чтобы объяснить, почему мы воспользовались подстрокой. Хотя в данном случае версия SQL нам была уже известна, в реальной ситуации этой информации у вас не будет. Даже если вы не будете иметь никакого понятия о номере версии, выяснить его помогут URL ниже. Подробнее о подстроках вы можете прочитать здесь.

Определение версии

Как вы уже могли догадаться, в нашем примере 5 версия, поскольку URL с этим числом не возвращает пустую страницу. Надеюсь, вы понимаете общий принцип.

Поиск таблиц, столбцов и записей

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

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

  • Проблема: Как выяснить имена таблиц, если сайт не показывает результаты запросов?
  • Решение: Мы может продолжать то, что делали до этого. Задайте сайту условие имя таблицы = X, где X — наш вариант названия. Повторяйте эту процедуру, пока условие не окажется верным. Это значит, что таблица с таким именем действительно существует.
  • Проблема: Это просто концепция, как применить её на практике? Как попросить базу данных вернуть истинный результат, если вы угадали имя таблицы? Не может же вариант с 1=1 подходить для всех случаев?
  • Решение: Воспользуемся запросом выбора — select 1 from X. Если существует таблица под названием X, тогда результатом будет единица. Мы можем использовать этот результат для создания условия. (select 1 from X) = 1. Если таблица X существует, запрос выдаст 1. А раз 1=1, условие будет истинным. Если X не существует, условие будет ложным.
  • Проблема: Что если у нас не получится угадать название таблицы?
  • Решение: Есть ещё 2 альтернативы. Первая — воспользоваться функцией substr, так же как во время выяснения номера версии, постепенно подбирая символы имени таблицы. Фактически, мы спросим таблицу, является ли первым символом её названия a. Если ответ отрицательный, переходим к b, c, d и так далее. Затем повторяем это для второго символа. Таким образом, мы гарантированно выявим название таблицы (надеюсь, вы начинаете понимать, почему эту атаку назвали слепой SQL инъекцией).

Альтернативное решение: Мы можем воспользоваться значениями ASCII, чтобы ускорить вышеописанный процесс. Напрямую сравнивать символы нельзя. Число 6 больше 5, но b не больше a. Символы так сравнить не получится. Однако мы можем обратиться к их ASCII формам, поскольку каждая буква алфавита в ASCII выражается числом. Мы можем использовать данный факт, чтобы спросить таблицу, будет ли первый символ её имени больше P или меньше. Если больше, нам не придётся проверять алфавит до P и наоборот (это просто концепция, я продемонстрирую её на примере ниже).

Сейчас я просто попробую угадать название таблицы. Оставшиеся 2 концепции будут показаны во время поиска имени столбца и значений данных.

Ограничение запроса: Стоит отметить, что запрос выбора возвращает все результаты из определённой таблицы, а не только первый. К примеру, если в таблице 500 записей, и вы запрашиваете, строки, первым символом в которых будет «a», результатом будет не одна, а все записи с первой буквой «a». Этого мы не хотим. Чтобы избежать подобной ситуации, воспользуйтесь оператором Limit.

Вот краткий обзор. Узнать об операторе Limit подробнее можно здесь.

Давайте посмотрим, что значат параметры отступ и количество в операторе LIMIT:

  • Отступ указываете смещение от первого ряда таблицы. Не забывайте, что отсчёт начинается с 0, а не 1.

  • Количество указывает на максимальное число возвращаемых рядов.

Мы рассмотрели все концепции. Надеюсь, вы сможете прочитать все команды и понять, что они обозначают.

Название таблицы

Постараемся угадать имя таблицы.

Имя таблицы 312464Во время выполнения настоящей слепой SQL инъекции сообщение об ошибке отображаться не будет. Мы увидим пустую страницу, как и ранее.

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

Страница загружается нормально. Значит, таблица под названием users и правда существует.

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

PS: Здесь не обязательно использовать оператор limit, поскольку мы угадали имя таблицы целиком, а не по одному символу.

Название столбца

1. Угадывание полного имени

Есть 2 способа получить название колонки. Первый — угадать его полностью, как мы сделали с именем таблицы.

https://testphp.vulnweb.com/listproducts.php?cat=2 and (SELECT substring(concat(1,username),1,1) from users limit 0,1)=1

https://testphp.vulnweb.com/listproducts.php?cat=2 and (SELECT substring(concat(1,uname),1,1) from users limit 0,1)=1

Для uname страница отображается нормально, поэтому мы знаем, что столбец с таким именем существует. Чтобы попрактиковаться, вы можете заменить uname на pass, cc, address, email, name, phone или cart1. Все эти столбцы тоже присутствуют в таблице.

2. Угадывание по одному символу с помощью знака равенства (=)

Второй способ — продвижение от одного символа к другому. Это можно сделать двумя путями. Вы можете угадывать символы напрямую или искать промежуток, в котором находится нужный символ и угадывать его только после этого. Я покажу оба. Данный метод требует наличия information_schema, то есть сработает только в MySQL версии 5, но не 4.  

Здесь я воспользовался числом 117. В реальной ситуации вам придётся перебрать все возможные ascii коды (от 65 до 122 — от A до z)

https://testphp.vulnweb.com/listproducts.php?cat=2 and ascii(substring((select concat(column_name) from information_schema.columns where table_name=0x7573657273+limit 0,1),1,1))= 117

PS: Я проверил, выполняет ли MySQL автоматическую конвертацию символов в их ASCII значения. Оказалось, что он действительно это делает. Так что мы можем немного сократить запрос. Таким образом, в противоположность тому, что я говорил ранее, b и правда больше a. Вот тот же самый код, только с u вместо 117

https://testphp.vulnweb.com/listproducts.php?cat=2%20and%20substring((select%20concat(column_name)%20from%20information_schema.columns%20where%20table_name=0x7573657273+limit%200,1),1,1)='u'

164 — это ASCII для u. Мы знаем, что колонка называется uname, так что страница должна отображаться нормально. Может попробовать значения отличные от 85 и посмотреть что будет. Кроме этого, 7573657273 — hex-код для users (0x указывает на hex). Не забывайте, что можете проделать то же самое и при поиске названий таблиц, внеся несколько изменений. Во-первых, замените слово column в коде выше на table. Потребуются и другие правки. Вот как будет выглядеть финальный код:

3. Угадывание по одному символу c помощью оператора > или <, за которым следует =

Данный вариант почти идентичен предыдущему способу.

Теперь мы знаем, что нужный символ > 100 (100 — это ‘d’), так как страница загружается нормально.

Но он меньше чем 120 (‘x’), ведь в этом случае страница отображается с ошибками.

Больше чем 110 (‘n’)

Больше 155 («s»). Осталось всего 5 вариантов: 116, 117, 118, 119, 120 (нужный символ больше 116, но меньше 120). Мы можем перебрать их по очереди. Я отметил в запросах часть, отвечающую за ascii код. Вы можете удалить выделенный жирным текст и заменить его числами или символами и одинарных кавычках ('a', 'b' и так далее)

Наконец, мы достигаем успеха, на следующем запросе:

Однако нам известна лишь первая бука названия столбца. Чтобы найти второй символ, замените выделенную красным цветом цифру 1 на 2. Код будет выглядеть так:

Страница загрузится некорректно, поскольку вторым символом uname является n. (ascii 110)

Здесь вы тоже можете воспользоваться методом > < =. Всё кроме 2 останется неизменным.

Извлечение данных

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

Страница отображается неверно для 120 (x).

Итак, первый символ — «t». Для второго символа воспользуемся следующим кодом (теперь без ascii).

Нужная нам буква находится где-то между «b и «f».

Продолжаем попытки.

Второй символ — «e». Работайте, пока не получите полное значение из колонки uname. Вы можете убедиться, что символ является последним, воспользовавшись этой командой:

Если слева есть ещё символы, условие >0 всегда будет выполняться.


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