
Информация и PoC для CVE-2024-45200, уязвимость переполнения буфера в Mario Kart 8 Deluxe под названием "KartLANPwn"
Информация и proof-of-concept для уязвимости переполнения стека «KartLANPwn» в Mario Kart 8 Deluxe
Автор: Chad Hyatt
KartLANPwn — это уязвимость в Mario Kart 8 Deluxe, вызванная некорректным использованием P2P-сетевой библиотеки Pia. Отдельные реализации «CopyAppData» иногда вызываются с outBufSize, превышающим размер самого буфера out. Это потенциально может привести к удалённому выполнению кода (RCE) на уровне пользователя на консолях других игроков, если уязвимость объединить с утечкой информации. KartLANPwn затрагивает использование реализаций LAN/LDN (локальная многопользовательская игра) и NEX (онлайн-мультиплеер) библиотеки Pia.
Данная уязвимость затрагивает все версии Mario Kart 8 Deluxe вплоть до v3.0.1 включительно (v3.0.2 для Китая/Tencent) и была специально продемонстрирована через функцию «LAN Play» в розничной версии MK8DX v3.0.1.
По состоянию на 11 сентября 2024 года Nintendo выпустила исправление для KartLANPwn вместе с v3.0.3 для всех регионов, кроме Китая. По состоянию на 27 сентября 2024 года v3.0.3 также вышла для Китая. Мы раскрыли эту информацию с разрешения Nintendo.
Проприетарная сетевая библиотека Pia используется в собственных играх Nintendo Switch с поддержкой локальной или онлайн-игры для реализации P2P-кода для LAN/LDN (локальная многопользовательская игра) и протокола NEX, используемого для онлайн-мультиплеера.
Для демонстрации мы сосредоточимся на LAN-протоколе Pia. Консоль хоста комнаты открывает UDP-«discovery» сокет на :30000 на локальном широковещательном адресе (255.255.255.255), а другие консоли в той же сети должны отправлять «запросы на просмотр» со своего сокета (также на :30000) на маршрутизатор, который пересылает их другим устройствам в той же сети.
Хост комнаты (наш «сервер») отправляет обратно начальный «browse-reply», который должен содержать информацию о комнате (имя хоста, данные Mii, количество игроков и т.д.). Внутри пакета browse-reply длина данных приложения, которые будут скопированы в буфер out, может быть установлена значением до 150, хотя out имеет размер всего 128 байт. Это позволяет переполнить значения в стековом фрейме (в данном конкретном случае для pop {r4-r8, pc}).
По крайней мере, в версии Pia, используемой в MK8DX, для того, что мы рассматриваем, пакеты browse-reply для LAN формируются следующим образом:
(Все целые числа кодируются в big-endian)
Остальная часть информации о сессии следует далее, но неважна для создания пакета для KartLANPwn
LAN_CopyAppData»Для демонстрации приведён примерный псевдокод реализации «CopyAppData» в LAN. Хотя в этой функции самой по себе нет ничего плохого, неправильное использование (в случае MK8DX) позволяет перезаписать стековый фрейм нашими входными данными приложения в packet.
(функция находится по адресу +0xA0F8C0 в main для MK8DX v3.0.1)
void LAN_CopyAppData(int* r0, int packet, int out, uint outBufSize) {
int u1 = 68612;
if (out != NULL) {
// Фактическая длина для чтения из буфера `packet` в начале наших
// данных приложения (которыми управляет наш «сервер»)
uint appDataLength = *(uint *)(packet + 432);
// В некоторых случаях outBufSize передаётся с числом, превышающим границы
// буфера `out`, что позволяет вызвать переполнение стекового буфера (в нашем
// конкретном случае `*out` имеет длину 128 байт, а outBufSize равен 150!)
if (appDataLength <= outBufSize) {
memcpy(out, packet + 48, outBufSize); // packet[47], начало данных приложения
u1 = 0;
}
*r0 = u1;
return;
}
*r0 = 68615;
return;
}
Мы предоставили простой PoC-скрипт на Python, который действует как фальшивый хост комнаты и отвечает консолям других игроков специально сформированным пакетом «browse-reply», вызывающим крах процесса игры при открытии меню «LAN Play» в MK8DX на консоли, находящейся в одной сети с компьютером, запускающим скрипт.
Скачайте ZIP-архив репозитория напрямую с GitHub или клонируйте с помощью git:
git clone https://github.com/latte-soft/kartlanpwn.git && cd kartlanpwn
python3 kartlanpwn-poc.py
Видеодемонстрация на YouTube:
А для вас, ARM-гиков, вот скриншот из GDB с результатирующим segfault:

