Chrome предал вас — любое расширение теперь способно украсть OAuth-токены и секретные ссылки

Chrome предал вас — любое расширение теперь способно украсть OAuth-токены и секретные ссылки

OAuth-токены, API-ключи и закрытые видео YouTube сливают посимвольно.

image

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

Ключевой элемент здесь, API declarativeNetRequest. Он позволяет расширениям добавлять динамические правила, которые блокируют запросы по шаблону. Эти правила умеют сопоставлять URL по регулярным выражениям. В Chrome заблокированный запрос обрывается сразу и возвращает ошибку net::ERR_BLOCKED_BY_CLIENT, причем практически мгновенно. А если правило не срабатывает, браузер продолжает обычную загрузку, которая занимает заметно больше времени, потому что начинаются сетевые подключения. Именно это время и становится подсказкой, которую расширение может использовать как "оракул".

Внутри Chromium поведение блокировки выглядит так, что при совпадении правила система очищает ожидающие колбэки и быстро завершает обработку с net::ERR_BLOCKED_BY_CLIENT. Для расширения это означает два разных сценария. Когда блокировка сработала, реакция быстрая, в воспроизведении разница составляла примерно 10–30 мс. Если совпадения нет, запрос идет дальше и задержка становится выше, порядка 50–100 мс и больше, в зависимости от сети и сайта.

Чтобы измерять этот эффект, расширение использует два обычных механизма, не требующих tabs permission. Во первых, оно вызывает chrome.tabs.reload, чтобы инициировать перезагрузку страницы. Во вторых, оно подписывается на chrome.tabs.onUpdated и ждет событие, где статус меняется на complete. Дальше остается просто измерить, сколько времени прошло между reload и моментом, когда вкладка "дозагрузилась". Быстро, значит запрос оборвали правилом. Медленно, значит браузер действительно пытался загрузить страницу.

Дальше работает уже чистая математика. Расширение создает динамическое правило блокировки с регулярным выражением, которое проверяет конкретную позицию в URL. Правило подбирается так, чтобы совпадение означало, что символ на этой позиции относится к определенному подмножеству возможных значений. Затем расширение перезагружает вкладку и по времени понимает, совпало или нет. Повторяя такой тест и делая двоичный поиск по набору символов, можно восстановить адрес посимвольно за небольшое число шагов на каждый знак. Автор отчета приложил видео repro.mp4, где утечка видна в консоли, символы появляются один за другим.

Опасность этой техники в том, что она не требует взаимодействия с пользователем и не дает явных признаков. Расширение может тихо собирать чувствительные данные из адресной строки, а затем отправлять их наружу. В качестве примеров приводятся OAuth коды и токены в URL вроде https://accounts.google.com/callback?code=secret, параметры сессионных идентификаторов и API ключей в query string, ссылки на приватные или "непоисковые" материалы, включая YouTube, Google Drive и Dropbox, токены для сброса пароля, а также поисковые запросы, в том числе медицинские или финансовые. Утечку можно сделать почти незаметной, например свернув окно или утащив его на задний план на время извлечения символов.

Исследователь попытался определить, когда именно проблема появилась. Первичный bisect сузил диапазон регрессии до ревизий 718858–718878 в репозитории Chromium. Дополнительный анализ указывает на коммит 1539dcc828ee3ba96c0949f1202cf9139b883d82, где в правила declarativeNetRequest добавили оценку регулярных выражений так, что текущая демонстрация стала работать. При этом автор допускает, что похожий подход можно было бы адаптировать и для более ранних версий, возможно вплоть до появления Dynamic Rules API в изменении 1531023, то есть задолго до указанного коммита.

Проверка проводилась на Windows 11 24H2. Уязвимость воспроизводилась сразу в нескольких ветках Chrome. В списке фигурируют Stable 144.0.7559.97, Beta 145.0.7632.18, Dev 146.0.7647.4 и Canary 146.0.7653.0.

Сценарий воспроизведения выглядит просто. Нужно открыть вкладку с адресом вида https://accounts.google.com/callback?code=secret и поставить ее первой. Затем загрузить в браузер тестовое расширение из двух файлов, manifest.json и background.js. После этого открывается DevTools для service worker расширения, и в консоли можно наблюдать, как URL извлекается посимвольно.