Когда мы думаем об эксплойтах V8, первое, что приходит на ум, скорее всего, связано со сложными цепочками эксплойтов нулевого дня для браузеров. Хотя браузер может быть наиболее интересной целью для V8-эксплойтов, не стоит забывать, что этот JavaScript-движок с открытым исходным кодом также встроен в бесчисленные проекты помимо браузера. И когда движок JavaScript используется через границу безопасности для выполнения потенциально недоверенного кода, могут возникнуть проблемы безопасности.
Backdoor
Одна из таких проблем затронула популярную видеоигру Dota 2. В Dota использовалась устаревшая сборка v8.dll, которая была скомпилирована в декабре 2018 года. Неудивительно, что эта сборка была уязвима к целому ряду CVE, многие из которых даже были известными эксплуатируемыми уязвимостями с публичным доказательством концепции (PoC) эксплойтов. Avast обнаружили, что одна из этих уязвимостей, CVE-2021-38003, эксплуатировалась в дикой природе в четырех пользовательских режимах игры, опубликованных в игре. Поскольку V8 в Dota не был помещен в песочницу, эксплойт сам по себе позволял удаленно выполнять код против других игроков Dota.
Avast сообщили о наших результатах разработчику Dota 2, компании Valve. В ответ Valve выпустила обновление для Dota 12 января, в котором была обновлена старая и уязвимая версия V8. Это обновление вступило в силу немедленно, поскольку Dota должна быть обновлена, чтобы игроки могли участвовать в сетевых играх. Valve также приняла дополнительные меры: удалила нарушающие правила пользовательские режимы игры, уведомила пострадавших игроков и ввела новые средства защиты, чтобы уменьшить площадь атаки на игру.
Справочная информация
Dota 2 - это MOBA-игра, которая была первоначально выпущена 9 июля 2013 года. Несмотря на то, что игре уже почти 10 лет (или, возможно, 20, если считать с оригинальной Dota 1), она по-прежнему привлекает большую базу игроков - около 15 миллионов активных ежемесячных игроков. Как и другие популярные игры, Dota представляет собой сложное программное обеспечение, собранное из множества отдельных компонентов. Одним из компонентов, который представляет для нас особый интерес, является фреймворк Panorama. Это фреймворк, разработанный самой Valve для разработки пользовательского интерфейса с использованием хорошо известной веб-триады HTML, CSS и JavaScript. Часть JavaScript здесь была проблематичной, поскольку она выполнялась уязвимой версией V8. Таким образом, вредоносный JavaScript мог использовать уязвимость V8 и получить контроль над машиной жертвы. Это не было бы такой проблемой для немодифицированной игры, поскольку по умолчанию должны выполняться только легитимные скрипты, созданные Valve. Однако Dota очень открыта для кастомизации сообществом игроков, что открывает двери для субъектов угроз, пытающихся пронести вредоносные фрагменты JavaScript своим ничего не подозревающим жертвам.
Кастомизация Dota может принимать различные формы: Существуют пользовательские носимые внутриигровые предметы, дикторские пакеты, экраны загрузки, смайлики в чате и многое другое. Важно отметить, что существуют также пользовательские режимы игры, разработанные сообществом. Это, по сути, совершенно новые игры, которые используют мощный игровой движок Dota, позволяющий любому человеку с небольшим опытом программирования реализовать свои идеи для игры. Пользовательские игровые режимы играют важную роль в Dota, и Valve хорошо понимает преимущества предоставления игрокам возможности проявить свое творчество, разрабатывая пользовательские игровые режимы. В конце концов, сама Dota начиналась как игровой режим для Warcraft III: The Frozen Throne. Возможно, именно поэтому игровые режимы можно установить одним щелчком мыши из игры. В результате существуют тысячи игровых режимов, некоторые из которых чрезвычайно популярны. Например, в DOTA AUTO CHESS играли более 10 миллионов игроков.
Прежде чем в игровой режим смогут играть обычные игроки, он должен быть опубликован в магазине Steam. Процесс публикации включает в себя проверку, проводимую Valve. Хотя это потенциально может отсеять некоторые вредоносные игровые режимы, ни один процесс проверки не является идеальным. По крайней мере четырем вредоносным режимам игры удалось проскользнуть. Avast считает, что процесс проверки существует в основном для модерации, чтобы предотвратить публикацию неприемлемого контента. Существует множество способов спрятать бэкдор в игровом режиме, и попытка обнаружить их все во время проверки заняла бы очень много времени.
Основная игровая логика пользовательских режимов игры написана на языке Lua. Она выполняется на игровом сервере, который может быть либо машиной игрока, либо выделенным сервером, принадлежащим Valve. Для написания сценариев на стороне клиента используется JavaScript из фреймворка Panorama. Он в основном используется для управления элементами пользовательского интерфейса, такими как табло или статусные строки квестов. JavaScript выполняется движком V8, при этом имеется полная поддержка многих расширенных функций, включая выполнение WebAssembly. Кроме того, существует специфический для Dota API, который открывает дополнительные функциональные возможности. Особенно интересной была функция $.AsyncWebRequest, которая, в сочетании с eval, могла быть использована для бэкдора игрового режима, чтобы он мог выполнять произвольный дополнительный код JavaScript, загруженный из Интернета. Возможно, именно эта проблема стала причиной того, что функция $.AsyncWebRequest была устаревшей и в конечном итоге была удалена совсем. Однако есть способы обойти эту проблему. Например, веб-запрос может быть выполнен серверным кодом Lua, а ответ передан клиентскому JavaScript с помощью API обмена сообщениями об игровых событиях.
Просто тестирование
Avast обнаружили четыре вредоносных пользовательских игровых режима, опубликованных в магазине Steam, все они были разработаны одним и тем же автором. Первый игровой режим (id 1556548695) особенно интересен, поскольку, судя по отсутствию прикрепленной к нему полезной нагрузки, злоумышленник только тестировал эксплойт. Интересно, что злоумышленник также использовал этот игровой режим для тестирования различных других техник, оставляя в нем закомментированный код или неиспользуемые функции. Это дало нам прекрасную возможность понять ход мыслей злоумышленника.
Как видно на приведенном выше снимке экрана, злоумышленник очень прозрачно рассказал о природе этого игрового режима, назвав его test addon plz ignore и даже использовав описание, чтобы призвать других игроков не загружать этот игровой режим. Хотя это может показаться проявлением доброй воли, в трех других вредоносных игровых режимах тот же злоумышленник использовал прямо противоположный подход и старался сделать вредоносный код как можно более скрытным.
Эксплойт JavaScript в этом пользовательском режиме игры можно найти в файле overthrow_scoreboard.vjs_c. Раньше это был легитимный JavaScript-файл, реализующий функциональность табло, но злоумышленник заменил его содержимое эксплойтом для CVE-2021-38003. Эта уязвимость была первоначально обнаружена как "нулевой день" исследователями Google Клеманом Лецинем и Самуэлем Гроссом, когда она была использована в дикой природе в цепочке эксплойтов против полностью пропатченного телефона Samsung.
В настоящее время существуют публичные PoCs и записи для этого CVE. Однако они не были доступны в марте 2022 года, когда злоумышленник последний раз обновлял игровой режим. Это означает, что значительную часть эксплойта им пришлось разрабатывать самостоятельно (даже если бы на тот момент существовал публичный PoC, злоумышленнику все равно пришлось бы обладать определенными техническими навыками, чтобы перенести его на устаревшую сборку V8, которую использовала Dota). Несмотря на это, ядро эксплойта было представлено в записи баг-трекера Chromium для CVE. Там есть фрагмент кода, который может задействовать уязвимость для утечки якобы недоступного объекта TheHole, а затем использовать этот утеченный объект для повреждения размера карты. Злоумышленник взял этот фрагмент и вставил его в свой эксплойт, построив остальную часть эксплойта на основе этой поврежденной карты.
Интересно, что эксплойт содержит большое количество закомментированного кода и отладочных отпечатков. Это говорит о том, что злоумышленнику пришлось приложить немало усилий для использования уязвимости. Разработанная злоумышленником часть эксплойта начинается с использования поврежденной карты для повреждения длины массива, достигая примитива относительного чтения/записи. Затем он повреждает указатель хранения резервной копии ArrayBuffer, чтобы получить произвольный примитив чтения/записи. Функция addrof отсутствует, так как адреса утекают путем размещения целевого объекта с известным смещением от поврежденного массива и последующего использования примитива относительного чтения. Наконец, после произвольного чтения/записи эксплойт использует известный трюк WebAssembly для выполнения пользовательского шеллкода. Avast протестировали весь эксплойт локально против Dota и подтвердили, что он работает.
Помимо этого JavaScript-эксплойта, пользовательский режим игры также содержит еще один интересный файл, который носит зловещее название evil.lua. Именно в нем злоумышленник протестировал возможности выполнения Lua на стороне сервера. Смотрите фрагмент Lua ниже, где злоумышленник проверял, в частности, следующее:
- Ведение журнала
- Динамическая компиляция дополнительного кода Lua (loadstring)
- Определение точной версии интерпретатора Lua
- Выполнение произвольных системных команд (whoami)
- Создание корутин
- Сетевое подключение (HTTP GET запросы)
К сожалению, у нас нет доступа к полной истории обновлений этого конкретного игрового режима. Поэтому вполне возможно, что какой-то интересный код из предыдущих версий уже не присутствует в той версии, которую мы анализировали. По крайней мере, из журнала изменений видно, что для этого игрового режима было выпущено девять обновлений, причем все они произошли либо в ноябре 2018 года, либо в марте 2022 года. Поскольку эксплуатируемая уязвимость JavaScript была обнаружена только в 2021 году, Avast предполагает, что изначально игровой режим начинался как легитимная игра, а вредоносная функциональность была добавлена только в обновлениях марта 2022 года.
Бэкдор
После обнаружения первого вредоносного режима игры нам, конечно же, стало интересно, существуют ли еще подобные эксплойты. Поскольку злоумышленник не потрудился сообщить об уязвимости в Valve, Avast решили, что у него могут быть злые намерения и он попытается использовать ее в более широком масштабе. В результате мы разработали скрипт, который загружал все файлы JavaScript из всех пользовательских режимов игры, опубликованных в магазине Steam. Это дало нам гигабайты JavaScript, которые мы могли запросить на предмет подозрительных шаблонов кода.
Потребовалось совсем немного времени, чтобы обнаружить еще три вредоносных игровых режима, созданных одним и тем же автором (который также оказался автором ранее проанализированного тестового аддона plz ignore game mode). Эти игровые режимы назывались Overdog no annoying heroes (id 2776998052), Custom Hero Brawl (id 2780728794) и Overthrow RTZ Edition X10 XP (id 2780559339). Интересно, что тот же автор также опубликовал пятый игровой режим под названием Brawl in Petah Tiqwa (id 1590547173), который не содержал никакого вредоносного кода (к нашему большому удивлению).
Вредоносный код в этих трех новых игровых режимах гораздо более тонкий. В исходном коде нет ни файла с именем evil.lua, ни JavaScript-эксплойта. Вместо этого есть простой бэкдор, состоящий всего из двадцати строк кода. Этот бэкдор может выполнять произвольный JavaScript, загруженный через HTTP, что дает злоумышленнику возможность не только скрыть код эксплойта, но и обновлять его по своему усмотрению без необходимости обновлять весь пользовательский игровой режим (и проходить через рискованный процесс проверки игрового режима).
Бэкдор начинается с того, что код JavaScript отправляет на сервер пользовательское событие ClientReady. Это сигнал серверу о том, что есть новый клиент игры-жертвы, ожидающий получения полезной нагрузки JavaScript. Код Lua на сервере регистрирует слушателя для события ClientReady. Когда он получает это событие, он делает HTTP GET запрос на свой C&C сервер, чтобы получить полезную нагрузку JavaScript. Эта полезная нагрузка ожидается в теле ответа, и она передается JavaScript на стороне клиента в пользовательском событии с именем test.
Когда JavaScript на стороне клиента получает это событие test, он разворачивает полезную нагрузку, динамически создает из нее новую функцию и немедленно выполняет ее. На высоком уровне это, очевидно, простой загрузчик, способный выполнять произвольный JavaScript, загруженный с сервера C&C. Сотрудничество клиентского JavaScript и серверного Lua-кода было необходимо только потому, что JavaScript больше не разрешалось напрямую обращаться к Интернету.
В то время, когда Avast обнаружили этот бэкдор, C&C-сервер уже не отвечал. Несмотря на это, мы можем с уверенностью предположить, что этот бэкдор предназначался для загрузки JavaScript-эксплойта для CVE-2021-38003. Это объясняется тем, что все три режима игры с бэкдором были обновлены одним и тем же автором в течение 10 дней после того, как этот автор внедрил JavaScript-эксплойт в свой первый вредоносный режим игры. Однако мы по-прежнему не уверены в том, что к эксплойту был прикреплен какой-либо вредоносный шеллкод. В конце концов, использование ngrok для C&C несколько нетрадиционно и может свидетельствовать о том, что злоумышленник лишь тестировал функциональность бэкдора. Так или иначе, можно сказать, что эта атака не была очень масштабной. По данным Valve, пострадали менее 200 игроков.
Размышления на прощание
Обнаружив четыре вредоносных игровых режима, Avast попытались найти другие - к сожалению, наш след простыл. Поэтому неясно, каковы были конечные намерения злоумышленников. Однако Avast считают, что это были не совсем чистые исследовательские намерения по двум основным причинам. Во-первых, злоумышленник не сообщил об уязвимости в Valve (что, как правило, считается хорошим поступком). Во-вторых, злоумышленник пытался скрыть эксплойт в незаметном бэкдоре. Тем не менее, не исключено, что у злоумышленника не было чисто злонамеренных намерений, поскольку он мог бы использовать эту уязвимость с гораздо большим эффектом.
Например, злоумышленник может попытаться захватить популярный пользовательский режим игры. Многие игровые режимы игнорируются их первоначальными разработчиками, поэтому злоумышленник может попытаться сделать что-то простое, например, пообещать исправить ошибки и продолжить разработку бесплатно. После некоторого количества легитимных обновлений злоумышленник может попытаться пронести в игру бэкдор JavaScript. Поскольку игровые режимы обновляются автоматически в фоновом режиме, у ничего не подозревающих игроков-жертв не будет много возможностей защититься.
В качестве альтернативы злоумышленник может искать другие способы использования уязвимости без использования пользовательских игровых режимов. Например, злоумышленник может попытаться найти отдельную XSS-уязвимость, чтобы объединить ее с эксплойтом V8. Такая XSS-уязвимость может позволить злоумышленнику выполнить произвольный JavaScript в экземпляре Panorama удаленной жертвы. Затем эксплойт V8 может быть использован для выхода из фреймворка Panorama. Обратите внимание, что Panorama также активно используется в главном меню игры, поэтому, в зависимости от характера XSS-уязвимости, радиус поражения может быть не меньше, чем у 15 миллионов ежемесячных игроков.
Indicators of Compromise
SHA256
- 3c00f15d233a3dd851d68ecb8c7de38b1abf59787643a2159c9d6a7454f9c3b7
- 44c79f185576e1ec7d0d7909eb7d4815cbf8348f37f62c0debd0d5056fb1100b
- 4bb1d6dcb1e12c3e5997b8dc7fa3db45d44bade39cfcceab56f90134ca2d09f3
- 4d3c6986b924108911709b95cb4c379720c323e6f7b3a069b866b76e0e3ec6b5
- 4fad709e74345c39a85ce5a2c7f3b71d755240d27dd46688fa3993298056cf39
- 85635bd92cc59354f48f8c39c6db7a5f93cabfb543e0bcc3ec9e600f228f2569
- 880a0722a5f47d950170c5f66550e1cdef60e4e84c0ce1014e2d6d7ad1b15c14
- cca585b896017bd87038fd34a7f50a1e0f64b6d6767bcde66ea3f98d6dd4bfd0