Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
В этой статье я разберу тему SQL-инъекций с самого начала — для тех, кто только осваивается в мире инфосека. Мы пройдемся по базовым уязвимостям и пошагово разберем несложные атаки. Потом поговорим о sqlmap — инструменте для автоматического пентеста SQLi. В конце порекомендую ресурсы для дальнейшего изучения. warning
Статья имеет ознакомительный характер и предназначена для обучения специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
От редакции
Если ты крутой пентестер и тема, о которой мы сегодня будем говорить, для тебя неактуальна уже лет двадцать, то не волнуйся. «Хакер» обязательно продолжит радовать тебя хардкорными материалами. Но мы буквально обязаны иметь и что‑то для новичков. Да и о базовых вещах стоит время от времени писать заново, с учетом современных реалий.
SQL (Structured Query Language) — это язык программирования, который почти повсеместно используется для работы с базами данных. В частности, он пригождается при разработке сайтов. Давай посмотрим, как выглядит типичный сценарий его применения.
Предположим, у нас есть интернет‑магазин, торгующий спортивными товарами, и пользователь Васёк, желающий купить гантели.
Что же такое база данных? Так часто называют, во‑первых, саму программу, работающую на сервере (точнее, она называется СУБД — система управления базами данных), а во‑вторых, файл или несколько файлов, в которых в специальном формате хранятся данные.
Данные записываются в виде таблиц, а в таблицах — столбцы и строки, как в документе 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 1=1
.
В коде после подстановки значения это будет выглядеть вот так:
SELECT * FROM users WHERE username = '' or 1=1 -- - ' AND password = '$user_input_password'"
На русском наш запрос можно прочесть так: «Выбери все строки из базы данных users
, где имя пользователя пустое ИЛИ единица, равная единице». А поскольку единица всегда равна единице, это условие будет соблюдаться для каждой строки.
Результат — мы в админке!
Однако Васька не интересует содержимое админки, цены на гантели он может посмотреть и на сайте. Его цель — таблица с именами пользователей и паролями. И они ведь наверняка хранятся в той же базе данных!
Источник: xakep.ru