HVACking:Delta 産業用制御システムの脆弱性と影響を解説

McAfee LabsのAdvanced Threat Research Team(ATR)は、より安全な製品を開発者が事業や個人に提供するのをサポートするため、ソフトウェアとハードウェア双方のセキュリティに関する問題の発見に取り組んでいます。私たちはこのほどDelta Controls社製の産業用制御システム(ICS)について調べました。「enteliBUS Manager」と呼ばれるこの製品は、ビル管理など複数のアプリケーションに使用されています。Deltaのコントローラを調べた結果、「main.so」ライブラリで未報告のバッファーオーバーフローが発見されました。CVE-2019-9569によって識別されたこの欠陥は、最終的にリモートコード実行を可能とし、悪意のある攻撃者によってアクセスコントロール、ボイラー室、冷暖房空調設備などが操作される可能性があります。私たちは2018年12月7日にこの調査結果をDelta Controlsに報告しました。Deltaは数週間のうちに対応し、私たちは継続的な対話を開始し、セキュリティー修復の構築とテストを経て、2019年6月下旬に公開しました。その全体のプロセスにおけるDeltaの努力と協力を私たちは高く評価します。今回の調査で確認された脆弱性とその潜在的な影響について詳細の技術的分析をお伝えします。


攻撃対象範囲の調査

新しいデバイスを調査するにあたっての最初の課題は、ソフトウェアとハードウェアの両面から、それがどのように機能するかを理解することです。産業用制御システム領域の多くのデバイスと同様に、このデバイスには3つの主要なソフトウェアコンポーネントがあります。ブートローダー、システムアプリケーション、そしてユーザー定義プログラミングです。攻撃の経路についてソフトウェアを調べることは重要ですが、ユーザーが定義する対象範囲についてはインストールごとに変更される可能性があるため重点を置いていません。したがって、私たちが重視したいのは、ボートローダーとシステムアプリケーションです。オペレーティング・システムについては、ユーザー固有のプログラミングに関わらず、デバイスを運用するためにメーカーがカスタムコードを実装するのが一般的です。このカスタムコードは脆弱性が最も多く存在する場所にあることが多く、プロダクトインストール基盤の全体に広がっています。それにもかかわらず、私たちはどうやってこのコードにアクセスすればいいのでしょうか?重要なシステムであるがゆえに、ファームウェアとソフトウェアは公開されておらず、資料も限定されています。従って、基礎となるシステムソフトウェアの周辺を調査するしか手はありません。最も重大な脆弱性がリモートであることを考えると、デバイスの単純なネットワークスキャンから始めることは理にかなっています。TCPスキャンではポートが開かれず、UDPスキャンによってのみポート47808と47809が開かれたことを確認しました。資料を参照して、これがおそらくBuilding Automation Control Network(BACnet)と呼ばれるプロトコルに使用されていることを特定しました。BACnet固有のネットワーク列挙スクリプトを使用して、もう少し情報を見つけ出しました。

root@kali:~# nmap –script bacnet-info -sU -p 47808 192.168.7.15

