WebSocket

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

Недавно в сети появилась пара статей о том, что веб-сайты злоупотребляют функциональностью websocket для сканирования портов компьютеров пользователей. 

Причина, по которой эти техники работают в том, что браузеры позволяют websocket’ам из публичных источников открывать websocket-соединения для localhost без многих защит.

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

Реальность оказалось несколько хуже, чем я думал. 

Схема

  1. Установите либо внедрите вредоносное рекламное ПО на популярный сайт, который фронтенд разработчики чаще всего посещают. Предположим, им будет http://frontend-overflowstack.com/.
  2. На этой странице добавьте код, пытающийся открыть websocket-соединения для распространённых портов (сканирование 10 000 портов занимает около секунды, поэтому на их количество можете не скупиться).
  3. Если страница сможет открыть соединение, оставьте его открытым и перенаправляйте все полученные сообщения в вашу секретную базу данных корысти.
  4. ?
  5. Получайте прибыль.

Работает ли это?

Я разместил очень простую страничку на: http://frontend-overflowstack.com/. При загрузке она пробует подключить websocket к каждому порту компьютера посетителя в диапазоне от 2 000 до 10 000 (за исключением нескольких, которые не допускает Firefox). Если порт подключается, то начинается его прослушивание и выводятся все полученные им сообщения. Эта страница не сохраняет и никаким другим способом не передаёт полученные данные. Они просто временно отображаются на экране.

При появлении на этой странице вывода, по-настоящему вредоносный сайт мог бы легко отправить этот вывод на любой желаемый сервер.

Генерирование данных

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

var express = require('express')
var http = require('http')
var path = require('path')
var reload = require('reload');

var app = express()
 
app.get('/', function (req, res) {
	res.sendFile(path.join(__dirname, 'public', 'test.html'));
})

var server = http.createServer(app)
 
reload(app).then(function (reloadReturned) {
  server.listen(3000, function () {});
	setInterval(reloadReturned.reload, 5000);
});

При выполнении этот код запускает сервер на порту 3000, websocket-сервер на порту 9856 и отправляет сообщение: reload для всех подключенных websocket-клиентов каждые 5 секунд. 

При запуске нашего анализирующего траффик сайта появится следующее:

Итак, frontend-overflowstack.com напрямую подслушивает сообщения о перезагрузке, отправляемые локальным dev-сервером в мой локальный браузер.

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

Интрига закручивается

Большая часть современной фронтенд разработки, по всей видимости, использует React, как правило, в комплекте с webpack-dev-server, который содержит свой собственный, более изощрённый интерфейс websocket.

Этот сервер через свой websocket передаёт уже гораздо больше интересных данных. Продемонстрировать это так же просто, как вызвать create-react-app:

$ npx create-react-app test
$ cd test/
$ npm start

Если мы это выполним и снова взглянем на наш злобный сайт, то увидим:

Мгновенно появляется больше данных. Теперь мы получаем хэши и сообщения о состоянии — всю бесполезную информацию.

Но что происходит, когда разработчик допускает опечатку? Webpack-dev-server, используя своё websocket-соединение, услужливо пытается отправить кучу информации об отладке и стеке на экран разработчика.

К счастью, наш сайт может видеть и это тоже:

А теперь всё становится ещё интереснее. Мы получили фрагменты кода, пути к файлам, расположения — много разной полезной информации.

При этом будет ещё лучше, если в итоге разработчик случайно ошибётся в строке, содержащей существенные данные:

Теперь у нас есть копия ключа доступа AWS этого разработчика. Быстро, запускаем биткоин-майнеры!

Анатомия “Атаки”.

Ни один технический проект не завершён без диаграммы. Вот, как это работает на графике:

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

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

Оценка угрозы

Ограничивающие факторы

Если говорить серьёзно, то этот вектор атаки достаточно слаб. Вам нужно заманить ничего не подозревающих пользователей на свой сайт и убедить их там оставаться во время разработки JS кода. 

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

Обобщая соображения

Как бы то ни было, мы видели, что разные сайты уже используют технологию сканирования websocket портов, не озадачиваясь осведомлением разработчиков. Учитывая, что инструменты JS используют небольшое число хорошо известных портов, написание скрипта для тонкой эксфильтрации dev-траффика React не будет особо сложным. 

Представьте себе внутреннего работника Twitbook, который просто нажимает save в редакторе, обуславливая тем самым слив токена доступа или внутреннего адреса сервера “не той” аудитории. 

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

Устранение проблемы

Я следовал идее попыток прервать механизмы горячей перезагрузки JavaScript, потому что это фактически единственное общее использование websocket, с которым я знаком. Discord тоже использует websocket, но беглое его рассмотрение не принесло никаких очевидных результатов, поскольку этот канал спроектирован с учётом публичного интернета. 

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

Учитывая это, скорее всего, и другие случаи использования websocket (для данных, спроектированных не для публичного интернета) могут быть также скомпрометированы.

Вероятно, webpack-dev-server должен выполнять некоторую аутентификацию или для горячей перезагрузки должны использоваться альтернативные каналы связи браузера (думаю, это уже планируется и по другим причинам).

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

Хочется надеяться на какое-то исправление, которое бы привнесло в браузеры дополнительные элементы управления.

Читайте также:


Перевод статьи Steve Stagg: Stealing Secrets from Developers using Websockets.