Кросс-браузерный трекинг на основе перебора обработчика внешних протоколов

В статье описывается способ создания кросс-браузерного идентификатора при помощи использования уязвимостей четырех популярных браузеров: Tor Browser, Safari, Chrome и Firefox. Ссылки на демо, исходный код.

33d209ae417b38589e48ea63b039eb78.png

Два месяца назад, в процессе исследования браузера Safari, я случайно наткнулся на уязвимость, позволяющую проверять наличие конкретного приложения на компьютере пользователя прямиком из браузера с использованием JavaScript.

Проверяя список из 24 приложений на предмет присутствия в операционной системе можно создать 24-битный идентификатор. Такой идентификатор будет одинаковый во всех браузерах и позволит связывать сессии пользователей между браузерами. А если еще и обучить простую модель, то можно предсказывать профессию пользователя или страну, в которой он родился.

Представьте, что на вашем компьютере установлена IDE для Python, PostgreSQL и телеграмм. Вы открываете случайную страницу в интернете, на которой написано, что вы бэкенд-разработчик из России, а еще там есть ваш идентификатор, который точно такой же даже в Tor Browser. Страшно?

Идея крутая, и после нескольких месяцев исследований это стало реальным. Детали реализации описываются ниже, однако есть ограничения:

  • Демо плохо работает для Linux. Хромиум браузеры открывают все приложения через xdg-open, поэтому детект приложений там не возможен вообще. Firefox работает не везде из-за кастомных настроек;

  • По собранной статистике, 8% сессий показывают правильный результат со второго раза или не стабильный результат вообще;

  • 15% идентификаторов в базе невалидные. Либо все приложения установлены, либо не установлено ничего;

  • Демо проверяет наличие зарегистрированных протоколов, поэтому некоторые удаленные приложения отображаются как установленные;

  • На момент прочтения статьи уязвимости могут быть исправлены. На момент написания — только Tor Browser подготовил PR с исправлениями.

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

Технические детали

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

Если приложение существует в системе, то браузер покажет подтверждающее диалоговое окно для его открытия. Этот механизм называется Deep Linking.

Открытие ZoomОткрытие Zoom

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

В реальности все гораздо сложнее. Браузеры обладают специальными механизмами для предотвращения подобных проверок. Именно уязвимости в данных механизмах позволяют провернуть нашу задумку. В конечной реализации используются CORS политики, особенности браузерных окон и iframe элементы.

Chromium

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

b6069ff04d05c93e1e6b3ef9c7b64f4c.png

Однако, все функции кастомных Extension (дополнений) хрома сбрасывают этот глобальный флаг. Это сделано, чтобы Extensions могли открывать специальные ссылки (вроде mailto: ) без подтверждения пользователей. Данную особенность можно использовать для обхода блокировки перебора протоколов.

Здесь я вспомнил про встроенный Chrome PDF Viewer, который реализован как Extension. Мы можем открывать PDF файл после каждой проверки внешнего протокола и сбрасывать защиту от перебора.

Конечный алгоритм:

  1. Вызвать переход на внешний протокол с помощью метода location.replace ();

  2. Проверить наличие подтверждающего диалогового окна с помощью input Элемента. Дело в том, что при наличии данного окна невозможно фокусировать элемент ввода до тех пор, пока окно присутствует в браузере;

  3. Открыть любой PDF файл на другом домене, чтобы сбросить диалоговое окно и механизм защиты;

  4. Выполнять шаги 1–3 в цикле, чтобы проверить весь список приложений.

Firefox

Здесь мы будем использовать механизмы same-origin policy. Создаем дополнительное окно и переходим по внешнему протоколу. Если приложение не установлено, то браузер покажет внутреннюю страницу, которая будет не доступна из JavaScript. Установленное приложение будет открыто как страница about: blank, которая доступна.

2adffcfb226489b6d025109d82d1889c.png

Конечный алгоритм:

  1. Открыть дополнительное окно с помощью метода window.open;

  2. Перейти по ссылке внешнего протокола с помощью метода location.replace;

  3. Проверить доступ к объекту document дополнительного окна. Если доступ есть, то приложение установлено в системе;

  4. Повторять шаги 2–3 для всего списка приложений.

Safari

Используем тот же метод, что и в Firefox. Однако, при наличии приложения необходимо перезагрузить основную страницу (метод location.reload) для сброса подтверждающего окна.

Tor Browser

Разработчики браузера, по-видимому, решили отключить диалоговые окна на открытие внешних приложений. Исходили из соображений безопасности, однако это сильно упрощает нам работу.

Здесь не нужны никакие дополнительные окна. Мы просто будем использовать iframe элементы и проверять same-origin policy.

ab6ba68a94671194daa820a60639c0ed.png

Проверять можно пассивно: делаем проверку каждые 10 секунд в фоне без требования к действиям от пользователей.

Можно активно: показываем капчу на 24 символа и проверяем после каждого нажатия клавиши.

Выводы

Данная уязвимость существует уже несколько лет, а многие разработчики браузеров о ней знают, если судить по баг репортам. Тем не менее, я не смог найти сайты, которые ее используют.

Ссылки:

© Habrahabr.ru