CDN-провайдер Cloudflare внедрял содержимое памяти своего сервера в код произвольных веб-страниц

87d5e09446ce4aefb0d171cc70064b0f.pngСпециалисты по безопасности из Google обнаружили неприятный баг, чем-то похожий на приснопамятную уязвимость Heartbleed в OpenSSL. Она тоже выдаёт любому желающему криптографические ключи пользователей, а также куки, пароли, содержимое POST-запросов с личными данными, кредитные карты, ключи API и другое содержимое чужих сессий.

Здесь уязвимость ограничена всего одним сервис-провайдером, пусть и таким крупным как Cloudflare. Но в определённом смысле этот баг Cloudbleed хуже, чем Heartbleed, потому что утечка данных происходит спонтанно. Эти страницы рутинно скачиваются краулерами, индексируются поисковыми системами, до сих пор хранятся в архивах веб-страниц и в кэше Google.

Cloudflare является посредником между хостером сайта и посетителями сайта, выполняя роль обратного прокси для веб-сайтов. Из-за ошибки программиста системы Cloudflare на Nginx с сентября 2016 года внедряли случайные фрагменты оперативной памяти своего сервера в содержимое веб-страниц, которое выдавалось всем пользователям.

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

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

1f2daed7b7404aac84538126c2e1f854.png
Фрагмент сессии некоего случайного пользователя Fitbit, который получили хакеры Project Zero по запросу к Cloudflare

Среди клиентов Cloudflare — такие клиенты как Uber, OK Cupid и Fitbit (всего пострадали около 4 287 625 доменов, в том числе 2ip.ru, 4pda.ru, avito.ru, diary.ru, forbes.ru, iphones.ru, javascript.ru, prlog.ru, rghost.ru, rosbalt.ru, searchengines.ru). Понятно, что в пользовательских сессиях тех же Uber, OK Cupid и Fitbit передаётся чувствительная информация о поездках пользователей, кредитных картах, маршруты поездок, личные данные о человеке с сайта знакомств, показания фитнес-трекеров и многое другое. Не говоря о миллионах других сайтов, которые сидят на Cloudflare (она контролирует примерно 5% веба, в том числе 11% сайтов из топ-10000). Это как сидеть в ресторане, где одновременно с меню официант подаёт вам часть содержимого кошелька предыдущего клиента.

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

17 февраля 2017 года сотрудник компании Google и известный хакер Тэвис Орманди (Tavis Ormandy) во время дистилляции корпуса случайно наткнулся на неожиданные данные. Это был не обычный набор мусора, искажённых или неправильно маркированных данных, а нечто в таком странном формате, что Тэвис Орманди потратил время на дебаггинг, пытаясь выяснить причину ошибки дистилляции, то есть ошибку в своём собственном коде. «На самом деле данные были настолько странные, что некоторые мои коллеги из отдела Project Zero тоже заинтересовались, — пишет Тэвис. — Вскоре стало ясно, что мы смотрим на фрагменты неинициализированной памяти, которые перемежаются валидными данными. Загадка разгадалась, потому что эти неинициализированные данные шли от программы, которая как раз поместила в память те данные, которые я указал. Но некоторые окружающие фрагменты имели строки и объекты, которые словно пришли от обратного прокси под управлением Cloudflare — крупнейшего провайдера CDN».

Так оно и вышло. Чуть позже исследователям удалось воспроизвести баг. Утечка информации происходила при определённом сочетании HTML-тегов на странице, которое вызывало баг на сервере Cloudflare.

Исследователи поковырялись с багом — и среди нескольких образцов получили ключи шифрования, куки, пароли, фрагменты POST-форм и данные HTTPS-запросов других пользователей. Как только они увидели это, то немедленно прекратили эксплуатацию бага и сразу сообщили о нём в компанию Cloudflare.

Тэвис Орманди предполагает, что баг Cloudflare связан с функцией ScrapeShield, которая работает у них на серверах — она парсит и обфусцирует HTML. Сама компания Cloudflare позже подтвердила, что проблема действительно была с парсером, который обфусцирует почту, делает Server Side Excludes и Automatic HTTPS Rewrites. Баг закрался примерно год назад, когда этот парсер, написанный на Ragel, оптимизировали и внедрили как модуль Nginx. Теперь выяснилось, что ошибка указателя и перерасход буфера в парсере был много лет, но раньше просто не происходила утечка памяти.

/* generated code */
if ( ++p == pe )
    goto _test_eof;

Вот как парсер из-за бага обрабатывал атрибут в теге