Безопасность MODX, часть 1 - обход фильтрации MODX тегов
Это первая часть доклада с конференции MODX Meetup Moscow. Информацию решил разбить на 2 статьи посвященных разным векторам атак. Полная запись доклада доступна в ВК, а на ютубе запись моего экрана. Ну и естественно слайды.
Для тех, кто в танке, скажу еще раз — парсер, который разбирает MODX теги рекурсивен. И благодаря именно этому возможна нежелательная обработка каких-то случайных данных полученных от пользователя. Разработчики движка это прекрасно понимают, потому позаботились о том, чтобы из получаемых данных вырезалось все, что хоть чем-то похоже на MODX теги.
Таким образом, если вы передадите сайту строчку похожую на
Hi [[++dsn]]!
В конечном счете это приводило к тому, что на выходе мы получали обработку тега и наблюдали параметры подключения к базе данных
Я согласен, что уязвимость 2012 года для обхода фильтрации тегов на данный момент уже не актуальна. Но тем не менее, вектор атаки остался прежним. Поскольку политика MODX — не воспринимать багрепорты с потенциальными угрозами безопасности, я считаю, что все разработчики должны знать как самостоятельно сделать свои сниппеты безопасными. Поэтому теперь давайте посмотрим на метод modX::sanitize и разберем различные типовые ситуации, которые помогут обойти фильтрацию.
Я создал тестовый сниппет с названием flag на демо-сайте. И вот несколько способов как его можно вызвать
Перед выводом пользовательских данных вы предусмотрительно воспользовались функцией strip_tags() или даже Jevix.
Таким образом, уязвимый сниппет в сильно упрощенном варианте будет выглядеть примерно следующим образом
Следующая ситуация немного необычная, но тем не менее, о ней стоит знать, т.к. все зависит от настроек вашего сайта. По умолчанию, метод modX::sanitize поддерживает массивы максимум с 99 уровнями вложенности. Соответственно если кто-то завернет вызов сниппета в массив с более чем 99 уровнями вложенности, то фильтрацию удастся обойти.
На данный момент от этого вектора атаки спасают лишь настройки PHP по умолчанию.
Максимальная вложенность массивов ограничена параметром max_input_nesting_level, который равен 64. Если в настройках вашего сервера установлено значение больше 100, то сайт потенциально уязвим.
Уязвимый сниппет на демо-сайте называется StageTwo. Для простоты, данные просто пропускаются через JSON:
Наконец мое любимое — вывод данных из сторонних источников. Это может быть список тем или комментариев с форума, который установлен у вас где-то на поддомене. Или даже лента сообщений из какой-нибудь социальной сети по определенному хеш-тегу.
Как можно догадаться, эксплуатация очень простая. Заходим на форум, создаем сообщение, в котором будут использоваться MODX теги. Затем открываем сайт и радуемся результату. 6 лет назад даже официальный сайт был подвержен этому вектору атаки
Как вы видите, способы обхода фильтрации достаточно типовые.
Поэтому единственное разумное решение — всегда подменять квадратные скобки на сущности, а не полагаться на встроенный обработчик.
P.S. В 2013 году был еще один способ обхода фильтрации, но о нем мало кто помнит и знает. Для истории и полноты статьи оставлю тут видео, которое тогда было передано в MODX для демонстрации уязвимости.
Для тех, кто в танке, скажу еще раз — парсер, который разбирает MODX теги рекурсивен. И благодаря именно этому возможна нежелательная обработка каких-то случайных данных полученных от пользователя. Разработчики движка это прекрасно понимают, потому позаботились о том, чтобы из получаемых данных вырезалось все, что хоть чем-то похоже на MODX теги.
Таким образом, если вы передадите сайту строчку похожую на
Hi [[++dsn]]!
То движок ее отфильтрует и вы увидите всего лишьHi !
В 2012 году мной был обнаружен способ обойти эту фильтрациюHi [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]++dsn]]!
И запрос с кучей скобок преобразовывался и становился вполне легитимным для MODXHi [[++dsn]]!
В конечном счете это приводило к тому, что на выходе мы получали обработку тега и наблюдали параметры подключения к базе данных
Hi mysql:host=127.0.0.1;dbname=modx;charset=utf8mb4!
На тот момент, из глобальных плейсхолдеров можно было узнать не только параметры подключения к базе через плейсхолдер [[++dsn]], но еще и логин-пароль пользователя этой самой базы. Сейчас плейсхолдеры с логином и паролем убрали. Но это не единственные плейсхолдеры которые стоит спрятать.- [[++emailsender]] Хранит в себе почтовый адрес отправителя. По умолчанию, после установки движка, этот адрес совпадает с почтовым ящиком администратора.
- [[++mail_smtp_pass]] — Содержит пароль от почты в случае если вы настроили отправку писем с использованием SMTP. Зачастую с этим паролем можно не только отправлять, но и читать почту.
Я согласен, что уязвимость 2012 года для обхода фильтрации тегов на данный момент уже не актуальна. Но тем не менее, вектор атаки остался прежним. Поскольку политика MODX — не воспринимать багрепорты с потенциальными угрозами безопасности, я считаю, что все разработчики должны знать как самостоятельно сделать свои сниппеты безопасными. Поэтому теперь давайте посмотрим на метод modX::sanitize и разберем различные типовые ситуации, которые помогут обойти фильтрацию.
Я создал тестовый сниппет с названием flag на демо-сайте. И вот несколько способов как его можно вызвать
Фильтрация HTML — способ 1
Перед выводом пользовательских данных вы предусмотрительно воспользовались функцией strip_tags() или даже Jevix.
Таким образом, уязвимый сниппет в сильно упрощенном варианте будет выглядеть примерно следующим образом
return strip_tags($_REQUEST['data']);
Соответственно строка [<b></b>[flag]<b></b>]
преобразуется во [[flag]]
Многомерные массивы — способ 2
Следующая ситуация немного необычная, но тем не менее, о ней стоит знать, т.к. все зависит от настроек вашего сайта. По умолчанию, метод modX::sanitize поддерживает массивы максимум с 99 уровнями вложенности. Соответственно если кто-то завернет вызов сниппета в массив с более чем 99 уровнями вложенности, то фильтрацию удастся обойти.
На данный момент от этого вектора атаки спасают лишь настройки PHP по умолчанию.
Максимальная вложенность массивов ограничена параметром max_input_nesting_level, который равен 64. Если в настройках вашего сервера установлено значение больше 100, то сайт потенциально уязвим.
Уязвимый сниппет на демо-сайте называется StageTwo. Для простоты, данные просто пропускаются через JSON:
return json_encode($_REQUEST['data'], JSON_FORCE_OBJECT);
Соответственно запросdata[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]=[[flag]]
Успешно обойдет фильтрацию, т.к. массив data имеет 100 уровень вложенности.Вывод данных из сторонних источников — способ 3
Наконец мое любимое — вывод данных из сторонних источников. Это может быть список тем или комментариев с форума, который установлен у вас где-то на поддомене. Или даже лента сообщений из какой-нибудь социальной сети по определенному хеш-тегу.
Как можно догадаться, эксплуатация очень простая. Заходим на форум, создаем сообщение, в котором будут использоваться MODX теги. Затем открываем сайт и радуемся результату. 6 лет назад даже официальный сайт был подвержен этому вектору атаки
Вывод
Как вы видите, способы обхода фильтрации достаточно типовые.
Поэтому единственное разумное решение — всегда подменять квадратные скобки на сущности, а не полагаться на встроенный обработчик.
[ - [
] - ]
P.S. В 2013 году был еще один способ обхода фильтрации, но о нем мало кто помнит и знает. Для истории и полноты статьи оставлю тут видео, которое тогда было передано в MODX для демонстрации уязвимости.
Поблагодарить автора
Отправить деньги
Комментарии: 34
2 способ будет не актуален после принятия PR #14098
Кстати, в Jevix есть параметр escapeTags — он по умолчанию выключен. Но если его включить, то все квадратные и фигурные скобки будут заменены на соответствующие HTML-коды.
Благодарю!
Спасибо Евгений. Очень познавательно!
А если используешь Fenom и его синтаксис?
Для аудита мне еще ни разу не попадался сайт с Fenom. Поэтому ничего не могу сказать
Проверил у себя, реально дыры…
А есть способ сделать фильтрацию всего, что вообще приходит в параметрах post и get? Т.е. прежде чем данные дойдут до какого-нибудь сниппета, они обработаются раньше? Чтобы исключить недостатки большинства компонентов.
А есть способ сделать фильтрацию всего, что вообще приходит в параметрах post и get? Т.е. прежде чем данные дойдут до какого-нибудь сниппета, они обработаются раньше? Чтобы исключить недостатки большинства компонентов.
Особо не силён, поэтому только догадки… Сниппеты обращаются к коннекторам, и практически все данные проходят через них. Возможно ли расширить какой-нибудь стандартный класс MODX, чтобы данные лучше фильтровать?
MODX фильтрует POST, GET, REQUEST, COOKIE в каждом запросе. Но этого, конечно, недостаточно. Фильтруйте сами. Например, так.
А ещё фильтруются эти значения
А ещё фильтруются эти значения
$targets= array ('PHP_SELF', 'HTTP_USER_AGENT', 'HTTP_REFERER', 'QUERY_STRING');
foreach ($targets as $target) {
$_SERVER[$target] = isset ($_SERVER[$target]) ? htmlspecialchars($_SERVER[$target], ENT_QUOTES) : null;
}
Спасибо за подсказку, способ нормальный, но если сам какой-нибудь сниппет пишу. А если компонент сторонний, то не иначе как в его внутренности лезть придётся. А сколько там данных передаётся и принимается. А если ставишь их по 20 штук… и там фильтрация слабенькая, или что-то упустил автор. Я просто довольно легко один сайт (свой) взломал по этой заметке, хотя далеко не спец в программировании, а в безопасности вообще нуб. Отсутствие фильтрации квадратных скобочек реально напрягает… В идеале было бы как-то в одном месте все данные фильтровать по каким-то своим правилам. Типа такой общий шлюз, сначала данные через него проходят, а потом уже в сниппеты и компоненты. Но не знаю возможно ли это.
Отсутствие фильтрации квадратных скобочек реально напрягает…Ну вот откуда Вы это взяли? Евгений же всё внятно объяснил.
В идеале было бы как-то в одном месте все данные фильтровать по каким-то своим правилам. Типа такой общий шлюз, сначала данные через него проходят, а потом уже в сниппеты и компоненты. Но не знаю возможно ли это.Без проблем. Плагин на событие OnMODXInit.
Ну вот откуда Вы это взяли? Евгений же всё внятно объяснил.Его же слова:
Как вы видите, способы обхода фильтрации достаточно типовые.Приведу пример. Есть компонент платный — Callbak. Я без проблем вписываю вызовы сниппетов или глобальных плейсхолдеров в поля формы обратного звонка, и на почту админу приходит ключ его сессии, или пароль от БД. Это хорошо, что штатно письмо только админу приходит. И это не единственный пример, который я знаю. И их может быть много больше. Да тот же FormIt в БД хранит не обработанные данные форм. Если они где-то выводятся на сайте, то картина будет похожая.
Поэтому единственное разумное решение — всегда подменять квадратные скобки на сущности, а не полагаться на встроенный обработчик.
Но, конечно, фильтровать вообще всё без разбора тоже не самый лучший вариант. Неплохо было бы какие-то стандарты написания и тестирования компонентов и сниппетов ввести, особенно платных, и какой-нибудь значок соответствующий — «фильтрует данные» (как в программах — проверено антивирусом), или что-то типа того. И в настройках системных, чтобы опции были как строго фильтровать данные, какие и т.д. Отдельный раздел по безопасности в документации к компоненту. Чтобы было ясно, что этот вопрос волновал разработчика. Как-то так… Есть же для верстальщиков валидатор, вот подобный чек-лист у разработчиков должен быть.
По-моему, это не выход. Фильтрация должна быть глобальной на уровне системы, иначе угроза будет постоянной.
По-моему, это не выход. Фильтрация должна быть глобальной на уровне системы, иначе угроза будет постоянной.Просто, например, может возникнуть необходимость хранить исходные данные от посетителей. Особенно если они нигде не выводятся на сайте. Опять же какие-то гибкие настройки должны быть, что фильтровать, а что нет, и как. Допустим, сниппет хочет вывести какой-то плейсхолдер, вот по умолчанию он должен фильтроваться перед выводом на сайте на уровне ядра. А если я как разработчик хочу отключить это поведение, то должен указать это системе (по аналогии с виндой — запуск от имени администратора). Короче, всё что во фронтенд выводится, всё фильтровать по идее нужно, по умолчанию. И тогда в БД можно хранить что угодно. А если ещё и на уровне приёма данных также сделать, то двойная защита будет. Тогда и новички в MODX, коих большинство, будут в безопасности. Скажем, хотят вывести
[Мой сайт], а и им MODX подменяет на [Мой сайт]
Начинают негодовать, почему так происходит. И потом узнают про безопаность. Т.е. сначала идёт безопасность, а потом уже остальное.Ну это чисто мои мысли вслух так сказать, не знаю как система устроена и насколько это всё реализуемо)
Народ, правильно ли я понимаю: иного выхода у пользователей, как ждать новой версии modx с фиксом — просто нет. Ну или массово ставим фильтрацию в плагине на всех проектах.
Учитывая, что не все, у кого сайт на modx читают форум и не все смогут быстренько сделать плагин — ждём новую волну?
Учитывая, что не все, у кого сайт на modx читают форум и не все смогут быстренько сделать плагин — ждём новую волну?
Вряд ли будет фикс. Задача фильтрации входящих данных ложится на разработчика сайта, а не системы.
Я в библиотеку modHelpers добавил функции для кодирования скобок. Выпушу в ближайшее время.
Я в библиотеку modHelpers добавил функции для кодирования скобок. Выпушу в ближайшее время.
Этой уязвимости подвержен не только этот компонент. К сожалению эта проблема ложится на плечи разработчика сайта. Решение простое — фильтр запросов. Мне также пришлось на свой сайт срочно делать фильтр.
А про FormIt я писал ещё полгода назад.
А про FormIt я писал ещё полгода назад.
FormIt пробовал вчера таким же образом тестить, вырезает скобки. То ли что-то изменилось за полгода, то ли я особо сильно не экспериментировал.
* Полтора года назад. Описался.
Насколько я знаю проблема так и осталась.И феном также нужно фильтровать.
Насколько я знаю проблема так и осталась.И феном также нужно фильтровать.
Обновил mSearch2 — улучшил фильтрацию поискового запроса.
а есть еще culture_key… это тот параметр который воообще ни как не проверяется, но редко кем используется.
Обновил mvtForms. Помимо прочего — добавлена фильтрация тегов.
Ещё большое спасибо за демонстрацию, что остались разработчики использующие Windows.Садоводы и фруктофилы пусть утрутся ))
Реально ли тестить компоненты на известные уязвимости при приёме в modstore?
Может и не все, но выборочно и помечать их.
Это будет что-то стоить и для магазина и авторов, но дело нужное всем.
Может и не все, но выборочно и помечать их.
Это будет что-то стоить и для магазина и авторов, но дело нужное всем.
За чей счёт банкет?)) да и какой смысл проверять только при приемке. Очередное обновление и новая дырка…
Да, при каждом обновлении. За чей счёт, это решать надо )
Нашедшему дырку компонент в подарок))
Ну, как вариант )
Если серьёзно, я думаю, движение в сторону аудита неизбежно, если система хочет успешного развития.
Если серьёзно, я думаю, движение в сторону аудита неизбежно, если система хочет успешного развития.
Ага. Только в сторону аудита разработчика сайта. Забейте в гугл inurl:core/components/pdotools и убедитесь, насколько велик и ужасен безмозглый разработчик. И их армия.
Точно. А если открылась новая уязвимость, то аудитора пороть и подарок взад забирать!
глагол «забирать» нужно убрать и ваще норм будет :D
Для наивных, что какой-то один человек всё проверит и исправит — Простой CSS-код заставляет iPhone перезагружаться. Это компания Форреста Гампа с огромным штатом тестировщиков и аналитиков.
Я писал про тестирование на известные уязвимости, которые могут быть использованы в массовом порядке.
Разумеется, всё проверить и исправить в компоненте невозможно и цели такой быть не может.
А вот принять меры для снижения вероятности массового взлома через компонент путём его аудита специалистом по безопасности — это другое дело.
Разумеется, всё проверить и исправить в компоненте невозможно и цели такой быть не может.
А вот принять меры для снижения вероятности массового взлома через компонент путём его аудита специалистом по безопасности — это другое дело.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.