HN 日本語サマリー

← 一覧へ戻る
セキュリティ

電動スクーターのリモートアンロック

Remotely Unlocking Electric Scooters (henriemategui.com)

18 pointsby henriemategui2 コメント

要約

この記事は、セキュリティ研究者が電動スクーター会社のバックエンドシステムに見つけた脆弱性について詳述しています。公開されているWordPressサイトから管理者ユーザー名を見つけ、オペレーターパネルのパスワードを総当たり攻撃で特定することで、研究者はシステムへのアクセス権を得ました。このアクセスにより、機密性の高い運用データを確認できただけでなく、物理的なスクーターをリモートでアンロックすることが可能になり、重大なセキュリティ上の欠陥が明らかになりました。

全文翻訳

リモートで電動スクーターをアンロックする 注: 会社を保護するため、会社を特定できる可能性のあるものはすべて偽の例に置き換えました。electricscootercompany.com.br ドメイン、アプリパッケージ、ユーザーの詳細(スラッグ、名前、メールアドレス)はすべて架空のものです。実際の会社とは一切一致しません。 事の始まりは、あるニュース記事でした。私の街に、ある会社が大量の電動スクーターを導入したという記事でした。ほとんどの人は、新しい移動手段だと見ました。私は、誰もまだ調査していないバックエンドで動作する、インターネットに接続されたデバイスのフリート(群)を見ました。 まず、2つのことが必要でした。それがどの会社なのか、そして通常のユーザーにとってサービスがどのように機能するのかということです。記事に会社名が載っており、簡単なGoogle検索でそのサイトにたどり着きました。サイトには、次のようなフローが説明されていました。スマートフォンでアプリを開く。スクーターのQRコードをスキャンする。車両のアンロック料金を支払う。乗車する。これは、どのユーザーにとっても「ハッピーパス」です。私は、その裏で何が起こっているのかを知りたかったのです。 ステップ1:偵察 私は、electricscootercompany.com.br に関連するすべてをマッピングすることから始めました。サブドメインの列挙により、次のような多くのサブドメインが見つかりました。 www app api privacidade privacidade2 dev membro vouchers planos validate painel これらのすべてが実際のアプリだったわけではありません。app、membro、vouchers、planos は、基本的に同じページを表示し、アプリストアへの誘導のみを行っていました。多くの名前がありましたが、新しい発見はありませんでした。dev ホストは 500 エラーを返し、PHP セッションクッキーを設定しましたが、私が利用できるものはありませんでした。validate は「Conta não localizada」(アカウントが見つかりません)と応答しました。// 当時はどのパラメータが必要か分かりませんでした。これらを記録して先に進みました。 3つのものが際立っていました。 www.electricscootercompany.com.br: WordPress マーケティングサイト。 api.electricscootercompany.com.br: クライアントが使用する REST API。 painel.electricscootercompany.com.br: オペレーター向けの Angular パネル。 サイト上のどこにもパネルへのリンクはありませんでした。列挙によってのみ見つけました。リンクされていないからといって、それがロックダウンされているわけではありません。 ステップ2: 侵入せずにパネルを開く パネルは誰でもアクセスできました。本番環境の Angular アプリでした。私は 32 個すべての JavaScript チャンクをダウンロードし、バンドルを調べました。その中で、次の 83 個のエンドポイントを見つけました。 ユーザーと権限。 車両とマップ。 トリップの開始と終了。 IoT デバイス。 ガレージ、ドック、ジオフェンス。 バウチャー、トランザクション、財務モジュール。 これにより、ターゲットの魅力が分かりましたが、何も進展しませんでした。保護された約 38 個のルートに有効なセッションなしでアクセスを試みましたが、すべて同じ応答でした。HTTP 401 です。 試したことの多くはうまくいきませんでした。 直接ルートアクセス: 認証によってブロックされました。 署名されていない管理者 JWT: バックエンドによって拒否されました。 トークンクレームの改ざん: 有効なセッションは生成されませんでした。 ログイン時の SQL インジェクション: 認証フィールドに対して SQLMap を実行しましたが、注入可能なパラメータは見つかりませんでした。 レポートエンドポイント: php/report.php は空の 500 エラーを返しました。 典型的な公開ファイル: .git、.env、ソースマップはアクセスできませんでした。 直接的な攻撃に対しては、認証はうまく機能していました。そして、アプリはすでに簡単な方法を示していました。実際のユーザーを見つけて、そのパスワードを狙うことです。 ステップ3: WordPress が最初のピースを提供する 公開されている WordPress REST API は、作者をリストアップすることを許可してくれました。 GET /wp-json/wp/v2/users GET /wp-json/wp/v2/users/1?context=view 応答は、ユーザー ID 1、公開名 admin、スラッグ electricscootercompany を提供しました。通常、これはありふれた WordPress の列挙です。ここでは、同じ識別子を使用して、オペレーションパネルで試すことができました。 ログインは、入力した内容に応じて異なる応答を返しました。 既存の ID + 間違ったパスワード → "Senha inválida" // "無効なパスワード" 存在しない ID → "E-mail não encontrado" // "メールが見つかりません" したがって、electricscootercompany が単なるブログの作者であるかどうかを推測する必要はありませんでした。バックエンドは、同じ ID がオペレーションシステムにも存在することを直接教えてくれました。これにより、一般的な列挙が、価値のある名前を持つターゲットリストに変わりました。 ステップ4: 総当たり攻撃 ユーザーが確認されたので、ログインに対して総当たり攻撃を実行しました。繰り返しの試行を制限するものは何もありませんでした。そして、最終的に動作するパスワードが見つかったため、パネルへのログインが成功しました。 これが根本原因を本当に説明する部分です。レート制限がないため、既知の ID が 1 つあれば、推測ループを有効なセッションに変えることができました。これ以降のすべては、Burp でキャッチした実際のセッションに基づいています。 JWT をデコードすると、次の情報を持つアカウントが得られました。 { "data": { "PK_Usuario": 2, "email": "electricscootercompany", "nome": "ElectricScooterCompany", "nivel": 1000, "fk_empresa_grupo": 1 } } レベル 1000 は管理者ロールでした。そして、トークンは約 950 日間有効でした。そのため、一度取得したセッションは、誰かが意図的に無効にしない限り、何年も機能し続けるでしょう。 ステップ5: パネルが仮説でなくなる 有効なセッションが得られると、すべてが一度に変わりました。以前は 401 エラーを返していたルートが、実際の運用データを提供するようになりました。キャプチャでは、118 個の一意のホスト/メソッド/パスの組み合わせにわたる 168 件のファーストパーティリクエストを記録しました。 パネルは次のものへの読み取りアクセスを提供しました。 /mapas/__veiculos でのフリートマップ。 /docas でのドック。 ガレージと運用インフラストラクチャ。 /fronteiras/__coordenadas でのジオフェンスポリゴン。 ID と状態を含む IoT デバイスインベントリ。 個々の車両とフリート全体。 インターフェースで 408,000 件以上のレコードが表示されたアプリユーザー。 企業、権限、トランザクション、バウチャーバッチ、財務データ。 マップは、デジタルサイドと実際の地上での運用を接続しました。ガレージの場所、ドックの場所、街中に散らばっている車両、そして各車両に属する IoT デバイスがどこにあるかを示しました。 マーカーをクリックすると、車両の詳細が表示されました。コード、タイプ、およびその場所(例: ドック)です。 一部の応答は、このアクセスレベルの大きさを示すのに十分な大きさでした。 /docas ~312 KB /mapas/__veiculos ~593 KB /iots/.../free/true/... ~1.2 MB /fronteiras/__coordenadas ~32 KB Burp は大きな応答ボディを約 3 KB でカットオフしたため、すべての完全な応答を保存しているわけではありませんが、記録したステータス、パス、およびサイズはすべて確実です。 ステップ6: 見るだけではなかった 次に、パネルがデータを読み取るだけでなく、書き込むこともできるかどうかを知りたいと思いました。セッションは、次のものに対する PUT 呼び出しを示しました。 車両レコード。 ユーザーアカウント。 バウチャーバッチ。 また、テストアカウントの無料ライドフラグを切り替えることもできました。したがって、これは単にデータを読み取るだけではありませんでした。ビジネスルールやレコードを変更することもできました。 車両レジストリを使用すると、フリート内の任意のスクーターを検索し(インターフェースには数千件のレコードが表示されていました)、編集フォームを開くことができました。 このフォームは、読み取りが制御に変わる場所でした。変更を保存するだけでなく、アンロック、ロック、再起動 IoT ボタンがあり、これらは物理デバイスに直接送信されます。 最も恐ろしい部分は IoT モジュールでした。パネルは次のようなコマンドを発行しました。 POST /iot_sends/ Content-Type: application/json {"pk_veiculo":699,"comando":"open"} バックエンドは次のように応答しました。 {"retorno":"comandos enviados"} // "コマンドが送信されました" 同じ車両を閉じるために 2 番目のコマンドを発行しました。 {"pk_veiculo":699,"comando":"close"} 同じ確認応答がありました。 テストを 1 台の車両、pk_veiculo: 699 に限定しました。盗まれた管理者アカウントは、どこからでもスクーターをアンロックし、すぐにロックし直すことができました。パネルには成功メッセージも表示されました。 しかし、API 応答と画面上の緑色のメッセージだけでは、多くを証明できません。コマンドが実際のスクーターに到達したかどうかを確認する必要がありました。私は近くにスクーターがなかったので、友人にスクーターに近づいてもらい、コマンドを発行した瞬間にそれを撮影してもらいました。そして、それは機能しました。スクーターはアンロックされ、ライトが点灯し、誰も料金を支払ったり、QR コードをスキャンしたり、触れたりすることなく、走行できる状態になりました。 テストしなかったことも、テストしたことと同じくらい重要です。多数の車両に対してこれを自動化することはありませんでしたし、移動、ブレーキ、またはその他の実際の危険を引き起こす可能性のあるものには一切触れませんでした。1 台のスクーターに対する 1 つのコマンドで、Web パネルが実際のハードウェアに到達することを示すのに十分でした。 文書化する価値のある行き止まり すべてのリードが成功したわけではありません。行き止まりも共有する価値があります。それらを消去していくことが、成功する道へと私を導いたからです。 Interc