Чтобы разобраться в деталях самой ошибки, потребовалось всего пара дней, но действительно использовать переполнение? Это совсем другая история. Ядро Nintendo Switch в пользовательском режиме шутить не любит; оно чрезвычайно строго во всех возможных местах. Мы потратили много часов, обмениваясь идеями и проверяя различные направления, но в итоге реального (произвольного) выполнения кода добиться не удалось. В нашем конкретном случае даже надёжный ROP казался маловероятным, поскольку практически все относительные сетевые функции были основаны на классах, к тому же у нас был прямой доступ на запись только к r4-r8. (*_this мой любимый) Кроме того, без предварительной утечки информации не так просто пропустить mov r4, r0 или что-то подобное, чтобы управлять указателем _this. Забавно!
Что касается отчёта, на этот раз процесс с Nintendo прошёл относительно гладко: от момента подачи отчёта до раскрытия. Хотя выплаченное вознаграждение в $512 оказалось гораздо меньше ожидаемого (ну разве не всегда так?), это всё же лучше, чем появление «Ниндзя из Nintendo» у моей двери...
Nintendo, несомненно, извлекла урок из более простых времён Wii, 3DS и даже Wii U. Они хорошо подготовились: успешная эксплуатация любого вида, кроме DoS-краша из пользовательского режима на Switch, практически невозможна. ASLR, принудительное использование страниц No-eXecute (нельзя напрямую писать в исполняемые страницы памяти, играм приходится использовать специальный системный модуль для JIT!), невозможность ROP и другие препятствия. (жду, когда моддинг-сцена очень быстро опровергнет эти слова..) С другой стороны, за последние пару месяцев я узнал много нового о совершенно новой для меня платформе и архитектуре за очень короткое время, не говоря уже о замечательных людях, с которыми мне посчастливилось работать над KartLANPwn! (Спасибо Pablo и fishguy 😄)
KartLANPwn © 2024 лицензирован по CC BY 4.0.
Чтобы просмотреть копию этой лицензии, посетите https://creativecommons.org/licenses/by/4.0/
| Индекс | Псевдо-тип | Описание |
|---|
| 0 | u8 | Тип пакета (0x1) |
| 1 | u32 | Размер тела информации о сессии (1266 в нашем случае) |
| 5 | (42 байта) | Различные поля информации о сессии, неважны для нас |
| 47 | (0x180 байт) | Начало области для данных приложения |
| 431 | u32 | Длина данных приложения |
| Дата | Информация |
|---|
| 18.07.2024 | Отчёт отправлен в Nintendo через HackerOne |
| 31.07.2024 | Внутренняя триажировка отчёта |
| 11.09.2024 | Исправление выпущено вместе с Mario Kart 8 Deluxe v3.0.3 для всех регионов, кроме Китая |
| 12.09.2024 | Вознаграждение выплачено Nintendo |
| 12.09.2024 | Другие исследователи достаточно быстро обнаружили исправление для «уязвимости в сетевом коде игры» в бинарном сравнении v3.0.3 |
| 27.09.2024 | Mario Kart 8 Deluxe v3.0.3 выпущена для китайского региона |
| 29.09.2024 | Nintendo разрешила раскрытие информации, этот репозиторий стал общедоступным |
| 30.09.2024 | CVE-2024-45200 опубликован в NVD |