Ars Longa, Vita Brevis

Март 11, 2008

Безопасность, о которой все так много говорят…

Рубрика: MySQL, PHP, Security
Метки: , , , , , , ,
Vladimir @ 5:10 дп
RSS 2.0

Простой способ сломать сайт. Может быть, Ваши сайты тоже уязвимы?

Специалисты по безопасности, пожалуй, всем уже проели плешь, говоря о том, что все данные, приходящие от пользователя, нужно тщательно проверять… Казалось бы, “азбучные истины”, этому должны учить в школе :-) . Мне стало интересно: а многие ли сайты действительно защищены?

Внимание: материал предоставлен только в ознакомительных/образовательных целях! Автор не несёт ответственности за всё, что может случиться :-)

Одна из самых распространённых атак (я не беру DoS, DDoS и иже с ними) — это SQL Injection Attack.

Как пишет Wikipedia,

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

Инъекция SQL, в зависимости от типа используемой СУБД и условий инъекции, может дать возможность атакующему выполнить произвольный запрос к базе данных (например, прочитать содержимое любых таблиц, удалить, изменить или добавить данные), получить возможность чтения и/или записи локальных файлов и выполнения произвольных команд на атакуемом сервере.

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

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

    $id = $_GET['id'];
    $query = "SELECT * FROM {$members} WHERE id = $id LIMIT 1";
    $res = mysql_query($query, $link);

Всем хорош код, когда $id — это число. Например, если скрипт был вызван script.php?id=123, то запрос примет форму

SELECT * FROM members WHERE id = 5 LIMIT 1

Всё хорошо, но встречаются нехорошие дяди, которые вместо числа могут скормить скрипту всякую каку: для данного простого примера достаточно передать скрипту в параметрах script.php?id=0%20OR%201, чтобы заставить его выбрать все записи (о том, как обойти LIMIT, чуть ниже). Фактически, запрос будет переписан в виде

SELECT * FROM members WHERE id = 0 OR 1 LIMIT 1

Так как практически всегда для ID используются автоинкрементные поля, то выражение (id = 0) будет всегда ложно. А (FALSE OR 1) — всегда истинно.

Теперь как заставить скрипт обойти LIMIT. Всё просто: script.php?id=0%20OR%201%20LIMIT%20A,B/*%20-

Вместо A и B — значения для offset и count соответственно :).

SELECT * FROM members WHERE id = 0 OR 1 LIMIT 5,1/*-- LIMIT 1

Что примечательно: MySQL успешно выполнит запрос не смотря на то, что комментарий-то не закрыт. А так как /* */ — многострочный комментарий, то запрос отлично выполнится, даже если он многострочный.

Кстати, есть и другой метод — более простой, но не всегда рабочий — просто передать нужный id, и не париться с запросом. Очевидно, что если скрипт делает выборку не только по id, но и по другому полю (например, WHERE `id` = 'id' AND `member_id` = 'member_id'), то простая подмена числа не сработает. А инъекция будет работать.

Сама по себе техника простая, но очень эффективная. Когда я работал над сайтом uk-swingers.com (исправлял баги), я показал заказчику, как из его базы можно было воровать… номера кредитных карточек с кодами CVV, информацией о владельце и прочими данными. То, что хранение подобной информации в БД запрещено, это другая история… Да, он шифровал информацию в БД (при помощи алгоритма RC4 :-) ). Да, он стирал 4 средние цифры из номера кредитки (через несколько часов после выполнения транзакции). А толку-то? Данные могли быть зашифрованы как угодно, но браузеру они передаются в открытом виде. А номер кредитной карточки можно легко восстановить — во-первых, можно угадать BIN, исходя из территориальной близости к банку, во-вторых, перебором можно вычислить возможные номера карточки (для валидации номера используется формула Луна). На другом сайте (имя пока открыть не могу) так можно было читать чужие письма и воровать почтовые адреса и пароли (и устраивать кучу других гадостей).

Немного сложнее со строками. Я провел исследование, и выяснил, что если строка передается на сервер из <input type=”hidden”>, то она далеко не всегда экранируется.

Пример с реального сайта (кстати, писали даже не индусы, а вьетнамцы):

    $name = $_REQUEST['member'];
    $query = "SELECT * FROM {$prefix}profiles WHERE user = '$name'";
    $res = mysql_query($query, $link);

script.php?member=testdavid

SELECT * FROM tuvinh_profiles WHERE user = 'testdavid'

Боремся аналогично: так как строки в MySQL заключаются в одинарные или двойные кавычки, то нужно модифицировать запрос так, чтобы закрыть кавычку:

SELECT * FROM tuvinh_profiles WHERE user = '' OR 1 /* " OR 1 /*
SELECT * FROM tuvinh_profiles WHERE user = "' OR 1 /* " OR 1 /*

Аналогично вышеприведённому примеру, в запрос можно добавить LIMIT и ORDER BY — оставляю это в качестве упражнения читателю.

Ладно, хватит голой теории, переходим к практике :-)
Большую популярность получили сайты, публикующие различные статьи различных авторов: владельцы сайтов получают контент, авторы — обратную ссылку на свой сайт и возможность показывать свою рекламу. Таких сайтов много: articledashboard.com, articledirectory.com, webarticles.com и т.п.

Один из таких сайтов (articledashboard.com) писали индусы :-) Заинтересовали они меня тем (articledashboard.com, не индусы), что они предлагали скачать “exact software that powers Article Dashboard for FREE”. Кто не любит халяву :-) Хотя PHP-код зашифрован ionCube, индусское авторство доказывается наличием огромного файла functions.php (я подобное видел во всех проектах, когда приходилось переделывать код после индусов). А индусы не любят делать addslashes() :-) . Тоже из опыта.

Итак, пришли на сайт, логинимся. Какой у нас username и password? Правильно, ‘ OR 1 /*.
В результате получили:

Теперь попытаемся попасть в админку: http://www.articledashboard.com/admin/
Увы, не судьба — требуется авторизация. Ну и ладно, не очень-то хотелось. Придётся ломать не админку, а сайт целиком . И это довольно легко!

На сайте есть хорошая функция — загрузка своей аватарки. Посмотрим, умеют ли индусы работать с GD2.

Есть очень хороший extension для FireFox, который позволяет подделывать mime-type. То есть фактически мы можем проинструктировать FireFox посылать произвольный файл (например, php) с mime-type, соответствующим изображению (например, image/png).

Пошлём серверу PHP-файл с mime-type image/png и посмотрим, что получится.

Сработало!

Ладно, это я добрый, а ведь мог и не phpinfo загрузить, а backdoor какой-нибудь :-)

И на закуску: все эти сайты работают под ArticleDirectory :-)

А вы говорите, безопасность…

2 Комментариев »

  1. Update: несколько дней назад я предупредил владельцев сайта Article Dashboard о том, что их ПО (а также то ПО, которое они распространяют) уязвимо. Ответа от них не последовало. Что же, отсутствие ответа — тоже ответ. Но вот только решение проблем безопасности страусиным методом еще не разу себя не оправдывало.

    Комментарий от Vladimir — Март 15, 2008 @ 3:43 пп

  2. А вот и первые жертвы:

    1. solarpanelarticles.com
    2. bestwrittenarticles.com
    3. greatarticlesformoms.com
    4. my-article-dashboard.com
    5. bestglobalwarmingarticles.com

    Комментарий от Vladimir — Март 15, 2008 @ 7:21 пп

RSS комментариев к этой записи. URL обратной ссылки

Оставить комментарий