DJIは、民生用ドローンおよび空中ディスプレイ技術の世界的リーダーです。

同社の製品は、コンシューマ市場だけでなく、法人市場でも大きなシェアを獲得しています。例えば、重要インフラ、製造、農業、建設、危機管理など、さまざまな業種の組織が同社製品を業務に活用しています。このように、世界中の個人および法人に広く利用されている同社製ドローンは、さまざまな景色、さまざまな対象物についてのデータや画像を記録しています。

Check Point Researchは先ごろ、DJI製ドローンのプラットフォームに脆弱性を発見しました。この脆弱性を悪用された場合、DJIのユーザは、気づかぬうちにアカウントに不正アクセスされ、次の情報にアクセスされる可能性があります。

  • ドローン飛行中のフライト・ログ、写真、動画(ユーザがこれらのデータをDJIのクラウド・サーバに同期していた場合)。フライト・ログには、飛行中の正確な位置情報に加え、飛行中に撮影した写真および動画のプレビューが含まれます。
  • ドローン飛行中のライブ・カメラ・ビューとマップ・ビュー(DJIのフライト管理ソフトウェア「FlightHub」を使用している場合)。
  • ユーザ・プロファイル情報など、ユーザ・アカウントに関連づけられた情報。

この脆弱性は、DJIが運営するオンライン・フォーラムDJI Forumから悪用できます。攻撃者は、同フォーラムにログイン済みのユーザに、細工を施した不正なリンクをクリックさせて認証情報を窃取します。この認証情報を使用すると、DJIの次のオンライン・リソースにアクセスできます。

  • DJIのWebプラットフォーム(アカウント、ストア、フォーラム)
  • パイロット・アプリ「DJI GO」または「DJI GO 4」から同期されたクラウド・サーバ上のデータ
  • DJIのドローン集中管理プラットフォーム「FlightHub」

チェック・ポイントは、2018年3月に本脆弱性をDJIに報告しましたが、同社は責任を持って対処され、本脆弱性はすでに修正されています。DJIは、本脆弱性を「高リスク/低確率」と評価し、チェック・ポイントの研究者によるアクセスを除き、同脆弱性が悪用された証拠は見つかっていないとしています。

 

図: 想定される3つの攻撃フロー(簡略図)

攻撃のデモ動画
(注: 動画および図に含まれる飛行経路のスクリーンショットと画像は、 Airdataから提供していただきました。同社は今回の調査には関係しておらず、また本脆弱性の影響も受けていません)

 

技術分析
以降では、Webサイト、モバイル・アプリ、FlightHubというDJIの3つのプラットフォームから、同社製ドローンのフライトおよびユーザに関する機密情報にアクセスする手順について解説します。
まずは、脆弱性が存在する場所とその仕組みについて説明します。

 

脆弱性
ごく簡単に説明すると、この脆弱性はDJIのユーザ識別プロセスに存在します。
DJIでは、ユーザの識別や、各プラットフォームにアクセスするためのトークン(チケット)の作成にCookieを使用していますが、このCookieを窃取することができるのです。このCookieを使用すると、ユーザのアカウントを簡単に乗っ取り、そのユーザのモバイル・アプリ、Webアカウント、FlightHubアカウントを完全にコントロールできます。
リサーチ・チームはまず、DJI Webサイトのログイン・プロセスを把握するところから調査を始めました。DJIのバックエンドによるユーザの識別方法とログイン処理で重要な役割を果たすパラメータおよびCookieを確認するためです。そこで、クライアント(Webブラウザ)とDJIバックエンドとの間で送受信されるすべてのトラフィックを分析しました。
するとすぐ、各サービス用に次のサブドメインを使用していることが分かりました。

  • forum.dji.com
  • account.dji.com
  • store.dji.com

また、ドメイン間のログインにはOAuthフレームワークを使用していることが判明したため、サブドメイン間のトラフィック分析にも着手しました。
この分析の結果、mobile.php URLへのリクエストから、テストに使用したDJIユーザの機密情報を入手できることが分かりました。username、member_uid、tokenなどです。

この結果を受けて、DJIのバックエンドによるユーザの識別方法と、バックエンドが同じ識別IDを使用しているかどうかを調査。ここで使用されているCookieを調べたところ、「meta-key」というCookieがユーザの識別に使用されていることが分かりました。

 

 

図1: mobile.phpへのリクエスト

このmeta-key Cookieを入手するためには、http-only属性で保護されていないサブドメインを見つける必要があります(この属性は、JavaScriptによるCookieの漏洩を阻止します)。
調査の結果、forum.dji.comがこのニーズを満たすことが分かったため、このプラットフォームで利用可能な脆弱性を探すことにしました。

 

