Оглавление
Sighax: как хакеры взломали самую защищённую часть Nintendo 3DS
Друзья, на сайте появился раздел о проектах DMG-Dreams и обзор физического издания Super Mario Land DX.
На Алиэкспресс появились:
Крутые металлические этикетки для консолей Game Boy Advance.
В истории Nintendo 3DS был момент, когда казалось, что она взломана окончательно.
Эксплойт A9LH (Arm9LoaderHax) позволял запускать кастомную прошивку и давал почти полный контроль над консолью. Инструмент - мощный , но капризный и рискованный в установке. Это как поставить спортивный мотор и прямоток в старые «Жигули»: ехать можно быстро, но управлять такой машиной сложно — и всегда есть шанс не вписаться в поворот.
А вот B9S (Boot9Strap), созданный на базе уязвимости Sighax, стал полной противоположностью. Это уже не тюнинг из гаража, а Mercedes — быстрый, надёжный и комфортный. Он запускается раньше, работает стабильнее и делает всё то же самое, но без лишнего риска. То, что раньше казалось хрупкой самоделкой, теперь стало цельной, инженерно выверенной системой.
Sighax – это взлом, который позволяет на самом низовом уровне правильно подписать кастомную прошивку.
Именно так выглядит Sighax эксплойт
До появления Sighax у пиратов была одна большая проблема: все их хаки работали на уже запущенной консоли. То есть, чтобы поиграть в пиратскую игру, нужно было сначала включить консоль, затем вручную активировать эксплойт, и только потом запускать игру. После выключения весь процесс нужно было повторять заново.
Почему так происходило? Потому что каждый раз при включении процессор ARM9 выполнял криптографическую проверку прошивки. Если подпись не была настоящей — консоль просто не загружалась. А так как кастомная прошивка по определению изменена, то пройти проверку она не могла.
Именно эту проблему и решал Sighax. Он позволял обойти систему проверки подписи и загрузить модифицированную прошивку — как если бы она была официальной. Причём это происходило ещё до запуска самой системы.
Но если сам Sighax уместился в пару строчек кода, то его поиски были похожи на охоту за иголкой в стоге сена.
Охота за Sighax: история поиска самой глубокой бреши в системе
6 мая 2014 года Федеральная комиссия по связи США (FCC), регулирующая, в том числе, выпуск игровых консолей, опубликовала техническую документацию Nintendo.
В документах говорилось, что между моделями FTR-001 (первая ревизия Nintendo 2DS) и FTR-001(-01) (обновлённая версия) нет различий в железе — единственное отличие заключалось в изменении функций безопасности процессора и прошивки загрузчика.
На это обратил внимание Derrek. Он предположил, что если Nintendo внезапно изменила загрузчик, значит в предыдущем что-то было не так — возможно, уязвимость. Он начал копать — и действительно нашёл дыры в системе подписи прошивки.
Nintendo использовала самописный анализатор ASN.1 для проверки подписи RSA-2048, которая содержала хеш SHA-256 от прошивки. И если саму подпись взломать было практически невозможно (для этого потребовались бы годы вычислений), то сам анализатор оказался на удивление хрупким: множество недочётов, неосторожностей и опасных допущений.
Позже Derrek пошёл ещё дальше — ему удалось получить Boot ROM — скрытую часть памяти, в которой как раз и содержалась реализация проверки подписи. Главная сложность заключалась в том, что этот код автоматически отключался системой сразу после загрузки и не мог быть считан при обычной работе.
27 декабря 2016 года Derrek выступил на хакерской конференции Chaos Communication Congress (33C3). Он публично рассказал о найденной уязвимости в Nintendo 3DS и показал, что смог скачать Boot ROM — то, что считалось невозможным.
Однако, он всего лишь показал, что брешь есть, но не оставил ни кода, ни подробностей — лишь массу вопросов. А ведь Boot ROM — это не просто технический компонент, это настоящая кладовая секретов Nintendo, где спрятаны ключи шифрования, внутренние протоколы и механизмы безопасности всей системы 3DS. Такой намёк, да ещё и вокруг самого важного участка системы, не мог остаться без внимания сообщества. В игру вступили SciresM, Myriachan, Normmatt, TuxSH и Hedgeberg.
Проблема была в том, что они знали лишь примерное направление, но совершенно не представляли, что именно нужно сделать и с чего начать. Они понимали: Boot ROM (Boot9) зашит в чип консоли навсегда и не может быть изменён после выпуска с завода. А значит, если уязвимость действительно находилась в Boot9 — как утверждал Derrek, — то она должна была остаться там навсегда. И если найти способ её воспроизвести, то это можно было использовать.
И тогда команда сделала важное допущение: Nintendo, как это у неё бывало не раз, могла повторно использовать один и тот же код в разных частях системы. Особенно это касалось функций безопасности, например, анализатора подписи RSA в формате ASN.1.
Они начали изучать прошивки, которые были доступны публично, и действительно нашли там похожий код. Однако, начиная с версии 1.0.0 (ядро 0.14), в этом коде уже присутствовали дополнительные проверки структуры подписи, в том числе строгая проверка так называемой области заполнения (padding). Эти проверки устраняли те самые слабые места, которые могли быть использованы для обхода защиты. Это означало, что анализ новых прошивок больше не имел смысла, потому что они уже не содержали уязвимого кода, аналогичного Boot9. А значит, становился невозможным и подбор рабочей подписи, ведь чтобы её сконструировать, нужно было точно воспроизвести поведение уязвимого анализатора Boot9.
Тогда исследователи решили пойти дальше и попытались выяснить, что было до версии 1.0.0 — в так называемой factory firmware, установленной на консоли до момента выхода с завода.
Они начали скупать консоли с минимально возможной версией прошивки, рассчитывая, что с помощью особенностей NAND-памяти удастся восстановить фрагменты заводской прошивки. Дело в том, что NAND, как и большинство энергонезависимых накопителей, не удаляет информацию полностью, а лишь помечает блоки как "стёртые", и те могут сохраняться в сыром виде до момента перезаписи.
Путём анализа десятков таких консолей им удалось восстановить раннюю версию прошивки с ядром 0.13 — то есть предшественницу публичной 1.0.0. В ней действительно оказался уязвимый анализатор подписи, без исправлений, которые были добавлены позже. Это стало первым реальным способом исследовать и атаковать уязвимость Boot9, не имея прямого доступа к нему.
Используя восстановленную прошивку, команда собрала первую поддельную подпись, которая успешно обходила проверку на factory firmware. Однако, при тестировании на настоящем Boot9, консоль вместо привычного синего экрана ошибки просто зависала с чёрным экраном. Именно чёрный экран, а не привычный синий экран ошибки, дал понять: уязвимость сработала, но Boot9 попытался обратиться к недоступной памяти, и выполнение остановилось ещё до обработки ошибки.
Путь к универсальному эксплойту оказался сложнее. Теперь требовалось создать такую подпись, которая идеально подходила бы под поведение именно Boot9. Началась фаза перебора.
Представьте себе огромный лабиринт, в котором разбросаны 128 комнат. Исследователи знали, что нужная им — одна из этих 128. Но было одно «но»: они не могли просто открыть двери по очереди. Вместо этого им нужно было найти единственный правильный путь, который вёл к нужной комнате, — путь, закодированный внутри особой RSA-подписи.
Всё осложнялось тем, что таких возможных путей было около 323 ундециллионов. И это был не просто список маршрутов — каждый из них нужно было пройти целиком, ведь дверь открывалась только в том случае, если путь был пройден строго по правилам: от начала до конца. При таком масштабе даже Солнце погасло бы раньше, чем завершился полный перебор всех возможных маршрутов.
Конечно, теоретически им могло повезти — и нужный путь оказался бы, скажем, десятым по счёту. Но вероятность этого была исчезающе мала. Поэтому пришлось искать закономерности и «фишки», которые могли бы упростить поиск. Например, каждая попытка требовала 17 умножений и 17 операций по модулю, но можно было вычислить значение один раз — и сразу проверить ещё и его отрицательный вариант. Это уже сокращало объём поиска вдвое. Кроме того, они использовали математические особенности RSA, комбинировали промежуточные результаты и запускали всё на видеокартах с оптимизированными алгоритмами.
Чтобы пройти по этим миллионам цифровых маршрутов, одной лишь смекалки было недостаточно — понадобились ещё и вычислительные мощности. Исследователи собрали всё, что могли: дома круглосуточно работали мощные игровые видеокарты, вроде GTX 1080 Ti и 980 Ti, но для задачи такого масштаба этого оказалось недостаточно.
Тогда они обратились к облачным серверам Amazon. Один такой экземпляр — p2.8xlarge — содержал восемь GPU Tesla K80 и 32 процессорных ядра, обеспечивая 230 миллионов проверок в секунду. Машины работали девять дней без остановки, а аренда стоила около 7 долларов в час.
В результате, вместо теоретических сотен триллионов путей, реальный объём поиска удалось сократить примерно до 80 триллионов. И уже на восьмитриллионной попытке был найден нужный путь — та самая подпись, которая обводила систему вокруг пальца.
Но простого нахождения подписи было недостаточно. Здесь стоит на мгновение остановиться и задать главный вопрос: что вообще хотели SciresM, Myriachan, Normmatt, TuxSH и Hedgeberg? Ответ прост и амбициозен — полный контроль над устройством.
Им было недостаточно просто обмануть Boot9 и загрузить кастомную прошивку. Да, это уже было победой — но не той, ради которой они вложили сотни часов работы, облачные вычисления и тысячи долларов. Их настоящая цель была глубже — достичь той самой секретной половины Boot ROM, откуда начинается всё: криптографические ключи, механизмы защиты, внутренние протоколы. Именно туда, куда, по словам Derrek'а, ему удалось заглянуть.
Главная проблема была в том, что Boot9 закрывался прежде, чем передавал управление прошивке. Получался замкнутый круг: да, поддельная подпись позволяла загружать прошивку, но, когда начинал исполняться код прошивки — доступ к Boot9 уже был закрыт.
И тогда команда придумала элегантный и дерзкий план. Boot9, кроме защиты, выполняет ещё одну важную задачу — он копирует код прошивки в оперативную память. А значит, нужный код можно вставить туда заранее — главное, чтобы он успел запуститься до закрытия доступа.
После загрузки прошивки в память, нужно было специально вызвать ошибку. Тогда консоль сама вызывала обработчик ошибок. А там находился изменённый код. Очень элегантное решение.
И все старания оказались не напрасны. Пробившись за самую глухую стену защиты, исследователи наконец добрались до той самой запретной зоны Boot9, где хранились не только ключи, но и тайны, которые Nintendo никогда не планировала раскрывать. И среди всего этого они нашли то, что можно было бы назвать идеальным финалом этой истории.
В одной из функций Boot9 скрывалась тихая, никем не задокументированная проверка. Если при включении консоли была закрыта крышка и пользователь зажимал комбинацию START + SELECT + X, Boot9 проверял — а не вставлен ли Nintendo DS картридж. И если да — то вместо обычной загрузки он пытался загрузить альтернативную прошивку прямо с этого картриджа.
Вот так, казалось бы, обычный бюрократический шаг — подача патента — стал первой трещиной в броне, которую Nintendo считала неприступной. Один технический документ и молчание Derrek'а, который не стал делиться своими наработками, настолько подогрели интерес других исследователей, что те буквально не могли остановиться: заводские прошивки, триллионы попыток, взлом защит и собственный код, который исполнялся раньше запуска системы.
И всё это привело к тому, что консоль, построенная вокруг шифрования и недоверия, полностью раскрыла все свои секреты. Не силой. Не случайно. А благодаря упорству, уму и желанию понять то, что, казалось бы, было надёжно спрятано.
Как работает эксплойт?
Все Nintendo 3DS консоли имеют одинаковые Boot ROMы, прошитые в чипы на заводе. Когда вы нажимаете кнопку питания, то процесс загрузки консоли инициализируется Boot9.
Загрузка консоли выглядит так:
1. Инициализируются слоты ключей AES с секретными ключами из Boot9;
2. Инициализируются слоты ключей RSA с открытыми ключами для прошивки 3DS;
3. Выбирается загрузочное устройство (обычно NAND чип, где хранится прошивка);
4. Считывается заголовок прошивки из NAND чипа в память консоли (если на предыдущем шаге выбран именно он);
4.1. Заголовок прошивки содержит ряд необходимых для загрузки данных (точки запуска ARM9 и ARM 11, зашифрованная закрытым ключом Nintendo RSA-2048 подпись, которая содержит SHA-256 хеш);
4.2. На этом этапе происходит два процесса:
4.2.1. Создается SHA-256 хеш заголовка, который записывается в определенное место памяти;
4.2.2. RSA-2048 подпись копируется прямо перед созданным SHA-256 хешем;
5. Запускается синтаксический анализатор, который исследует внутренности зашифрованной подписи, находит там SHA-256 хеш заголовка и сравнивает его с SHA-256 хешем, который был создан в пункте 4.2.1.;
6. Считываются разделы прошивки в память в соответствии с параметрами из заголовка;
7. Закрывается доступ к защищенным половинам Boot9 и Boot11 (с них начинается работа процессоров ARM9 и ARM11, но для защиты она обрывается перед следующим шагом);
8. Процессоры ARM9 и ARM11 перескакивают на точки запуска, указанные в заголовке прошивки.
Так выглядит стандартный процесс запуска консоли. По идее этот стартовый этап является самым защищенным этапом работы. Да, когда прошивка уже запустится, можно использовать определенные эксплойты, чтобы заставить работать свой код. Но эти эксплойты каждый раз нужно активировать заново. Потому что, оперативная память консоли после выключения очищается. А исправить прошивку таким образом, чтобы она всегда оставалась измененной нельзя (точнее, в исключительных случаях можно, но это опасно и может привести к окирпичиванию), ведь прошивка при включении консоли каждый раз проверяется.
Давайте подробнее остановимся на шагах: 4 и 5, ведь именно здесь происходит магия взлома Sighax.
Когда Boot9 доходит до шага 4, то фактически в памяти создается новый код (шаги 4.2.1. и 4.2.2.):
Может показаться, что это идеальная защита. Мы рассчитываем хеш начала заголовка, шифруем его закрытым ключом Nintendo и получившуюся подпись кладем в окончание заголовка. Если вы что-то меняете в заголовке (а вам придется там что-то менять если вы делаете свою кастомную прошивку), то хеш заголовка не совпадет с зашифрованным хешем. Взять хеш заголовка от своей кастомной прошивки и положить его в RSA-2048 подпись мы не можем, ведь не знаем закрытого ключа Nintendo, а открытый ключ (который Boot9 получает на шаге 2) для этого не подойдет. На первый взгляд кажется, что это замкнутый круг, но это не так.
RSA PKCS#1v1.5 подпись с зашифрованным SHA-256 хешем закодирована в формате ASN.1. Такие данные представляют последовательность байтов, которые идут один за другим без разрывов (такую последовательность данных в формате ASN.1 можно увидеть на картинке ниже).
Для того, чтобы расшифровать этот хеш из RSA-2048 подписи Nintendo написали свой собственный синтаксический анализатор. В идеальном случае он двигается по подписи слева направо и сверху вниз (фактически как мы читаем книгу).
Он доходит до байта 01 (фиолетовый цвет – padding type) и понимает, что дальше будут пустые FF байты. Пропускает их. Потом пропускает байты 30 31 30 (красный, розовый, зеленый цвета – outer block id, outer block size и inner block id соответственно) и наконец переходит к самому главному байту (салатового цвета – inner block size), который говорит о расстоянии до блока, когда начнется хеш подписи. Соответственно, анализатор пропускает это пространство (бледно-зеленый) и переходит к значениям 04 (светло-коричневый – SHA256 block id) и 20 (желтый - SHA256 block size), которые обозначают, что дальше пойдет подпись SHA256 в таком-то размере. Опять пропускает эти значения и наконец-то он доходит до самой подписи, которая была изначально закодирована RSA-2048.
В итоге происходит сравнение хеша из RSA-2048 подписи (зеленый цвет) и SHA256 хеша, который рассчитан Boot9 самостоятельно (синий цвет).
Но у этого анализатора есть много слабых мест:
1. Не выполняется проверка границ полей;
2. Разрешен тип блока PKCS#1v1.5 2 (предназначен для зашифрованного сообщения, а не подписи, в идеальном варианте Nintendo используют PKCS#1v1.5 1);
3. Не требуется, чтобы заполнение блока подписи было полным (это позволяет использовать менее строгую схему подписи).
Из-за первой ошибки появляется уязвимость, которая позволяет сравнивать хеш с самим собой. Вторая и третьи ошибки позволяют перебрать подписи (брут-форсом) и найти ту самую, которая пройдет анализатор и позволит использовать первую ошибку.
Проблема в том, что простой перебор подписей – это очень тяжелая процедура, которая требует 17 операций умножения и 17 модуляций на одну попытку. Даже в упрощённом варианте, используя ошибки 2 и 3, нужная подпись была одной из два в сорок третьей степени.
То есть, в самом худшем случае потребовалось бы около ВОСЬМИДЕСЯТИ ТРИЛЛИОНОВ попыток, чтобы найти нужную идеальную подпись. И такая подпись была найдена спустя девять дней и более чем восьми триллионов попыток. Как вы понимаете, в самом худшем варианте понадобилось бы девяносто дней. И это упрощенный поиск из-за ошибок Nintendo. Представляете, если бы этих ошибок не было?
Теперь давайте вернемся к той самой «идеальной подписи», которая была получена спустя 9 дней. Зачем она нужна? В первую очередь, чтобы пройти самописный синтаксический анализатор. Но главная ее цель состоит в том, чтобы Boot9 всегда выдавал положительный отклик при проверке обоих хешей (шаг 5).
Буквально на первом шаге видны отличия. Значение 01 было изменено на 02 (фиолетовый цвет). Это как раз ошибка два, о которой мы говорили ранее. Дальше идут не пустые FF блоки, а зашифрованное сообщение, которое пропускается анализатором. Потом идут стандартные значения 30 30 30 и новое число 5e, которое означает, что дальше будет много ненужной информации (гораздо больше, чем в нормальном варианте). И в конце - два значения 6a и 2c, которые говорят, что дальше будет идти расшифрованный хеш. Но за счет удлиненного салатового блока, расшифрованный хеш как-бы попадает в ту зону, куда изначально был записан рассчитанный хеш (шаг 4.2.1.). Анализатор доходит до этого места и считывает первый хеш. Потом он идет по тому адресу, куда был записан созданный хеш и сравнивает два значения.
То есть, получается, что он читает хеш из одного и того же места и потом сравнивает их. Как вы понимаете такая проверка всегда будет пройдена.