Перекрёстный допрос: разоблачение маскировки отпечатка с помощью JavaScript-инъекций

21 ДЕКАБРЯ 2021 | БРАУЗЕРНЫЕ ОТПЕЧАТКИ

Ищете способы скрыть свой онлайн-отпечаток? Возможно, вы сталкивались с использованием JavaScript-инъекций. Этот метод относительно простой и дешёвый. Но безопасен ли он? Наше исследование показало, что ответ прост — нет. Мы рассмотрели уязвимости этого метода и то, как его можно обнаружить. А ещё, в статье есть код, который вы сможете протестировать самостоятельно.

Что такое маскировка через JS-инъекцию?

Вот три основных метода маскировки браузерного отпечатка:

  • JavaScript-инъекция

  • Нативный подход

  • Сочетание вышеупомянутых методов

Что мы подразумеваем под отпечатком?

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

Давайте кратко рассмотрим нативный подход к маскировке отпечатков. Он использует новые исполняемые файлы, которые базируются на браузерах с открытым исходным кодом, таких как Chromium. Этот метод заменяет определённые значения вашего отпечатка, чтобы скрыть вашу личность. Нативный подход получил своё название благодаря тому, что он очень надёжен и сложен для обнаружения. Это наиболее эффективный метод, однако его реализация требует больше времени и ресурсов.

JavaScript-инъекция — это способ вставки кода на веб-страницу для изменения значений определённых атрибутов. Например, подобно адвокату, который представляет подозреваемого на допросе, JS-инъекция может перезаписать атрибуты, чтобы скрыть настоящую личность пользователя. Как Сол из «Лучше звоните Солу», который отвечает на вопросы полиции от имени своего клиента, JS-инъекция может имитировать поведение другого пользователя.

JavaScript-инъекция позволяет скрыть настоящие атрибуты браузера, заменив их на другие значения.

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

Как осуществляется JS-инъекция?

Существует несколько методов маскировки JS-инъекции. Некоторые выбирают фреймворки автоматизации браузеров, такие как Selenium и Puppeteer. Есть и те, кто предпочитает расширения браузера, поскольку это, вероятно, самый удобный вариант. Наше исследование также выявило, что для этих целей можно использовать бесплатный HTTPS-прокси mitmproxy, хотя мы не нашли примеров его использования на практике.

Наиболее простой метод осуществления JS-инъекции — использование браузерных расширений.

Как работает JS-инъекция через браузерные расширения?

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

  • Файл, описывающий расширение: manifest.json

  • Файлы контентных скриптов

  • Ядро, состоящее из фоновых скриптов

Тестирование JS-инъекции на практике

Представляем наше расширение

Перейдём к самому интересному! Мы рады представить наше собственное браузерное расширение, которое мы будем использовать в качестве примера. Давайте познакомимся: Saul — Better JS Injection Call, инновационное решение для скрытия отпечатков.

Для тех, кто интересуется кодом, первым делом мы представляем сердце нашего расширения — файл manifest.json. Он определяет все его возможности.

1{
2 "manifest_version": 2,
3 "name": "Better JS Injection Call - Saul - A artistic solution for fingerprint masking",
4 "version": "1.0.0",
5 "content_scripts": [
6 {
7 "matches": [""],
8 "js": ["bettercall.js"],
9 "run_at": "document_end",
10 "all_frames": true,
11 "match_about_blank": true
12 }
13 ]
14}
15

Теперь давайте рассмотрим контентный скрипт нашего расширения — bettercall.js. Нам просто нужно добавить наш код JavaScript для маскировки отпечатков браузера. В блоке content_script нашего файла manifest выше (с пятой строки и далее) вы видите некоторые инструкции, которые похожи на бессмыслицу. По сути, они означают, что наш контентный скрипт будет добавлен ко всем веб-страницам и фреймам (то есть на HTML-элементы, загружающие другие веб-страницы или HTML-страницы внутри веб-страницы, которую мы посещаем).

Перейдём к бизнес-логике нашего расширения. Представляем наш bettercall.js:

1const maskLanguage = (language) => {
2 Object.defineProperty(navigator, 'language', {
3 get: () => language,
4 });
5};
6
7const doMask = (method, ...args) => {
8 const stringifiedMethod = method instanceof Function
9 ? method.toString()
10 : `() => { ${method} }`;
11
12 const stringifiedArgs = JSON.stringify(args);
13
14 const scriptContent = `
15 (${stringifiedMethod})(...${stringifiedArgs});
16 document.currentScript.parentElement
17 .removeChild(document.currentScript);
18 `;
19
20 const scriptElement = document.createElement('script');
21 scriptElement.textContent = scriptContent;
22 document.documentElement.append(scriptElement);
23
24 const scriptElement2 = document.createElement('script');
25 scriptElement2.textContent = "document.body.innerHTML += 'Injected';";
26 document.documentElement.append(scriptElement2);
27
28};
29
30doMask(maskLanguage, 'eo-Multilogin');

Наше расширение изменяет атрибут navigator.language с языка, который мы установили, на eo-Multilogin. EO — это символ языка эсперанто, созданного для облегчения глобального общения.

Этот код добавляет строку «Injected» в тело всех контекстов, в которые он внедряется. Мы будем использовать её для оценки работы нашего расширения.

Применение на практике

Давайте включим наше расширение из меню chrome://extension и посмотрим, что произошло с нашим navigator.language.

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

Давайте продолжим наш пример с допросом полиции и представим, что фреймы, которые загружает главная страница, это допросные комнаты в полицейском участке. В идеале мы ожидаем, что наш адвокат будет с нами на протяжении всего допроса — так же, как мы ожидаем полной инъекции...