脆弱性の発見
インターネットで検索したところ、偶然次のようなGETリクエストを見つけました。https://forum.dji.com/forum.php?mod=ajax&action=downremoteimg&message=
messageパラメータはレスポンスに反映されていましたが、次の2つの問題がありました。

  1. 「”」、「‘」、「/」の前にスラッシュを追加するaddslashes関数が使用されている。
  2. XSSペイロード・インジェクションが未定義のupdateDownImageListという関数で発生している。この関数は、私たちが把握できていない別のJavaScriptコードからインポートされています。

GETリクエストに対するレスポンスは、次のような疑似コードになると想定しました。

そこでまず、関数エスケープに対処するため、バックスラッシュと一重引用符を次のように処理しました。
parent.updateDownImageList(‘ \’ ‘);
このようにすると、addslashes関数によって、上で指定したバックスラッシュをエスケープするバックスラッシュが追加され、次のような文字列になるはずです。
parent.updateDownImageList(‘ \\ ‘  ‘);
続いて、残る「‘」、「)」、「;」と、未定義の関数updateDownImageListにも対処する必要があります。
「‘」、「)」、「;」については、htmlコメント「<!–」を追加して対処することにしました。この結果、ペイロードは次のようになります。
parent.updateDownImageList(‘ \'<!–  ‘);
最後に残ったのは、未定義の関数updateDownImageListです。この関数は定義されていないため、自分で定義する必要があります。
最終的なペイロードは次のようになりました。
\’ alert(document.cookie); function updateDownImageList (data) {} <!–

 

図3: ペイロードに対して返されたCookie

これで、meta-key Cookieを特定のWebサイトに送信するペイロードを作成できるようになりました。このタイプのXSSは、どのようなXSS保護機能でもブロックできません。ペイロードがJavaScript自体の中にあり、スクリプトやイベントを含んでいないからです。

このXSS攻撃を成立させるために必要なのは、ペイロードへのリンクを含むコンテンツをDJI Forumに投稿することだけです。ただしDJIは、フォーラム内のコンテンツへのリンクしか投稿できないように制限しており、この方法では不正なWebサイトへのリンクを投稿することはできません。

 

図4: DJI Forumに投稿された不正なWebサイトへのリンク(例)

リサーチ・チームのXSSはフォーラム内にあるため、このリンク制限を回避することができます。また、多くのユーザがフォーラムの情報をやり取りしており、これらのユーザがメッセージやリンクを共有してくれるため、攻撃者自身がリンクを広める必要はありません。
meta-key Cookieの取得に成功した後は、ログイン・プロセスの検証に進み、DJIのバッグエンドによる各DJIプラットフォームでのログイン処理の方法を確認しました。まず調査したのは、DJIのWebサイトです。

 

DJI Webサイト
DJI Webサイトのユーザ・アカウントにアクセスするために必要なのは、対応するmeta-key Cookieだけです。このCookieは、サブドメインのaccount.dji.comでは「mck」と呼ばれています。
まず、DJIのアカウントを作成してログインしました。ログイン・プロセスを分析したところ、サブドメイン間でのユーザ認証(accounts.dji.comからforum.dji.comにアクセスし、その後でdji.comに戻るなど)にはOAuthを使用していることが分かりました。
そのため、DJIがユーザ認証を必要とするたびに、ユーザはinitData.doとmck Cookieを送信することになります。これに対するレスポンスは、チケットを含むコールバックURLとなります。
このURLに接続すると、ユーザは認証情報なしで認証されます。
したがって、アカウントを乗っ取るためには次の手順が必要となります。

  1. 攻撃を行うための認証情報でdji.comにログインし、DJI.COMでクリックする。すると、dji.comにリダイレクトされる。
  2. initData.doリクエストのmck Cookieの値を、XSSで入手した標的のmck Cookieに置き換える。
  3. ログイン・プロセスを続行し、標的のアカウントにアクセスする。

initData.doリクエストの中身を以下に示します。

 

図5: initData.doリクエスト

 

DJIアプリ
DJIモバイル・アプリでアカウントを乗っ取る際には、アプリ自体に実装されたいくつかの保護対策をバイパスする必要がありました。
ログイン・プロセスを調査するためには、アプリからDJIのバックエンドに送られるリクエストをインターセプトする必要があります。しかし、SSL Pinningにより、アプリケーション・トラフィックのインターセプトと調査が妨害されてしまいました。
そこで、アプリを逆コンパイルしてSSL Pinningのバイパス方法を突き止めようとしましたが、アプリではモバイル・アプリ向けセキュリティ企業SecNeoの保護技術が採用されており、逆コンパイルも失敗に終わりました。
SecNeoの説明によると、同社は次の保護技術を提供しています。

  • ソースコードのリバース・エンジニアリングを防止、機密データを保護
  • アンチフック技術により、アプリの改変、再パック、デバッグを防止
  • 動的なコード挿入と逆コンパイル/逆アセンブルを防止

そこで、Fridaを使用して上記対策のバイパスを試みました。しかし、試行錯誤してみても、dji.go.v4アプリケーションにはアタッチすることができません。
dji.go.v4プロセスにアタッチできない理由を探るため、まず「frida-ps –U」コマンドを使って、モバイル・デバイスで実行中の全プロセスをリストアップしました。
すると、コマンド実行直後は1つしか存在していなかったdji.go.v4プロセスが、数秒後、もう1つ開始されていることに気づきました。

/proc/11194/statusを見てみると、新しいプロセスは最初のプロセスにアタッチされ、デバッグを行っていることが分かりました。すでにdji.go.v4プロセスのデバッグが行われていること。これが、Fridaでデバッグできない理由だったのです。

 

図6: 最初のdji.go.v4プロセスにアタッチされた新しいdji.go.v4プロセス(Fridaで確認)

最初に開始されたプロセスはデバッガではなく、実際のアプリケーションであることが分かっています。デバッガは、この実際のアプリケーションにアタッチされ、アプリケーションを保護していました。これは、競合状態が発生していることを意味しています。この状況を利用すれば、アプリケーション・プロセスにフックをアタッチし、デバッガ・プロセスの開始前にデタッチすることができるはずです。
そこで、Burp Suite証明書を用意してモバイル・デバイスにコピーし、DJIアプリケーションを独自に生成しました。すると、デバッガの初期化前にこのアプリケーションがサスペンド・モードで起動します。
続いて、次の処理を行うFridaスクリプトを用意しました。

  1. Burp Suite証明書を開き、X509証明書を利用可能にする。
  2. KeyStoreをロードして証明書を格納する。
  3. TrustManagerFactoryを作成し、Burp Suite証明書を格納した先ほどのKeyStoreで初期化する。
  4. SSLContextをオーバーロードし、用意したTrustManagerで上記のTrustManagerをフックする。

フックが完了した後、サスペンド・モードのアプリケーションを再開し、フックをアプリケーションからデタッチしました。これで、すべてのフックの処理が終わった後で、デバッガによる保護を開始することが可能になります。

以上の方法により、SSL Pinningをバイパスし、トラフィックをBurp Suiteで分析できるようになりました。

 

図7: SSL Pinningのバイパス後、Burp Suiteに表示されたトラフィック

SSL Pinningをバイパスできたところで、モバイル・アプリケーション・トラフィックをインターセプトするためのプロキシを設定しました。
Webアプリケーションのログイン・プロセスを分析したところ、ユーザが認証情報を入力すると、モバイル・アプリによって/apis/apprest/v1/email_loginにリクエストが送信されることが分かりました。これに対するレスポンスは、次のような内容になっています。

{“code”:0,”message”:”ok”,”data”:{“nick_name”:”XXXXXXXXX”,”cookie_name”:”_meta_key”,”cookie_key“:”NTUxNjM2ZTXXXXXXXXXXXXXXNmI2NjhmYTQ5NGMz“,”active”:false,”email”:”XXXXXXXX@gmail.com”,”token“:”XXXXXXXXX2139“,”validity”:15275XXXXX,”user_id”:”9629807625XXXXXX”,”register_phone”:””,”area_code”:””,”inner_email”:false,”subscription”:false,”vipLevel”:null,”vipInfos”:[]}}

ここで注目すべきは、次の2つのパラメータです。

  • cookie_key – ここまでに何度も言及してきた、DJI Forumからのmeta-key/mckです。
  • token – 本レポートの冒頭で紹介した、request mobile.phpから取得できるパラメータです。

 

アカウントを乗っ取る手順
アカウントを乗っ取る手順は次のとおりです。

  1. まずmeta-keyと、自身のトークンと置き換える標的のトークンを入手する必要があります。そこで、標的のmeta-keyをmobile.phpに送信し、対応するトークンを入手します。
  2. 次に、自分自身の認証情報を入力します。すると、ログイン・リクエストが送信されます。
  3. 手順2に対するレスポンスを受け取ったら、cookie_keyの値を標的のmeta-keyで、自身のトークンを手順1のトークンで置き換えます。
  4. これで、標的のアカウントへの完全なアクセス権を入手できたことになります。

この手順で脆弱性を悪用すると、標的のアカウントを乗っ取り、フライト情報や撮影した写真など、同期されたすべてのデータにアクセスできます。
さらに調査を進めたところ、フライト・ログ・ファイルを解析すれば、ドローン飛行中に撮影した写真の位置情報や角度、ドローンの登録地、記録された最後の所在地など、さらに多くの情報を得られることが分かりました。

フライト・ログ・ファイルを入手するために必要な作業は、モバイル・デバイスとフライト記録を同期することだけです。これにより、DJIのクラウド・サーバに手動でアップロード済みのすべてのフライト・ログがモバイル・デバイスに保存されます。フライト・ファイルは、すべてDJI/dji.go.v4/FlightRecordフォルダに保存されます。また、これらのファイルをDJIのサイトにアップロードすれば、標的のフライト・データに関するさまざまな情報を参照できます。

 

(注: 上記の飛行経路のスクリーンショットと画像は、Airdataから提供していただきました。同社は今回の調査には関係しておらず、また本脆弱性の影響も受けていません)

 

DJI-FlightHub

DJI-FlightHubは、ドローン操縦の詳細な可視化および管理機能を提供するエンタープライズ・ユーザ向けのWebアプリケーションです。マップ・ビュー機能や、ドローン周辺の音を聞くこともできるライブ・カメラ・ビュー機能を備えており、世界中のどこからでも、ドローンの操縦状況をリアルタイムで確認できます。このため、ドローンの正確な位置を把握して、複数のドローンを連携して操縦することが可能です。
DJI-FlightHubには、管理パネルにアクセスするためのデスクトップ・アプリケーション、ドローンを操縦するためのパイロット・アプリケーション、管理機能にアクセスするためのWebポータルが用意されています(Webポータルは今回の攻撃に利用できます)。Webポータルは、www.dji-flighthub.comからアクセスできます。
DJI-FlightHubには、管理者、キャプテン、パイロットという3種類のアカウントがあります。管理者は、DJI-FlightHubアカウントの管理を担当し、DJI-FlightHubプラットフォームへのアクセス、フライト・データの表示、キャプテン・アカウントまたはパイロット・アカウントの新規作成を行えます。キャプテンは、DJI-FlightHubプラットフォームへのログインとパイロット・アカウントの新規作成を行えます。パイロットは、パイロット・アプリによるドローンの操縦と、ドローンとDJI-FlightHubアカウントの関連づけを行えます。

つまり、管理者アカウントまたはキャプテン・アカウントにアクセスできれば、ドローンの操縦状況をリアルタイムで確認できるということです。管理者アカウントまたはキャプテン・アカウントを乗っ取るためには、DJI-FlightHubのログイン・プロセスを理解する必要があります。

 

図8: DJI-FlightHubのログイン・ページ

「login」をクリックすると、initData.doリクエストが送信されますが、account.dji.comポータルの場合とは違い、レスポンスにチケットが含まれていません。チケットは、ユーザが認証情報を入力したときのlogin.doレスポンスに含まれています。先ほど、Webポータル経由でアカウントの乗っ取りに成功したaccount.dji.comとは違う動作であるため、DJI-FlightHubでアカウントを乗っ取るためには、別の方法を考える必要があります。
チケットは、initData.doリクエストで生成できることが分かっていますが、DJI-FlightHubでは何らかの理由でうまくいきませんでした。そこで、リクエストを検証してその理由を探ったところ、DJI-FlightHubのinitData.doリクエストには、DJI-FlightHubのappIdが含まれていることに気づきました。これが、レスポンスにチケットが含まれていない理由だったのです。これを踏まえて、appIdを別の既知の情報に置き換えれば、チケットを取得できるのではないかと推定しました。チケットさえ入手できれば、残る作業は、別のappIdのチケットでも機能するかどうかを確認することだけです。
ここで必要な作業は次のとおりです。

  1. 乗っ取り対象とする管理者のmckと共にappId=storeのinitData.doリクエストを送信し、レスポンスでチケットを取得する。
  2. FlightHubにログインする際、リクエストをインターセプトし、login.doリクエストのmckを変更し、そのレスポンスで、initData.doリクエストで受け取ったチケットに切り替える。これにより、その管理者のアカウントにリダイレクトされます。

このようにして攻撃者によるアカウントへのアクセスが行われても、本来の管理者への通知は一切行われません。その間、攻撃者は、現在リアルタイムで行われているフライトのドローン・カメラにログインして、一切の制限なしに映像を参照したり、FlightHubプラットフォームにアップロード済みの過去のフライト記録をダウンロードしたりできます

 

英文オリジナルはこちらをご確認ください。
https://research.checkpoint.com/dji-drone-vulnerability/