Безопасность 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
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
А есть способ сделать фильтрацию всего, что вообще приходит в параметрах post и get? Т.е. прежде чем данные дойдут до какого-нибудь сниппета, они обработаются раньше? Чтобы исключить недостатки большинства компонентов.
А ещё фильтруются эти значения
Без проблем. Плагин на событие OnMODXInit.
Приведу пример. Есть компонент платный — Callbak. Я без проблем вписываю вызовы сниппетов или глобальных плейсхолдеров в поля формы обратного звонка, и на почту админу приходит ключ его сессии, или пароль от БД. Это хорошо, что штатно письмо только админу приходит. И это не единственный пример, который я знаю. И их может быть много больше. Да тот же FormIt в БД хранит не обработанные данные форм. Если они где-то выводятся на сайте, то картина будет похожая.
Ну это чисто мои мысли вслух так сказать, не знаю как система устроена и насколько это всё реализуемо)
Учитывая, что не все, у кого сайт на modx читают форум и не все смогут быстренько сделать плагин — ждём новую волну?
Я в библиотеку modHelpers добавил функции для кодирования скобок. Выпушу в ближайшее время.
А про FormIt я писал ещё полгода назад.
Насколько я знаю проблема так и осталась.И феном также нужно фильтровать.
Может и не все, но выборочно и помечать их.
Это будет что-то стоить и для магазина и авторов, но дело нужное всем.
Если серьёзно, я думаю, движение в сторону аудита неизбежно, если система хочет успешного развития.
Разумеется, всё проверить и исправить в компоненте невозможно и цели такой быть не может.
А вот принять меры для снижения вероятности массового взлома через компонент путём его аудита специалистом по безопасности — это другое дело.