2 か月前のブログでは、当初 W32.Stuxnet は石油や天然ガスなどのパイプラインや原子力発電所などで使用されている産業用制御システム(ICS)を標的としていると述べました。そして、その後の記事で技術的な詳細についても少し触れています。
技術的なさらに詳しい説明については、9 月 29 日の Virus Bulletin Conference で発表される報告書にすべて含める予定ですが、ここ数日Stuxnet がシステムに感染して検知を回避するプロセスに大きな関心が集まっています。
Stuxnet は特定の ICS を標的としているので、テストシステムでの動作確認では、判断を誤る恐れがあります。これは最も注目すべき動作特性の大部分がまったく生じないことが考えられるためです。実行されてすぐに気付く動作の 1 つとして、Stuxnet はプログラマブルロジックコントローラ(PLC)のデータブロック DB890 にアクセスしようとすることが挙げられます。しかし、このデータブロックは実際には Stuxnet 自身により追加されたもので、標的となるシステムに最初からあったわけではありません。Stuxnet は、このブロックの監視と書き込みを行い、状況に応じて PLC プログラムフローを変更します。
今回のブログでは、PLC の感染とルートキット機能の詳細について説明します。特に、標的となった ICS における Stuxnet 攻撃を、以下の重要な側面から解説します。
- Stuxnet が標的とする産業用制御システムを選ぶ方法
- PLC コードブロックの感染に使用される方法
- 感染時に PLC に配置される実際のコード
- 感染した Windows コンピュータに存在する PLC ルートキットコード
これら 4 つの各タスクを実行するコードはかなり異なっているため、それぞれ個別に対応する必要があります。
Stuxnet の目標は、PLC を改変することで、産業用制御システムの動作を変えることです。これは、PLC に送信される読み取り/書き込み要求を傍受して、そのシステムが目的の標的であるかどうかを判断し、既存の PCL コードブロックを改変して新しいブロックを PLC に書き込んでから、最終的にルートキット機能を使用して PLC オペレータや PLC プログラマから PLC の感染を隠蔽することによって行います。タスクはそれぞれ異なります。たとえば、感染したコードブロックを隠蔽するタスクは、標準的な C/C++ コードを使用して、感染した Windows コンピュータで実行されるのに対して、Stuxnet が産業用制御システムで実行することを目的としている悪質なコードは PLC で実行され、MC7 バイトコードで記述されています。MC7 は、PLC で実行されるアセンブリ言語であり、本来、STL で記述されることが多いものです。
PLC を攻撃する Stuxnet のテクニックについて説明する前に、まず PCL のアクセス方法とプログラミング方法の基本を見てみましょう。
PLC にアクセスするには、特定のソフトウェアをインストールする必要があります。Stuxnet は、具体的には PLC の特定のモデルのプログラミングに使用される WinCC/Step 7 ソフトウェアを標的としています。このソフトウェアがインストールされていると、プログラマはデータケーブルを介して PLC に接続し、メモリの内容にアクセスしたり、PLC を再構成したり、PLC にプログラムをダウンロードしたり、以前にロードされたコードをデバッグしたりできます。PLC の構成とプログラミングが終わると、Windows コンピュータを切断しても PLC が単独で機能するようになります。これが実際にどのようなものかをイメージできるように、ラボにおける基本的なテスト装置の写真を次に示します。
次のスクリーンショットは、Step7 STL エディタにおける Stuxnet の悪質なコードの一部を示しています。Stuxnet のファンクションコード(FC)ブロックの 1 つについて、MC7 コードの先頭部分が表示されています。表示されているコードは、逆アセンブルされたブロック FC1873 のものです。
Step 7 ソフトウェアは、s7otbxdx.dll というライブラリファイルを使用して、PLC との実際の通信を行います。Step7 プログラムは、PLC にアクセスするときに、この DLL でさまざまなルーチンを呼び出します。たとえば、Step 7 を使用してコードのブロックを PLC から読み取る場合は、ルーチン s7blk_read が呼び出されます。s7otbxdx.dll のコードは、次の図に示すように PLC にアクセスしてコードを読み取り、それを Step7 プログラムに戻します。
それでは、Stuxnet がインストールされたときに、PLC へのアクセスがどのように機能するのかを見てみましょう。Stuxnet は、実行されると元の s7otbxdx.dll ファイルを s7otbxsx.dll という名前に変更します。次に、元の DLL を独自のバージョンに置き換えます。これで、Stuxnet はソフトウェアパッケージから PLC にアクセスするために生成されるすべての呼び出しを傍受できます。
Stuxnet の変更された s7otbxdx.dll ファイルには、元の DLL のエクスポートとして考えられる内容がすべて(最大 109 個)含まれています。これにより、同じ要求をすべて処理することが可能になります。これらのエクスポートの大部分は、s7otbxsx.dll という名前に変更された実際の DLL に転送されるだけであり、面倒なことは何も起きません。実際、元の 109 個のエクスポートのうち 93 個は問題ありません。しかし、残りの 16 個のエクスポートが問題です。これらのエクスポートは、転送されるだけでなくカスタム DLL により傍受されます。この傍受されるエクスポートは、PLC でコードブロックを読み取り、書き込み、そして特定するためのルーチンです。Stuxnet は、これらの要求を傍受することで、PLC のオペレータにまったく気付かれずに、PLC との間でやり取りされるデータを変更できるのです。このとき、PLC 上の悪質なコードを隠蔽することができるルーチンも使用されます。
Stuxnet が PLC にアクセスして PLC に感染する方法について説明するため、まずは存在するデータの種類について見てみましょう。PLC は、オペレータにより PLC にロードされたコードとデータのブロックを処理します。わかりやすくするために、最も一般的なブロックの種類とその役割について簡単に説明します。
- データブロック(DB)には、数値、構造など、プログラム固有のデータが含まれています。
- システムデータブロック(SDB)には、PLC の構成に関する情報が含まれています。このブロックは、PLC に接続されているハードウェアモジュールの数や種類に応じて作成されます。
- 組織ブロック(OB)は、プログラムのエントリポイントです。CPU により周期的に実行されます。Stuxnet に関しては、次の 2 つの OB が重要な役割を果たします。
- OB1 は、PLC プログラムのエントリポイントです。周期的に実行されますが、時間的な要件はありません。
- OB35 は、標準的な watchdog 組織ブロックであり、システムにより 100 ミリ秒ごとに実行されます。このファンクションには、時間重視の方式により、すぐに応答したり、ファンクションを実行したりするために、危険な入力の監視に必要なロジックが含まれていることがあります。
- ファンクションブロック(FC)は、標準的なコードブロックです。PLC により実行されるコードが含まれています。通常、OB1 ブロックは少なくとも 1 つの FC ブロックを参照します。
以降のセクションでは、これまで述べてきた脅威の 4 つの主要な側面について詳細に説明します。
1. 感染させる PLC の決定
Stuxnet は、標的となるシステムの特性に応じて異なるコードを使用して PLC に感染します。
感染シーケンスは、PLC の動作を変えるために PLC に挿入される PLC ブロック(コードブロックとデータブロック)で構成されます。この脅威には、3 つの感染シーケンスが含まれています。そのうち 2 つのシーケンスは非常によく似ており、機能的には同等です。シマンテックでは、これらの 2 つのシーケンスに A および B という名前を付けています。また、3 つ目のシーケンスはシーケンス C と呼んでいます。Stuxnet は、システムのフィンガープリントを取得することでそのシステムが目的の標的であるかどうかを判断します。具体的には次の点を確認します。
- PLC の種類/ファミリ: CPU が 6ES7-417 および 6ES7-315-2 の PLC のみ感染します。
- システムデータブロック: SDB が解析され、含まれている値に応じて、感染 A または B の方法で、あるいはどの方法も使用せずに感染プロセスが開始されます。SDB の解析時、コードは 2 つの値(7050h および 9500h)が存在するかどうかを検索し、それらの各値の発生回数に応じてシーケンス A または B を使用して PLC に感染します。
コードは、SDB ブロックのオフセット 50h で 2C CB 00 01 というバイトも検索します。これは、CP 342-5 通信プロセッサ(Profibus-DP に使用される)が使われていると見つかります。これらのバイトが見つからなければ、感染は行われません。
シーケンス C の感染条件は、他の要因によって決まります。
2. 感染方法
Stuxnet は、コードを先頭に付加する感染テクニックを使用します。Stuxnet が OB1 に感染すると、次の処理シーケンスを実行します。
- 元のブロックのサイズを大きくする
- ブロックの先頭に悪質なコードを記述する
- 悪質なコードの後に元の OB1 コードを挿入する
Stuxnet は、OB1 に感染するだけでなく、OB35 にも同様の方法で感染します。また、標準コプロセッサの DP_RECV コードブロックも独自のものに置き換えることで、Profibus(分散 I/O に使用される標準的な産業用ネットワークバス)でのネットワーク通信をフックします。
方法 A および B の感染プロセスの概要は、次のとおりです。
- PLC の種類を確認します。S7/315-2 が対象となります。
- SDB ブロックを確認し、シーケンス A または B のどちらに書き込むかを判断します。
- DP_RECV を見つけて FC1869 にコピーし、Stuxnet に埋め込まれた悪質なコピーで置き換えます。
- Stuxnet に埋め込まれた、シーケンスの悪質なブロック(合計 20 のブロック)を書き込みます。
- OB1 に感染して、悪質なコードがサイクルの最初に実行されるようにします。
- watchdog として機能する OB35 に感染します。
3. 感染コード
OB1 ファンクションに挿入されたコードには、感染シーケンス A および B を開始する役割があります。これらのシーケンスには、次のブロックが含まれています。
- コードブロック: FC1865 ~ FC1874、FC1876 ~ FC1880
(FC1869 は Stuxnet 内には含まれておらず、PLC にあった元の DP_RECV ブロックのコピーである点に注意)
- データブロック: DB888 ~ DB891
シーケンス A および B は、DP_RECV フックブロックを使用することにより Profibus でパケットを傍受します。これらのブロックで見つかった値をベースにして、その他のパケットが生成され、ケーブル上に送信されます。これは、複雑なステートマシン(前述の各種 FC ブロックに実装)により制御されます。このマシンは、データブロック DB890 を介して DLL により部分的に制御可能です。
特定の状況下では、シーケンス C が PLC に書き込まれます。このシーケンスには、次のように A および B より多くのブロックが含まれています。
- FC6055 ~ FC6084
- DB8062、DB8063
- DB8061、DB8064 ~ DB8070(オンザフライで生成)
シーケンス C は、PLC のメモリマッピングされた I/O 領域と周辺機器の I/O に対して、I/O 情報(入出力)を読み書きするようになっています。
プログラム A および B の制御フローを次に示します。この制御フローは、前に示した Step7 エディタのスクリーンショット(コードブロック FC1873)にも一部表示されています。
次の図からわかるように、コードシーケンス C のプログラムフローはさらに複雑です。
4. ルートキット
Stuxnet PLC ルートキットコードは、偽の s7otbxdx.dll にすべて含まれています。検知されないまま PLC に存在し続けるという目的を達成するために、少なくとも次の状況を排除する必要があります。
- 独自の悪質なコードブロックに対する読み取り要求
- 感染したブロック(OB1、OB35、DP_RECV)に対する読み取り要求
- 独自のコードを上書きする可能性がある書き込み要求
Stuxnet には、こうした要求を監視および傍受するコードが含まれています。この脅威は、Stuxnet の PLC コードが検出されたり、破壊されたりしないようにするため、こうした要求を改変してしまいます。次に、Stuxnet がフックされたエクスポートを使用してこうした状況を処理する方法の例を列挙します。
- s7blk_read: 読み取り要求を監視して、Stuxnet は次のものを返します。
- 実際の DP_RECV(FV1869 として保持)を要求。
- 要求が独自の悪質なコードに対するものである場合はエラー。
- OB1 または OB35 のクリーンなバージョン(オンザフライで感染を解除している)。
- s7blk_write: OB1/OB35 への書き込み要求を監視して、これらの新しいバージョンが確実に感染するようにします。
- s7blk_findfirst/s7blk_findnext: これらのルーチンは、PLC 上のブロックの列挙に使用されます。悪質なブロックは、自主的に「スキップ」されます。
- s7blk_delete: ブロックの削除も監視されます。
このようにして、Stuxnet は PLC で確実に存在し続けることができるわけです。
前述のとおり、Stuxnet は複雑な構造の脅威であり、PLC の感染コードもその複雑さの一因です。シマンテックでは数か月前に、挿入された MC7 コードをリバースエンジニアリングしましたが、このコード自体について説明するだけでも、たくさんのブログが書けるほどです。PLC 感染ルーチンだけでなく、脅威全体についての詳細をご覧になりたい場合は、Virus Bulletin Conference で近々公開される報告書をお読みください。