1<html>
2<head>
3</head>
4<body>
5<br>http://www.iframetest.test/iframe.html<br>
6<iframe width="100" height="55" id="normalFrame" src="http://www.iframetest.test/iframe.html"></iframe><br>
7<br>about:blank<br>
8<iframe width="100" height="55" id="aboutBlankFrame" src="about:blank"></iframe><br>
9<br>data://<br>
10<iframe width="100" height="55" id="dataFrame" src="data:text/html,<html><body></body></html>"></iframe><br>
11<br>javascript:<br>
12<iframe width="100" height="55" id="jsFrame" src='javascript:const a=0;'></iframe><br>
13<br>sandbox<br>
14<iframe width="100" height="55" id="sandboxFrame" sandbox src="http://www.iframetest.test/iframe.html"></iframe><br>
15<br>srcdoc<br>
16<iframe width="100" height="55" id="srcdocFrame" srcdoc="<html><head></head><body></body></html>"></iframe><br>
17</body>
18</html>

Ниже мы видим результаты.

Наш флаг «Injected» показывает, что большинство страниц содержат инъекцию. Однако фреймы, использующие URL-адреса data:// и javascript:// — нет. К сожалению, наш адвокат Сол очень занят и не всегда с нами. Поэтому в допросных комнатах для data:// и javascript:// наш браузер будет отвечать на некоторые вопросы о себе без покровительства своего адвоката. Давайте посмотрим, как это будет работать...

Перекрёстный допрос одним кликом

Мы все знакомы с методикой перекрёстного допроса, где задаются одни и те же вопросы одним и тем же людям снова и снова, но в разном порядке. Такая тактика может вывести из себя многих людей и довести их до признания вины.

В аналогичной ситуации оказался и наш бедный браузер без своего защитника. Код JavaScript начинает перекрёстный допрос, задавая те же вопросы как нашей веб-странице, так и её подстранице, которая загружается через элемент iframe.

Ниже показана страница, содержащая iframe javascript://.

1<html/>
2<iframe id="jsFrame" src="javascript:console.log('hello');"/></iframe/>
3</body/>

Мы можем доказать, что подозреваемый лжёт. Наш контентный скрипт, который был разработан для внедрения JavaScript и маскировки браузерного отпечатка, нельзя внедрить в фреймы с URL-адресами data:// и javascript://. Следовательно, результат должен был быть одинаковым, но не является таковым.

Посмотрите в нижний левый угол: второе окно отображает en-US вместо eo-Multilogin, как мы ожидали. Бинго! Сравнив ответы, полученные от navigator.language верхнего окна и javascript:// фреймов, мы видим разницу. Эта аномалия и свидетельствует о попытке внедрения JavaScript. Как мы и упоминали ранее, любые несоответствия или аномалии в вашем браузерном отпечатке — это повод для беспокойства.

Пришло время для ваших экспериментов

Что ещё может вызвать вышеупомянутую проблему?

Хотите попробовать решить её самостоятельно?

Cross-Examination: новый код для выявления маскировки браузерных отпечатков с помощью JS.

Протестируйте его сейчас — нажмите на ссылку и перейдите к HTML PoC.

Наш код собирает информацию о вашем браузере как из основного окна, так и из iframe: User-Agent, отпечаток Canvas и аудио, публичный IP-адрес WebRTC, разрешение экрана и часовой пояс.

Затем он генерирует хеш из собранной информации и использует его в качестве идентификатора посетителя для обоих окон. Наш код затем сравнивает идентификаторы, полученные из основного окна и iframe. В случае отличий он сообщает об аномалии.

Обратите внимание: мы используем javascript:// iframe, а не data:// iframe. Наши эксперименты показывают, что некоторые фреймворки для автоматизации браузера успешно внедряются в data:// iframe, но не в javascript:// iframe.

JS-инъекции в популярных расширениях

Помимо коммерческих решений, использующих JS-инъекции для маскировки отпечатков цифрового устройства, магазин Google Chrome предлагает сотни расширений, обещающих скрыть/подделать некоторые атрибуты браузера через JS-инъекции, такие как Canvas хеш, User-Agent и другие.

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

Мы проанализировали их, изучив раздел content_script в файле manifest.json, и пришли к следующим выводам:

Возьмём расширение User-Agent Switcher для Chrome (djflhoibgkdhkhhcedjiklpkjnoahfmg), у которого уже два миллиона скачиваний. Оно не переопределяет значение navigator.userAgent, несмотря на заявленную возможность изменять строку User-Agent. Расширение только перехватывает HTTP-запросы и изменяет заголовок User-Agent HTTP. И этот урок стоит учитывать.

Заключение: использование JS-инъекций несёт значительные риски

Вот единственный вывод, который мы можем сделать из наших экспериментов: использование JS-инъекций для маскировки браузерных отпечатков несёт в себе большие риски для любого бизнеса, который управляет множеством аккаунтов.

Этот подход широко используется из-за своей простоты и доступности, но его легко обнаружить, как показывают наши эксперименты. Даже гибридное решение не уменьшает этот риск: хотя одна его часть может быть более защищённой, часть, использующая маскировку с помощью JS-инъекций, так же легко обнаруживается.

Вот почему Multilogin использует собственные нативные решения: браузер Mimic, основанный на Chromium, и StealthFox, основанный на Mozilla Firefox. Надёжный способ избежать рисков — выбирать решения, которые не используют JS-инъекции. Ведь зачем рисковать, если можно выбрать более безопасный путь?