Starting Nmap 7.60 ( https://nmap.org ) at 2018-10-01 11:03 EDT
Nmap scan report for 192.168.7.15
Host is up (0.00032s latency).

PORT STATE SERVICE
47808/udp open bacnet
| bacnet-info: 
| Vendor ID: Delta Controls (8)
| Vendor Name: Delta Controls
| Object-identifier: 29000
| Firmware: 571848
| Application Software: V3.40
| Model Name: eBMGR-TCH

次に問題となるのは、ハードウェアからは何を学べるのか、ということです。この問題の答えを出すため、図1に示すように、最初にデバイスを慎重に分解しました。

図1

コントローラーには、ディスプレイを管理する1つのボードと、プロセッサーモジュールとフラッシュモジュールの両方を含むシステムオンモジュール(SOM)チップを保持するメイン基板があります。その基盤をよく観察し、私たちはいくつかの重要な所見を述べました。まず、プロセッサーはARM926EJコアプロセッサー、フラッシュモジュールはボールグリッドアレイ(BGA)チップで、基盤には複数の未実装ヘッダーがありました。

図2

ソフトウェアをより効果的に調べるには、ファームウェアを引き出す方法を決めなければなりませんでした。フラッシュメモリーのシステムに使用されるBGAチップはほぼ間違いなくファームウェアを保持しているとみられますが、ここで新たな課題が生じます。BGAチップはほかのチップと違って、付着可能なピンを対外的に提供していません。つまり、チップに直接アクセスするため、はんだ付けされたチップを基盤から除去しなければなりません。これはシステムを損壊する恐れがあり、良い考えとは言えません。

また、基盤に複数の未実装ヘッダーがあることも判明しました。これらのヘッダーの1つを使用してファームウェアを取り出す別の方法を見つけることができるので、これは有望でした。それぞれの未実装ヘッダーにピンをはんだ付けし、ロジックアナライザーを使用することで、基盤の中央にある4ピンヘッダーは115200の通信速度で実行中の汎用非同期送受信回路(UART)ヘッダーであると判断しました。

図3

Exodus XI Breakoutボード(@Logan_BrownとExodusチームに敬意を)を使用してUARTヘッダーに接続すると、保護されてないルートプロンプトがシステムに表示されます。今やシステムにフルアクセスすれば、システムがどのように機能するかより深い理解を得て、ファームウェアを抽出できるようになります。

図4

ファームウェア抽出とシステム分析

UARTインターフェイスを使用すると、リアルタイムでシステムを調査できますが、オフライン分析のためのファームウェアをどのように取り出せばいいのでしょうか?デバイスには2つのUSBポートがあり、ともにUSBドライブを実装できます。これにより、ddを使用してメモリーで実行中のものをフラッシュドライブにコピーできるようになり、ファームウェアを効率的に抽出します。次なる課題は、何をコピーするか、でした。

「/proc/mtd」を使用してメモリーがどのように分割されているかの情報を得ると、mtd4とmtd5にあるファイルシステムを見ることができます。ddを使用してmtd4とmtd5の両方のパーティションからコピーしました。私たちは後に、もしも永続的な問題が検出されたら、画像の1つがシステムフォールバックとして使用されるバックアップであることを発見しました。プロジェクトが続くにつれて、このファイルシステムのコピーはますます有益になりました。

アクティブなUART接続を使用することでシステムがどのように実行しているかをより詳細に調べられるようになりました。私たちはこのデバイスが 47808と47809のポートだけをリッスンすることを、前もって決定することができるので、これらのポートをリッスンするいかなるアプリケーションも、リモートエクスプロイトの唯一の攻撃ポイントになります。これは、UARTコンソールから「netstat-nap」を使用するとすぐに確認されました。

47808ポートは「dactetra」と呼ばれるアプリケーションによって使用されていることがわかりました。最低限の追加調査の結果、これはデバイスの主要機能を担うDeltaコントローラー特有のバイナリであると判断しました。


脆弱性を確認

オープンポートを介したネットワークをリッスンするデバイス固有のバイナリーを使用すれば、脆弱性の探索を開始するのにふさわしい場所がわかります。調査を始めるにあたって、私たちは一般的なネットワークファジングのアプローチを用いました。BACnetのネットワークファジングを実行するため、BACnetサーバ用に設計されたモジュールを持つSynopsys製のDefensicsと呼ばれるツールを用いました。このデバイスはBACnetサーバではなく、ルーターとしての機能を持ちますが、このテストスイートは、着手するのに最適な場所を示す複数のユニバーサルテストケースを提供します。BACnetは通信するために何種類かのブロードキャストパケットを活用します。「Who-Is」と「I-Am」の2つの通信パケットはすべてのBACnetデバイスに対応し、Defensics はそれらと連動するモジュールを提供します。Defensicsファズツールを使用してこれらのパケットの変異を作成することで、図5に示すように、デバイスが障害ポイントに遭遇してコアダンプを生成し、すぐに再起動するのを観察することができました。

図5

クラッシュを引き起こしたテストケースはその後切り分けられ、クラッシュに反復性があることを確認するため、何度か実行させました。このプロセスの過程で、クラッシュを引き起こすオリジナルの不正形式のパケットの後、さらにに96パケットが送信されたことを確認しました。一連の不正形式のパケットは、以下にあるように「I-Am」パケットでした。フルパケットは容量が大きいため表示されません。

図6

さらに調査を進めると、ファズツールが「0x22」を使用して8216バイトのBACnetのレイヤーサイズを持つパケットを生成したことがすぐにわかりました。また、ファズツールがBACnetアプリケーションレイヤーの最大受容サイズをわずか1476バイトと認識したこともわかりました。追加テストによって、このパケットを送信するだけでは同じ結果とはならないことがわかりました。97のすべてのパケットが送信された場合のみ、クラッシュが発生しました。


クラッシュの分析

システムはクラッシュにコアダンプを提供するため、さらなる情報を得るために分析することは理にかなっています。コアダンプ(図7で再現)により、デバイスがセグメンテーションの欠陥に直面したことが確認できました。また、レジスターR0が破損しているおそれがあるバックトレースとともに、不正形式のパケットをコピーしたデータと似通ったものを含んでいることがわかりました。

図7

コアダンプによってクラッシュの正確な場所も判明しました。デバイスのメモリーマップを使用することで、アドレス0x4026e580がmemcpyに位置していると判断することができました。デバイスは、Address Space Layout Randomization(ASLR)を導入していないため、メモリーアドレスはテストを通じて変更はありませんでした。ファームウェアの抽出に成功したので、IDA Proを使用してこのクラッシュの原因についてさらなる調査を試みました。開発者がコンパイル時にバイナリーを削除していなかったことで、IDAでの反転処理が容易になりました。

図8

分解によって、memcpyがR0に格納されている「アドレス」にR3内にあるものを書き込もうとしていたことがわかりました。ところが、このケースではそのアドレスを破損したため、セグメンテーションの欠陥を引き起こしてしまいました。いくつかの他レジスターのコンテンツも新たな情報を提供しました。R3内の0x81値は、BACnet Virtual Link Control (BVLC)レイヤーからの最初のBACnetパケットのバイトである可能性があり、そのパケットをBACnetとして識別しました。R3と、R5にあるアドレス値を一緒に調べることで、これがまぎれもなくBVLCレイヤーであるとの確信を高めました。これは、コピーされたデータが最後に送信されたパケットからきたもので、コピーデータの送信先が最初の不正形式のパケットから得られたものであることを示唆しています。レジスターR8とR10はソースと送信先ポート番号をそれぞれ持っており、このケースでは0xBAC0(エンディアンを構成)、標準的なBACnetポートの47808でした。調べてみると、R4には上書きの痕跡があるメモリーのセクションを示すメモリーアドレスがありました。ここに示すのが不正形式のパケットからのデータ(0x22)で、いくつかの領域では、メモリーはパケットデータで一部上書きされていました。memcpyの送信先の値はこの領域のメモリーに由来しているようでした。ASLRが有効ではないので、いつもこれが同じ位置にくると予測することができます。

図9

コアダンプ、パケット、IDAから得られた情報によって、この時点では、発見されたクラッシュはバッファー・オーバーフローでほぼ間違いないと考えました。ただし、memcpyはとても一般的な関数なので、このクラッシュの原因をより正確に究明する必要がありました。memcpyの送信先アドレスが破損しているとすれば、memcpyでのクラッシュはバッファー・オーバーフローによる単なる二次的被害ですが、それではどんなコードが実際にバッファー・オーバーフローを引き起こしたのでしょうか?この分析を始めるのにふさわしいのは、バックトレースでしょう。ただし、上記にあるように、バックトレースは入力データにより破損しています。このデバイスはARMプロセッサーを使用しているため、どんなコードがmemcpyを呼び出しているかのヒントをLRレジスターから探すことができます。処理のメモリーマップを参照すると、ここでは、LRは「main.so」に落ち込んだ0x401e68a8を指しています。統計分析に使用するため補正値を計算すると、図10にあるコードにたどり着きました。

図10

LRレジスターはmemcpyが戻った後に呼び出された指示を指しています。この場合、補正値0x15C8A4でLRが指しているアドレスの直前の指示に興味を持ちました。最初は予期していたmemcpyの呼び出しがなかったことに驚きましたが、scNetMove関数を少々掘り下げてみると、scNetMoveは単にmemcpyのラッパーだったことがわかりました。

図11

それでは、どうやって間違った送信先アドレスがmemcpyに渡されたのでしょうか?これに答えるためには、memcpyに送信されるバッファーの設定にどんなコードが対応しているのかとともに、システムがどうやって着信するパケットを処理しているのかについて理解をより深める必要があります。私たちはメインプロセスが19個のスレッドを生成しているのを見るため、psを使用して実行中のシステムを評価することができます。

「scNetMove」で発見した関数は「scBIPRxTask」と呼ばれ、メインバイナリーの外部の一か所のみで、つまりアプリケーションのネットワークの初期化関数において参照されました(図12参照)。

図12

scBIPRxTaskを分解したところ、新しいスレッド、つまり、ポート47808とポート47809上の両方のBACnet IPインターフェイスのために作成された「タスク」を発見しました。生成されたこれらのスレッドはそれぞれのポートに着信するすべてのパケットを処理します。パケットがシステムによって受信されると、scBIPRxTaskに対応するスレッドがそれぞれのパケットをトリガーします。IDA Proデコンパイラーを使用すると、それぞれのパケットで何が起きるかを確認できます。まず、この関数はmemsetを使用してスタック上に割り当てられたバッファーを消去し、ネットワークソケットからこのバッファーに読み込みます。このバッファーは次のmemcpy呼び出しのソースになります。新しいバッファーは1732バイトの静的サイズで作成され、ソケットから1732バイトのみが適切に読み取られます。

図13

ソケットからデータを読み込んだ後、この関数は受信したばかりのパケットを格納する場所を設定します。ここでは「pk_alloc」と呼ばれる関数を使用します。これは作成するパケットのサイズを唯一の引数とします。サイズは新たな静的な値となり、ソケット読み取り機能から受信したサイズではないことがわかりました。今回渡される新たな静的な値は1476バイトです。この割り当てられたバッファーはmemcpyの送信先になるものです。

図14

ソースと送信先の両方のバッファーが割り当てられると、「scNetMove」が呼び出され、続いてmemcpyが呼び出され、ソケット読み取りの戻り値から取得したサイズパラメータとともに両方のバッファーが渡されます。

図15

このコードパスでは、脆弱性がなぜ、どのように起こるかを説明しています。送信されたパケットごとに、スタックからメモリーにコピーされます。 ただし、パケットが1476バイトより長い場合、1476より大きく1732以下のバイトでは、送信先バッファーの最後を過ぎたメモリー内の多くのバイトが上書きされます。上書きされたメモリー内には、その後のmemcpy呼び出し送信先へのアドレスがあります。これは任意の書き込み条件につながるバッファー・オーバーフローの脆弱性があることを意味します。最初の不正な形式のパケットは、攻撃者が定義したデータ(このケースでは攻撃者が書き込みを希望する場所のアドレス)でメモリの一部を上書きします。追加の95パケットがシステムによって読み取られた後、攻撃者によってコントロールされたアドレスは、送信先バッファーとしてmemcpyに入力されます。不正な形式である不要な最後のパケットのデータは、以前の不正な形式のパケットで設定された場所に書き込まれる内容となります。最後のパケットも攻撃者によってコントロールされていると仮定すると、これはwrite-what-where状態になります。


ウォッチドッグタイマーの無効化

発見した脆弱性をしっかりと把握した上で、次の論理的なステップは実用的なエクスプロイトを作成することです。エクスプロイトを開発するとき、ターゲットを動的にデバッグする機能は非常に重要です。そのために、チームは最初にgdbserverなどのデバッグツールをデバイス固有のカーネルとアーキテクチャー用にクロスコンパイルする必要がありました。このデバイスは古いバージョンのLinuxカーネルを実行しているので、古いバージョンのBuildrootを使用してgdbserverとそれ以降の他のアプリケーションを構築しました。

USBドライブを使用してgdbserverをデバイスに転送し、実行中のアプリケーションをデバッグする最初の試みが行われました。図16に示すように、デバッガーをアプリケーションに接続して数秒後に、デバイスは再起動を開始しました。

図16

エラーメッセージは、なぜクラッシュが発生したのかについての手がかりを与えてくれます。それは、ウォッチドッグタイマーの不具合を示しています。ウォッチドッグタイマーは、重要な組み込みデバイスでは一般的で、システムが一定時間応答しない場合、問題を解決しようと対処します。このケースでは、開発者が選択した操作はシステムを再起動することです。このエラーメッセージのシステムバイナリーを検索すると、図17に示すコードのセクションが明らかになりました。実際のエラーメッセージはベンダーの要求により編集されています。

図17

この関数は、3つのカウンターをデクリメントします。いずれかのカウンターがゼロになると、エラーがスローされ、それからシステムが再起動されます。コードをさらに調べると、複数のプロセスがこの関数を呼び出してカウンターをひっきりなしにチェックしていることがわかります。つまり、このソフトウェアのウォッチドッグを無効にする方法を見つけ出さなければ、システムを動的にデバッグすることはできません。

この問題に対する一般的なアプローチの1つは、バイナリーにパッチを当てることです。バイナリ―へのパッチ適用を検討する際に重要なのは、使用しているパッチが意図しない副作用を引き起こさないようにすることです。これは一般的に、できる限り変更を最小限にとどめたいことを意味します。このケースでチームが考え出した最小限の有意義な変更は、「5を引く」を「0を引く」に変更することでした。これによってプログラム全体がどのように機能するのかが変わることはありませんが、カウンターをデクリメントするために関数を呼び出しても、必ずしもカウンターが単純に小さくなるとは限りません。パッチされたコードは図18に示してあります。IDAデコンパイラーが、もはや意味がなくなったコードから減算ステートメントを完全に削除したことに留意してください。

図18

ソフトウェアのウォッチドッグがパッチされた状態で、チームはアプリケーションを再び動的にデバッグしようとしました。gdbserverに接続してアプリケーションのデバッグを開始することができたため、当初はテストは成功したとみられていました。ところが、3分後にシステムがまたもや再起動しました。図19は、同じ結果をもたらす実験が何度か繰り返された後、再起動時にチームが検出したメッセージを示しています。

図19

これは、開始時の起動フェーズでのハードウェアのウォッチドッグが180秒(3分)に設定されていることを示します。システムにはハードウェアとソフトウェアの2つのウォッチドッグタイマーがあります。そこでタイマーを1つだけ無効にしました。バイナリ―にパッチを当てる方法はソフトウェアのウォッチドッグタイマーを無効にするために使用した方法と同じで、ハードウェアのウォッチドッグタイマーには機能しません。つまり再起動を防ぐためにアプリケーションもウォッチドッグを無効にする必要があります。この知識を武器に、ハードウェアのウォッチドッグを「追い出す」のに役立つかもしれないコードのためにデバイス上にあるDeltaバイナリーに目を向けました。デバッグの特徴が残っているので、ハードウェアのウォッチドッグ管理に対応する関数を比較的簡単に見つけることができます。

ハードウェアのウォッチドッグを無効にしようとする際に使用する可能性がある複数のアプローチがあります。このシナリオでは、ハードウェアウォッチドッグを処理したコードが共有ライブラリーにあり、エクスポートされたという事実を利用することにしました。これにより、既存のウォッチドッグ無効化コードを使用して新しいプログラムの作成が可能になりました。ハードウェアウォッチドッグを無効にする2番目のプログラムを作成することで、システムをリセットせずにDeltaアプリケーションをデバッグできました。

このプログラムはシステムのinitスクリプトに組み込まれていたため、起動時に実行され、継続的に「ドッグを追い出し」てハードウェアウォッチドッグを効果的に無効にしていました。注:このエクスプロイトの調査または作成において実際の犬が害を受けることはありません。それどころか、特別なご褒美をもらって、ウォッチドッグパッチのコーディングに貢献しました。その証拠に、これは今回の調査のリサーチャーが飼っている犬の最近の写真です。

図20

ハードウェアとソフトウェアの両方のウォッチドッグタイマーが収束したので、以前発見した脆弱性が悪用可能かどうかを引き続き判断することができます。


エクスプロイトの作成

エクスプロイトの実行を試してみる前に、まず私たちが気づいておく必要があるエクスプロイトの軽減または制限がシステムにあるかどうかを調べたいと考えました。まず、「checksec.sh」というオープンソースのスクリプトを実行しました。バイナリーで実行すると、このスクリプトは一般的なエクスプロイトの軽減が適用されているかどうかを報告します。図21は、「dactetra」という名前の主要なDeltaバイナリーで実行したときのスクリプトの出力を示しています。

図21

チェックの結果、NXのみが有効になりました。これは、脆弱なコードが存在する各共有ライブラリーにも当てはまります。

前述のように、この脆弱性によりwrite-what-where状態が可能になり、どこに何を書き込みたいのですか?という最も重要な質問につながります。最終的には、シェルコードをメモリーのどこかに書いて、そのシェルコードにジャンプしたいのです。攻撃者は最後に送信されたパケットをコントロールするため、攻撃者が自分のシェルコードをスタックに保有するのはもっともです。シェルコードをスタックに追加すると、checksecツールを使用して発見されたNo eXecute(NX)保護を回避する必要があります。これも可能ではあるのですが、もっと簡単な方法があるのではないかと思いました。

大きな不正な形式のパケットによって上書きされたメモリー位置でクラッシュダンプを再調査すると、攻撃者がコントロールできる合計32バイトの小さな連続したヒープメモリーのセクションが見つかりました。不正な形式のパケットのペイロードの内容である0x22バイトが存在するため、私たちはこの結論に至りました。オーバーフローが発生するとき、この領域の多くが0x22でいっぱいになりますが、write-what-where状態がトリガーされるまでに、これらのバイトの多くが上書きされてしまい、図22に示す32バイトのセクションが残ります。

図22

ヒープメモリーであるため、この領域も実行可能で、詳細はすぐに重要になります。不正な形式のパケットの0x22を繰り返しのないパターンに置き換えたことで、ペイロードのどこにシェルコードを配置するかが明らかになり、またこの領域のバイトがすべて固有のものであることが確認されました。

シェルコードを置く潜在的な場所で、次に対処するべき主要コンポーネントは実行をコントロールすることでした。write-what-where状態により、メモリ内の任意の場所に書き込むことができましたが、実行コントロールは付与されませんでした。この問題に対処する1つの技術が、Global Offset Table (GOT)を活用することです。Linuxでは、GOTは関数ポインターを絶対位置にリダイレクトし、ELF実行ファイルまたは共有オブジェクトの.gotセクションに配置されます。.gotセクションは実行時に書き込まれるため、通常はこの後の実行中でも書き込み可能です。再配置読み取り専用(RELRO)はエクスプロイトの軽減で、ロードされた.gotセクションがマッピングされると読み取り専用としてマークします。しかし、すでに見たように、この保護は都合がよいことに有効ではありません。これはwrite-what-were条件を使用してメモリー内のシェルコードのアドレスをGOTに書き込むことが可能であることを意味し、将来の関数呼び出しの関数ポインターを置き換えます。置き換えられた関数ポインターが呼び出されると、シェルコードが実行されます。

しかし、どの関数ポインターを置き換えるべきでしょうか?確実に成功確率を最高にするためには、可能な限り上書きに近いところで呼び出される関数に、ポインターを置き換えることが最善だと判断しました。これは、プログラムの実行中にメモリーレイアウトへの変更を最小限に抑えたいからです。「scNetMove」関数の戻りからコードをもう一度調べると、わずかな指示で「scDecodeBACnetUDP」が呼び出されているのがわかります。したがって、これはGOTで上書きするポインターの理想的な選択になります。

図23

何をどこに書くべきかがわかったので、次に脆弱性をトリガーするためにとられた正しいコードパスに適合するために必要なあらゆる条件を検討しました。バッファー・オーバーフローを引き起こすmemcpyのコードをもう一度見てみると、図24に示すように、上書きには確かに条件があることがわかりました。

図24

即値3でビット演算の論理積をしたとき、メモリー内で上書きを生成するコードはR0の値が0ではない場合にのみ使用されます。クラッシュダンプから、R0の値がコピーしたい送信先のアドレスであることがわかりました。これは問題を引き起こす可能性があります。書き込みをしたいアドレスが4バイトで揃えられた場合、(この可能性はとても高いのですが)、脆弱性のコードパスは採用されません。GOTで書き込みたいアドレスから1を引き、前のエントリーの最後のバイトを修復することで、コードパスを確実に取得できます。これにより、正しいコードパスが取得され、誤って2番目の関数ポインターを破損することはありません。


シェルコード

シェルコードを配置する場所は見つかりましたが、図25にあるように、ペイロードを書き込むスペースはごくわずか、具体的には32バイトしかみつけられませんでした。このような小さなスペースで何ができるでしょうか?大規模なシェルコードを必要としない1つの方法は、「return to libc」攻撃を使用してシステムコマンドを実行することです。エクスプロイトがそのまま使えるようにするには、システムで実行するコマンドであれプログラムであれ、デフォルトでデバイスに存在している必要があります。さらに、コマンド文字列自体は、処理しなければならない限られたバイト数を収容するために、かなり短くする必要があります。

理想的なシナリオは、デバイスへのリモートシェルアクセスを許可するコードを実行することです。幸いなことに、Netcatはデバイス上に存在し、このバージョンのNetcatは、接続のために持続的にポートをリッスンする「-ll」フラグと、接続時にコマンドを実行するための「-e」フラグの両方をサポートします。したがって、システムを使用して、いくつかのポートをリッスンするためにNetcatを実行し、接続が確立されたときにシェルを実行することができます。このコマンドでシステムを実行するためのシェルコードを書く前に、最初にデバイス上でさまざまなNetcatコマンドを直接テストして、それでもシェルを生成する最短のNetcatコマンドを究明しました。何度か反復した後、Netcatコマンドを13バイトに短縮することができました。

nc -llp9 -esh

命令は4バイトで揃える必要があり、対処すべき32バイトがあるので、4の倍数に最も近くなるように切り上げる文字列の長さにのみ関心があります。このケースでは16バイトです。これを合計32バイトから引くので、残りは16バイト、あるいは合計4つの命令があり、システムの引数を設定してそこにジャンプします。ARMでメモリー内の小さなスペースに、より多くの命令を収める一般的な方法は、Thumbモードに切り替えることです。というのも、ARMのThumbモードは、通常の32ビット(4バイト)ARM命令ではなく、16ビット(2バイト)命令を活用しているからです。残念ながら、このデバイスのプロセッサーはThumbモードをサポートしていなかったため、これは使えませんでした。

わずか4つのARM命令でタスクを達成するにあたっての課題は、ARMが即値に置く制限です。システムにジャンプするには、ジャンプ先アドレスとして即値を使用する必要がありますが、メモリーアドレスは一般的に小さい値ではありません。ARMの即値は12ビットに制限されています。 これらのビットのうちの8つは値自体のためであり、他の4つはビットシフトに使われます。つまり、即値の長さは1バイト(2桁の16進数)に限られますが、そのバイトには好きなやり方でゼロを埋め込むことができます。したがって、即値を使用して4バイトのフルメモリーアドレスを読み込むと、MOVを使用してもADDを使用しても4つの命令すべてが必要になります。処理すべき4つの命令に加え、システムの最初のパラメーターとして使用するレジスターであるR0にコマンド文字列のアドレスを読み込むために少なくとも1つ、そしてアドレスに分岐するためにも少なくとも1つの命令が必要です。 要求される命令は合計6つになります。

必要な命令数を減らす1つの方法は、シェルコードの実行時に必要なアドレスに近い値をすでに含んでいるレジスターをコピーすることから始めます。これが実現可能かどうかは、ジャンプしたいアドレスの値にかかっており、シェルコードが実行される直前にレジスターで利用可能なアドレスと比較します。

呼び出す必要があるアドレスから始めることで、ジャンプできる3つのアドレスがシステムを呼び出すことがわかりました。

  1. 0x4006425C – the address of a BL system (branch to system) instruction in boot.so.
  2. 0x40054510 – the address of the system entry in “so”’s GOT.
  3. 0x402874A4 – the direct address of system in libuClibc-0.9.30.so.

次に、図25に示すように、シェルコードがGDBを使用して実行されようとしているときに、これらのオプションとレジスター内の値を比較しました。

図25

シェルコードの実行時にアクセスできるレジスターのうち、その内容と、システムを呼び出すことができるこの3つのアドレスのうちの1つとの間で、最小デルタを与えるのはR4です。R4には0x40235CB4が含まれており、システムへの直接呼び出しのアドレスと比較すると、0x517F0のデルタが得られます。最後のニブルが0であることは理想的です。というのも最後のビットを考慮する必要がないからで、ARMの即値固有のローテーション・メカニズムによるものです。つまり、R4の内容を私たちが必要とするアドレスに変換するために必要なのは、2つの即値だけということです。1つは0x51000用、もう1つは0x7F0用です。あるレジスターを別のレジスターにMOVするときに即時オフセットを適用できるので、システムのアドレスを用いてレジスターを読み込むことが、2つの命令だけでできるはずです。分岐を実行するための1つの命令とコマンド文字列のための16バイトによって、32バイトですべてのシェルコードを取得できることになり、1つの命令でR0を文字列のアドレスに読み込むことができるとみなします。

4番目と最後の命令の直後にコマンドのASCII文字列を開始することで、文字列にそれを認識させる適切なオフセットでPCをR0にコピーできます。このアプローチによって加わる利点は、PCに関連するため、文字列のアドレスをメモリー内に配置されたシェルコードの場所から独立させることです。図26は、すべての制限を考慮したシェルコードの外観を示しています。

図26

重要なのは、nullで終わるASCII文字列リテラルをメモリーに配置するための「.asciz」アセンブラー・ディレクティブの使用に注意することです。R12 はARMアーキテクチャー上のIntra Procedural(IP)スクラッチ空間レジスターなので、分岐のアドレスを含むレジスターとして選択されました。これは、R12がサブルーチン内の汎用レジスターとしてしばしば使用されることを意味しており、想定外の悪影響を経験することなく、私たちの目的に合わせて上書きすることがほぼ間違いなく安全であることを示しています。


すべてをつなぎ合わせる

脆弱性、エクスプロイト、必要なシェルコードをしっかりと理解すれば、エクスプロイトの実行を試してみることができます。この攻撃を引き起こすために使用されたパケットのシーケンスを見ると、それは単一のパケット攻撃ではなく、複数のパケット攻撃でした。最初のバッファー・オーバーフローは大きな不正な形式のパケットに含まれていますが、そこにどんなデータを構築すればいいでしょうか?このパケットはメモリーを上書きしていますが、実行のコントロールを提供していません。 したがって、これは「セットアップ」または「ステージング」パケットと見ることができます。これは、memcpyが最後のパケットの送信先バッファーのアドレスを探す場所です。このパケットには上書きしたいアドレスが入り、その後にシェルコードが続きます。先に説明したように、上書きしようとしているアドレスは、GOT内のscDecodeBACnetUDP関数ポインターのアドレスから1を引いたもので、アドレスが4バイトで揃えられていないことを確認します。前の関数ポインターの最後のバイトを修復し、このアドレスを上書きすることで、実行をコントロールできるようになります。

大きな不正な形式のパケットには、「書き込み」先の「場所」が含まれており、シェルコードをメモリーに配置しますが、書き込む「内容」は含まれていません。この場合、その「内容」はシェルコードのアドレスなので、最後のパケットにこのアドレスが含まれる必要があります。最後の課題は、最後のパケットのどこにアドレスが属しているのかを判断することです。

不良アドレスに値0x81を書き込もうとしてmemcpyでクラッシュが起きた以前のコアダンプを思い出してください。0x81はBVLCレイヤーの最初のバイトで、必要なアドレスだけが上書きされるようにするために、アドレスが最後のパケット内に配置される必要があることを示しています。また、確実にアドレスの後にバイトがないようにする必要があり、そうでなければ、ターゲットアドレスを超えてGOTを上書きし続けます。このアプリケーションはマルチスレッドアプリケーションなので、シェルコードが実行する機会を得る前にアプリケーションがクラッシュするかもしれません。BVLCレイヤーは通常、パケットをBACnetパケットとして識別する方法であるため、このレイヤーを変更する際の潜在的な問題は、最後のパケットがもはやBACnetパケットのようには見えなくなることです。このようなケースでは、アプリケーションはなおパケットを取り込むのでしょうか?チームがこれをテストした結果、脆弱なコードはパケットタイプを検証するコードに先立って実行されるため、アプリケーションはタイプに関係なくすべてのブロードキャストパケットを取り込むことがわかりました。

すべてを考慮して一連の97個のパケットを送信することで、バインドシェルを作成してビル管理者をエクスプロイトすることに成功しました。以下はこの攻撃を示すビデオです。

 


現実世界のシナリオ

攻撃者にルートシェルを提供すれば脆弱性が悪用されることは明らかですが、攻撃者はそれを使って何ができるのでしょうか?攻撃者がシステムの通常の操作をコントロールしたり、貴重なデータを盗んだりできない限り、シェルはそれ自体では役に立ちません。その場合、デバイスに格納されている有用なデータはそれほど多くありません。システムの設定方法や何をコントロールしているかという情報をダウンロードできる者もおり、そこにはある程度の価値がありますが、それだけでは大きな影響はありません。使用不可の状態にターゲットを容易に配置できるDoS攻撃を用いて重要なシステムファイルを削除することも妥当ですが、単純な破壊もまたさまざまな理由から価値が低いものです。まず、前述のように、デバイスには起動中に障害が発生した場合にフォールバックするバックアップイメージがあります。デバイスに物理的なアクセスをしなければ、攻撃者はバックアップイメージが元のイメージとどのように異なるのか、または悪用できる可能性があるのかという明確な考えを持てません。バックアップイメージが異なるバージョンのファームウェアを使用している場合、エクスプロイトはもはや機能しません。おそらくもっと重要なことは、もともとDoS攻撃が繊細さに欠けていることに悩まされていることです。攻撃が実行されたときにすぐにアラームを停止させる場合、攻撃者はシステム内での持続性が長く続かないと予想できます。

システムが検出されずに攻撃者によってコントロールされた場合はどうでしょうか?このデバイスによってコントロールされる環境の種類を検討しながら、このシナリオはさらなる関心事になります。


通常のプログラミング

単なるルートシェルからデバイスの標準機能をコントロールするには、デバイスが通常の設定でどのように動作するかについてのより深い理解が必要です。通常、Delta eBMGRはインストーラーによって特定のタスクセットを実行するようにプログラムされています。これらのタスクは、アクセスコントロールの管理から建物の照明や空調設備などにまで及びます。一旦プログラムされると、コントローラーは複数の外部入力/出力(I/O)モジュールに接続されます。これらのモジュールは、接続されているデバイスの状態をコントロールすることと、情報を管理者に送り返すために利用されます。これらの「通常の状態」を再現するために、プロのインストーラーにサンプルのプログラムを使ってデバイスをプログラムさせ、適切なモジュールを添付しました。

図27は、各コンポーネントがサンプルプログラミングにどのように接続されているかを示しています。最初のテストでは、ポンプやボイラー、暖房弁のような大きな機器は実際に持っていませんでした。これらの機器の状態は、モジュール上のLEDかタッチスクリーンインターフェースのどちらかを通して追跡することができたため、テスト目的で入手することは不可欠でした。にもかかわらず、仮想であれなんであれ、それぞれの「デバイス」を入力または出力するどのタイプがモジュールに接続されているかに注目することは依然として重要です。

図27

これらのデバイスをコントロールするプログラミングは驚くほど簡単です。基本的に、入力に基づいて出力がレンダリングされます。図28は、テスト中にデバイスに存在するプログラミングロジックを示しています。

図28

3つのユーザー定義ソフトウェア変数があります。「暖房システム」、「室内温度Spt」、「暖房システム有効Spt」です。ここでの「spt」はセットポイントを指します。これらは実行時に管理者が定義でき、いつ出力をオンまたはオフにするかを決定するのに役立ちます。「暖房システム」バイナリー変数は単にシステムのオン/オフ状態をコントロールします。


デバイスをコントロール

最初に脆弱性を探し始めたときのように、デバイスをコントロールする方法はコントローラー毎によって異なるコードに依存しないようにする必要があります。そのため、Delta eBMGRに接続されているすべてのI / Oデバイスをコントロールする方法を見つけ、このデバイスの特定のプログラミングに依存しないようにします。

他のLinuxベースのシステムと同様に、最低レベルのインストーラー定義のプログラミングでは、システムの呼び出し、または関数を使用して、接続されているハードウェアをコントロールします。これらの関数を操作する方法を見つけることによって、インストーラーのプログラミングに関係なくモジュールをコントロールする普遍的な方法を手に入れるでしょう。システムへのルートアクセスがある場合、このタイプのコントロールを獲得する一般的な方法は、関数フックを使用することです。このアプローチの最初の課題は、単にどの関数をフックするかを決定することです。このケースでは、正常に動作している間に膨大な量のシステムのリバースエンジニアリングとデバッグを必要としました。調査する必要がある関数の範囲を狭めるために、バイナリー出力(BO)のコントロールに焦点を絞ることから始めました。最初の課題は、バイナリー出力の状態変更をハンドリングするコードをどのように見つけるかでした。

いくつかの重要な要素が、私たちが正しい方向に向かう助けになりました。まず、コントローラーのドキュメンテーションには、PLCデバイスによくあるコントローラー・エリア・ネットワークバス(CANバス)を介してデバイスがI / Oモジュールと通信することが示されています。前述のとおり、Deltaバイナリーにはすべてシンボルが含まれています。そのため、バイナリーで提供されている関数名を使用して、注目する必要があるコードの表面を減らすことができます。IDAによると、名前の最初の方に「canio」を持つ関数は28個しかないといいます。次に、BOの状態を変更するには物理ハードウェアを呼び出す必要があるため、その変更を行うにはLinuxシステムの呼び出しが必要であると考えられます。デバイスがIOデバイスに変更を加えているため、使用されているLinuxシステム呼び出しは「ioctl」でほぼ間違いありません。「canio」で始まり、「ioctl」を呼び出す関数を相互参照すると、28あった検索スペースは14に減りました。1つの関数名が非常に目立ちました。「canioWriteOutput」です。関数のデコンパイル版を、図29で再現しました。

図29

この仮説を使って、canioWriteOutput内で「ioctl」への呼び出しにブレークポイントを設定し、タッチスクリーンを使用してバイナリー出力の1つの状態を「off」から「on」に変更します。ブレークポイントがヒットしました!一度ブレークポイントをステップオーバーすると、正しいLEDが点灯し、出力がオンになっていることを確認できました。

フックする必要がある関数がわかったので、すぐに次の質問が浮かびます。どのようにしてフックするのか?このタスクを実行するには複数の方法がありますが、最も簡単で最も安定した方法の1つは、LD_PRELOADという環境変数を使用して、起動プロセス時にメインバイナリーがメモリーに読み込むライブラリーを作成することです。プログラムを実行する前に、共有オブジェクトまたはライブラリーへの1つまたは複数のパスがLD_PRELOADに設定されている場合、そのプログラムは他の共有ライブラリーよりも先にそれらのライブラリーをメモリースペースに読み込みます。 Linuxが関数呼び出しを解決するとき、ライブラリーがメモリーに読み込む順番で関数名を探すので、これは重要です。したがって、メインのDeltaバイナリーの関数が名前とシグニチャーを、攻撃者によって生成され最初に読み込まれたライブラリーで定義されたものと共有している場合、攻撃者が定義した関数が代わりに実行されます。攻撃者はデバイスにルートシェルを持っているので、起動時にDeltaソフトウェアが開始する前に、攻撃者が生成したライブラリーへのパスをLD_PRELOAD変数に入力するためにinitスクリプトを修正することができます。そして再起動の際に実行するマルウェアを実質的にインストールします。

プロジェクトの初期段階で作成されたクロスコンパイルのツールチェーンを使用することで、図30に示す「ライブラリー」でこの理論を簡単にテストできました。

図30

上記のコードに何かしら意味があるものはありませんが、この方法をフックしても期待通りに動作するかを確認します。最初に、canioWriteOutput用のIDAで見たのと同じ関数プロトタイプを使用して関数ポインターを定義しました。canioWriteOutputが呼び出されると、まずこの関数が呼び出され、「opt」ディレクトリーに出力ファイルを作成し、テキストを書き込む場所を提供し、フックが機能していることを証明します。次に、シンボルテーブルでオリジナルの「canioWriteObject」を検索し、フックに渡されたものと同じパラメーターを使用してそれを呼び出し、実質的にパススルー関数を作成します。このテストの成功により、この方法が機能することが確認されました。

関数フックが単なるパススルーとして機能する以上のことをするには、どのパラメーターが関数に渡されていて、それらが実行に与える影響を理解する必要がありました。GDBを使用することで、「オン」状態と「オフ」状態の両方で渡されたデータを調べることができます。canioWriteObjectでは、バイナリー出力の状態が、関数に渡される2番目のパラメーターにエンコードされていることがわかりました。そこから、他のパラメーターをそのままにして、2番目のパラメーターとして望ましい状態を実関数に渡すだけで、バイナリー出力の状態を理論的にコントロールできます。ただし実際には、この方法を使用して生成された状態変化が持続したのはほんの一瞬で、その後デバイスは出力をリセットして正しい状態に戻りました。

なぜデバイスは出力を正しい状態に戻したのでしょうか?何らかの種類の保護が実行されているのでしょうか?メインのDeltaバイナリーの文字列とデバイス上のファイルシステムを調査したことで、デバイスソフトウェアがファイルシステム上にデータベースを維持していることがわかりました。再起動の際にデバイスと状態情報を保持するためとみられます。これらのデータベースの少なくとも1つは、おそらく他の種類のI / Oデバイスとともにバイナリー出力の状態を格納するために使用されます。GDBを使用したさらなる調査により、すべてのバイナリー出力の状態をデバイスがこのデータベースに継続的にポーリングして、データベースから取得した状態を公開するためにcanioWriteOutputを呼び出し、それ以前のどんな状態でも上書きしていることがわかりました。同様に、タッチスクリーンを介してユーザーによって行われたこの状態への変更は、この同じデータベースに格納されます。最初は、デバイスへのルートアクセスがあるため、最も簡単な解決策はデータベースの値を変更することだと思われました。ただし、データベースは既知の基準のフォーマットではありません。これは、この形式を置き換え、さらにデータの格納方法を理解する時間が必要なことを意味しています。すでに関数をフックする方法があるので、canioWriteOutputが呼び出されたときに出力をコントロールする方がより簡単です。

これを達成するために、マルウェアを更新し、攻撃者が出力に変更を加えたかどうかを追跡できるようにしました。変更が加えられていた場合、フック関数は、canioWriteOutputの2番目のパラメーターに格納されている正しい状態を、実際のcanioWriteOutput関数を呼び出す前に攻撃者によってアサートされた状態に置き換えます。さもなければ、フック関数は実際の取引のための単純なパススルーとして機能します。攻撃者の観点からのこのプラスの副作用は、マルウェアがそれを修正した後でも、ユーザーが最後に要求した状態として出力がタッチスクリーンに表示されることです。単なる状態追跡の実装によって、攻撃者がアサートした状態が持続しないという以前の問題が解決されました。

バイナリー出力をコントロールすることで、モジュールに接続できる他の種類の入力と出力のそれぞれを調べることにしました。モジュールからデータを読み書きし、それをフックする際に使用される方法を識別するときと同様のアプローチを使用しました。残念ながら、すべての関数がcanioWriteOutputほど単純ではありませんでした。例えば、アナログ出力をコントロールするために使用された関数を入れ替えると、デバイスの状態を含むアナログデバイスに関する様々な情報を保持するために、関数がカスタムデータ構造を利用することがわかりました。その結果、それらの状態を修正する前にアナログ情報がどのように出力に送られているのかを理解するため、まず、これらのデータ構造のレイアウトを入れ替える必要がありました。静的解析と動的解析を組み合わせて使用することで、管理者に接続されているすべてのデバイスの状態をコントロールするために、包括的な悪意のあるライブラリーを作成することができました。


マルウェアを次のレベルに

ルートシェルから変更を加えることで、攻撃者がエクスプロイトされたデバイスをコントロールできることは確かに証明されますが、攻撃者にとってはアクティブシェルに付随しない完全なリモートコントロールを持つことがより実用的かつ現実的です。I / Oモジュールを操作するためにすでに起動時にライブラリーを読み取っていたので、同じライブラリーを使用してコマンド・アンド・コントロールタイプのインフラストラクチャーを作成することも可能だと判断しました。これによって攻撃者は、常時接続やシェルアクセスを維持することなく、単に、リモートから「マルウェア」にコマンドを送信できるようになります。

この概念を実現するためには、バックドアを作成する必要があり、初期化関数はおそらくそれを配置するのに最善の場所でした。ちょっと調べたところ、CANバスを初期化させる関数である「canioInit」を見つけました。 CANバスはデバイスの動作に何らかの変更を加えるために必要とされるので、バックドアが開始される前にこの関数が呼び出されるのを待つのは理にかなっています。前述のいくつかのフックとは異なり、この呼び出しやその戻りデータに変更を加えることはありません。バックドアが適切なタイミングで開始されるようにするための手段としてのみ使用しています。

図31

canioInitが呼び出されると、まず新しいスレッドを生成してから、実際のcanioInit関数を実行します。新しいスレッドはUDPポート1337でソケットを開き、「バイナリ出力0をオンにする」を示す「bo0 on」や、デバイスをユーザーのコントロールに戻す「リセット」などの非常に特殊なコマンドをリッスンします。提供されたコマンドに基づいて、このスレッドで呼び出された「set_io_state」手段は、前のセクションで説明したように、必要なフッキング手段をアクティブにしてI/Oをコントロールします。

図32

Deltaソフトウェアのメモリースペースに完全に機能するバックドアがあるため、私たちは現実的な攻撃の連鎖についてデバイスを完全にコントロールしました。図33に攻撃全体の概要を示します。

図33

悪意のあるパケットの送信からリモートコントロールの取得までの上記のプロセス全体は3分とかからず、最も時間のかかる作業は再起動でした。攻撃者がコントロールを確立すると、ユーザーが提供される情報には影響を与えずにデバイスを操作できるため、攻撃者は検出されないまま、Deltaコントローラーが管理するハードウェアの種類に応じた深刻な被害をもたらすのに十分な機会が与えられます。


現実世界への影響

このような攻撃はどのような影響を与えるのでしょうか?これらのコントローラーは、世界中の複数の業界で導入されています。Shodanを使って、脆弱なファームウェアのバージョンを実行している600近くのインターネットアクセス可能なコントローラーを観察しました。 2019年2月から2019年4月までの間のeBMGRデバイスを追跡したところ、パブリックIPアドレスで利用可能な新しいデバイスが多数存在することがわかりました。

2019年4月上旬の時点で、492個のeBMGRデバイスがShodanを使用したインターネット全体のスキャンで到達可能のままでした。見つけ出されたもののうち一部は明らかにShodanデータ内で見つかった、ユーザーが適用したタグに基づくハニーポットであり、404の潜在的に脆弱な被害者を残しています。同じファームウェアを使用している他のDeltaコントロールデバイスを含め、それらが同じエクスプロイトに対して脆弱である可能性が高いと仮定すると、潜在的なターゲットの総計は1600以上に膨らみます。2019年2月以来、119の新しいインターネット接続eBMGRデバイスを追跡しましたが、その後オフラインになった216台のデバイスがこれらを追い越しました。これは産業用制御システムのシステムアドミニストレータがこれらのデバイスをインターネットに接続するための標準的な方法と、インターネットに接続された脆弱なデバイスのフットプリントを減らすために、積極的に顧客に手を差し伸べているベンダー(Delta Controls)による戦略が一体となった組み合わせだと考えています。ほとんどのコントローラーは北米にあり、米国はオンラインデバイスの53%を占め、カナダは35%を占めています。場合によっては、IPアドレスは、Shodanによるデバイスの地理的位置がISP(インターネット・サービス・プロバイダー)まで遡るため、誤った位置情報をもたらすことがあるということには注意が必要です。

デバイスのアクセシビリティを考えると、他の業界よりも危険にさらされている業界があるようです。業界を特定するためにこれらのデバイスのごく一部しかマッピングできませんでしたが、上位3位のカテゴリーは、教育、電気通信、不動産であることがわかりました。教育には、小学校から大学までのすべてが含まれていました。大学では、複数のキャンパスにまたがる多数の施設で、学区全体にデバイスが導入されることがありました。その一例が、カナダの公立学校システムで、地区内の各校舎にアクセス可能なデバイスがありました。電気通信は、完全にISPや電話会社で構成されていました。これらの多くが、ISPがサービスプロバイダーとしてリストされているためだとみられます。不動産のカテゴリーには、一般的にオフィスビルとマンションが含まれています。検索結果によって入手可能なメタデータから、脆弱な製品を使用している教育、医療、政府、食品、ホスピタリティ、不動産、保育、金融機関の事例も見つけました。

さらに掘り下げると、公開情報を通じて簡単に他のターゲットを見つけることができました。機密文書をオンラインに掲載するのは一般的なやり方ではありませんが、これらのデバイスが会社のビルディング・オートメーション計画の一部として使用されていることを示す多くの文書を確認しました。これは必要なインフラストラクチャー建設の提案書が発行される政府庁舎にとくに当てはまります。詳細な提案、要件、価格設定、エンジニアリング図面、その他予備調査に役立つ情報を含む、全体で約20の文書を集めました。とある政府庁舎には、デバイスの内部ネットワーク設定、制御図面、さらにデバイスの設置場所も含む48ページのマニュアルがありました。

産業用制御システム構築を特定するインターネット上で見つかった編集されたネットワーク図

攻撃者が第三者の空調や暖房をオンやオフに出来たら、どんな重要な問題があるでしょうか?影響を受ける可能性があった業界をいくつか見てみましょう。これらのシステムが誤動作すると、病院、政府機関、電気通信などの業界では、深刻な結果をもたらすおそれがあります。例えば、eBMGRは医療施設や病院で陽圧・陰圧室を維持するために使用され、そこでは加圧のわずかな変化が、空気感染性疾患の拡散によって生命を脅かす危険性があります。その代わりに、データセンターがターゲットになったとします。データセンターは、過熱しないように低温に保つ必要があります。仮に攻撃者が脆弱なコントローラーにアクセスし、危険レベルにまで室温を上げ、アラームを無効にすると、重要なデータが永久に失われる可能性があるのは言うまでもなく、サーバーハードウェアが大量に物理的に損傷し、ダウンタイムコストが発生するかもしれません。Ponemon Institute (https://www.ponemon.org/library/2016-cost-of-data-center-outages)によると、データセンター停止に伴う平均損失コストは2016年に74万357ドルにも達し、さらに上昇しています。マイクロソフトはその代表例です。2018年に同社は冷却故障のために大規模なデータセンターの機能停止(https://devblogs.microsoft.com/devopsservice/?p=17485)となり、約22時間にわたってサービスに影響を及ぼしました。

LEDライトの点滅よりも大きく影響を示すために、McAfee ATRは、稼働しているDeltaシステムで小規模なデータセンターシミュレーションを構築するために、ローカルのDeltaインストーラーを縮小しました。これには暖房と冷房の両方の要素が含まれ、本物のHVACシステムへの攻撃の影響を示すことができます。このデモでは、温度を危険なレベルまで上げ、重大なアラームを無効にし、さらに正常に動作しているとコントローラーに見せかけることで、ターゲットシステムの通常の機能と、エンドツーエンドの完全な攻撃連鎖をお見せします。以下のビデオは、この単なるパッチ未適用の脆弱性が実際のシステムにどのようにして壊滅的な影響を与える危険性があるかを示しています。

また、ヒルズボロの当社のラボにあるこのデモシステムで、有効なパッチ(この場合はDelta Controlsが提供)を使用し脆弱性が即座に軽減されるのを確認できることを強調します。これが今回の調査プロジェクトの最終的な目的です。


結論

CVE-2019-9569のような発見は、あらゆるデバイスにおけるセキュアなコーディング実践の重要性を示しています。Deltaのビルディングマネージャーなどの産業用制御システムデバイスは、きちんとセキュアな状態になっていない場合に企業や人に害を与えてしまう危険性がある重要なシステムをコントロールします。

産業コントロールなどの非標準環境に該当する製品のセキュリティに関連するベストプラクティスと推奨事項がいくつかあります。デバイスの性質に基づけば、ウェブサーバ、エンドポイント、ネットワーキング機器のような基準のインフラストラクチャーと同じ可視性とプロセスコントロールが、それにはないかもしれません。その結果、eBMGR PLCのような産業コントロールハードウェアは、ネットワークやインターネットへの露出、脆弱性評価とパッチ管理、資産インベントリ、さらにはアクセスコントロールや設定レビューなど、さまざまな面から見過ごされるかもしれません。たとえば、最小特権の情報セキュリティーポリシーの原則はおそらく適切で、ネットワークの分離や保護されたネットワークセグメントは、敵対者へのアクセス境界を提供するのに役立つかもしれません。セキュリティ調査への関心と適切なパッチ適用戦略が、既知の脆弱性に対する露出時間を最小限に抑えます。これらの重要な資産を他のインフラストラクチャと同じ精査下に置くために、重要なセキュリティテナントをそれぞれ徹底的にレビューおよび検証することをお勧めします。

McAfee ATRの目標の1つは、今日の複雑で絶えず進化する状況の中で、広範囲の脅威を識別して明らかにすることです。マカフィーの脆弱性公開に関する情報セキュリティーポリシーにより、McAfee ATRはDelta Controlsチームに直接情報を提供して取り組みました。このパートナーシップの結果、ベンダーはこのブログで詳述した脆弱性を効果的に軽減するファームウェアのアップデートをリリースし、最終的にはDelta Controlsの顧客向けに、この攻撃から身を護る方法を提供しました。脆弱なファームウェアバージョン(571848以前のもの)を使用しているあらゆる企業は、自社のパッチポリシーとテスト戦略に沿って、できるだけ早く更新することを強く推奨しました。特に重要なのは、インターネットに直接接続されているシステムです。McAfeeのお客様は、8月6日にリリースされた次のシグネチャで保護されています。McAfee Network Security Platform 0x45d43f00 BACNET:Delta enteliBUS Manager (eBMGR) Remote Code Execution Vulnerability.

ベンダーと研究者という関係の重要な役割を担い、責任ある開示という独自の課題を乗り越える能力を備えたDelta Controlsチームの卓越した努力に注目したいと思います。自社の製品と業界の共通の利益の両方についてセキュリティ研究と情報公開の力を受け入れてきました。Delta Controlsの次の声明をご参照ください。これは、McAfeeとの連携と責任ある開示の力についての洞察を提供します。

 

※本ページの内容は、2019年8月9日(US時間)更新の以下のMcAfee Blogの内容です。