Содержание статьи
- Что такое SQL?
- Как выглядит запрос к приложению
- Проход в админку
- UNION-based SQLi
- Определяем число столбцов в таблице
- Узнаём версию БД
- Выводим список баз данных
- Выводим список таблиц в базе данных
- Выводим список столбцов в таблице
- Выводим информацию из нужных столбцов
- Sqlmap
- SQLi в дикой природе
- Где практиковаться?
warning
Статья имеет ознакомительный характер и предназначена для обучения специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
От редакции
Если ты крутой пентестер и тема, о которой мы сегодня будем говорить, для тебя неактуальна уже лет двадцать, то не волнуйся. «Хакер» обязательно продолжит радовать тебя хардкорными материалами. Но мы буквально обязаны иметь и что‑то для новичков. Да и о базовых вещах стоит время от времени писать заново, с учетом современных реалий.
Что такое SQL?
SQL (Structured Query Language) — это язык программирования, который почти повсеместно используется для работы с базами данных. В частности, он пригождается при разработке сайтов. Давай посмотрим, как выглядит типичный сценарий его применения.
Предположим, у нас есть интернет‑магазин, торгующий спортивными товарами, и пользователь Васёк, желающий купить гантели.
- Васёк вводит в строку поиска слово «гантели» и нажимает «Искать».
- Когда кнопка нажата, браузер запрашивает страницу поиска и передает на сервер строку «гантели».
- Работающее на сервере веб‑приложение, чтобы поискать в базе данных, формирует запрос к ней. Для этого в приложение программистом заложена команда на SQL, задающая условия поиска. В нее и подставляется полученное от пользователя слово «гантели».
- Получив запрос, база данных ищет подходящие под условия строки в таблице товаров и выдает их веб‑приложению.
- Веб‑приложение объединяет результат поиска с шаблоном страницы и выдает результат браузеру Васька. Васёк видит красивую страничку с изображением гантелей, ценами и прочей инфой.
Что же такое база данных? Так часто называют, во‑первых, саму программу, работающую на сервере (точнее, она называется СУБД — система управления базами данных), а во‑вторых, файл или несколько файлов, в которых в специальном формате хранятся данные.
Данные записываются в виде таблиц, а в таблицах — столбцы и строки, как в документе Excel. Зачастую одна СУБД может управлять сразу несколькими базами данных, каждая из которых уже содержит в себе таблицы.
Рекомендую поставить на свой компьютер любую СУБД и посмотреть на нее поближе самостоятельно. Проще всего будет запустить SQLite, а для просмотра данных можешь воспользоваться DB Browser. Более серьезные СУБД вроде MySQL, PostgreSQL и Microsoft SQL Server устроены чуть сложнее, но смысл совершенно тот же. И все они используют один язык запросов — SQL (хоть и с небольшими различиями).
Но вернемся к Ваську. Что, если ему нужны вовсе не гантели, а, например, данные других пользователей или возможность зайти в админку сайта? Какой запрос ему сделать тогда? Давай разбираться.
Как выглядит запрос к приложению
Чаще всего уязвимости типа SQLi возникают в поиске, комментариях и панелях администрирования, так что Ваську нужно внимательнее всего смотреть на параметры, которые браузер передает на сервер при заполнении разных строк.
Давай разберем вот такой запрос:
http://just_usual_site_with_sqli.com/tovar?id=drel
Это запрос типа GET — когда передаваемые параметры указываются прямо в ссылке — после знака вопроса. Здесь параметр id
— это идентификатор товара. Именно через этот параметр мы чуть позже и попытаемся произвести инъекцию.
Нередко применяются и запросы типа POST, при которых параметры передаются уже не в адресной строке, а в HTTP-заголовке. Запрос, сделанный методом POST, будет выглядеть примерно так.
Здесь передается несколько параметров: postId
, comment
, name
и другие.
Проход в админку
Теперь давай посмотрим, как уязвимость будет выглядеть в коде веб‑приложения, которое Васёк пытается взломать. Для примера возьмем код из финального экзамена HTB Academy SQLi Fundamentals.
Предположим, мы нашли адрес админки сайта, но, чтобы зайти туда, нам нужно ввести имя пользователя и пароль. Вот кусок кода на PHP, который по заданному имени пользователя и паролю проверяет, есть ли в базе такая пара:
$servername = "just_simple_server"$username = "admin"; # Юзернейм$password = "password123"; # Пароль$dbname = "simple_database"# Подключение к базе данных$conn = new mysqli($servername, $username, $password, $dbname);# Сюда приходит пользовательский ввод$user_input_username = $_GET['username'];$user_input_password = $_GET['password'];# А здесь создается сам запрос в базу данных# и подставляется то, что ввел пользователь$sql = "SELECT * FROM users WHERE username = '$user_input_username' AND password = '$user_input_password'";$result = $conn->query($sql);# Проверка результатов: нашлись ли в базе записи с нужными логином и паролемif ($result->num_rows > 0) { echo "Успешный вход";} else { echo "Неверное имя пользователя или пароль";}
Нам с тобой особенно интересна переменная $sql
, именно в ней и будет происходить вся магия.
На русском этот запрос звучал бы так: «Выбери все строки из базы данных users
, где имя пользователя — $user_input_username
, а пароль — $user_input_password
». Причем вместо названий переменных в реальности будут подставлены значения, введенные пользователем в форму.
Сейчас мы с тобой проведем SQL-инъекцию. Тут важно не заходить сразу с козырей. В первую очередь нам надо передать кавычку, чтобы она закрыла запрос и он не превратился в строку, как подразумевалось.
Для начала надо определить, какие именно кавычки стоят в коде: одинарные или двойные (PHP допускает оба варианта). В нашем случае мы можем посмотреть код и ответить на этот вопрос: одинарные. Но при настоящем пентесте кода у нас, скорее всего, не будет, поэтому определять придется самим — методом проб и ошибок. А точнее, методом перебора строк по списку.
Варианты нагрузок
Я подготовил для тебя небольшой список строк для перебора.
'"#%'#%'--%')--%' or 1=1#%' or 1=1--%') or 1=1#%') or 1=1--' or 1=1--' or 1=1#') or 1=1#') or 1=1--%' and 1=1#%' and 1=1--%') and 1=1--%') and 1=1#' and 1=1#' and 1=1--') and 1=1#') and 1=1--%' or 2=2#%'or 33=33--%') or 55=55#%') orord(5)=ord(5)--' or 2=2#' or 33=33--') or 55=55#') or ord(5)=ord(5)--%' and (chr('a')=chr('a'))#%'and 4=4--%') and (ASCII('a')=ASCII('a'))#%') and 'w'='w'--' and(chr('a')=chr('a'))#' and 4=4--') and(ASCII('a')=ASCII('a'))#')and 'w'='w'--
Допустим, мы определили, что в коде используются одинарные кавычки. Отлично, значит, строку можно закрыть, отправив в запросе одинарную кавычку! Но само по себе это нам никак не поможет, ведь после подстановки имени пользователя получится вот такой код:
$sql = "SELECT * FROM users WHERE username = ''' AND password = '$user_input_password'";
После нашей кавычки идет кавычка, которая изначально была в коде, в результате SQL просто не поймет, что это за нагромождение кавычек, и выдаст ошибку. Запрошенная страница, скорее всего, не будет отображена вовсе.
Нам нужно добавить что‑то, кроме кавычки, чтобы последующая часть запроса просто не сработала. Проще всего в этом случае использовать комментирование. В MySQL комментарии отбиваются двумя черточками (--
). Все идущие дальше символы будет считаться комментарием, а не частью команды.
Чтобы учесть все возможные случаи, полезно добавлять к двум черточкам пробел и еще какой‑нибудь символ (я ставлю еще один прочерк: --
). Так мы удостоверимся, что ни при каких условиях не получится пустой комментарий, ведь такое во многих реализациях SQL недопустимо.
info
В разных БД синтаксис комментирования может различаться.
Вот как в итоге будет выглядеть рабочая нагрузка в форме ввода.
А вот она же в коде после подстановки:
SELECT * FROM users WHERE username = '' -- -' AND password = '$user_input_password'"
Проверка пароля больше не производится — это условие теперь часть комментария. Но нам все еще требуется имя пользователя, а его мы можем и не знать. Нельзя ли снять и это условие тоже?
Давай взглянем на код еще раз. Мы видим, что проверка происходит по количеству строк в ответе от базы данных. Если совпадений нет, возвращается ноль строк. Значит, нам надо сделать так, чтобы этих строк было не ноль. Как насчет того, чтобы вывести в ответ сразу все строки из БД? Для этого нужно создать условие, которое будет истинно для каждой строки. И сделать это просто — достаточно добавить or
.
В коде после подстановки значения это будет выглядеть вот так:
SELECT * FROM users WHERE username = '' or 1=1 -- - ' AND password = '$user_input_password'"
На русском наш запрос можно прочесть так: «Выбери все строки из базы данных users
, где имя пользователя пустое ИЛИ единица, равная единице». А поскольку единица всегда равна единице, это условие будет соблюдаться для каждой строки.
Результат — мы в админке!
UNION-based SQLi
Однако Васька не интересует содержимое админки, цены на гантели он может посмотреть и на сайте. Его цель — таблица с именами пользователей и паролями. И они ведь наверняка хранятся в той же базе данных!
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»