隠れた脆弱性の発見
サプライ・チェーンへの攻撃は、セキュリティの分野でも増加の一途をたどっている。米国では、バイデン大統領の大統領令(Executive Order 14208)において、特にソフトウェアのサプライチェーンセキュリティに関する指針を示すセクションが設けられている。もし、あなたのソリューションが、ファームウェアのような基本的なものであっても、他のベンダ ーから提供されたコードに依存しているのであれば、そのコードが脆弱性について徹底的にテストされ ていることを確認することが重要である。これは、OT がサイバー犯罪者にとってより大きな関心領域となるにつれて、特に重要である。シュナイダー社が最近公表したような脆弱性は、攻撃者がネットワークへの足がかりを得るだけでなく、物理的な損害を引き起こすことを可能にする可能性がある。
ベンダは、コードの機密性を保護するために、ますます高度な難読化技術や暗号化技術を活用 しているため、脆弱性を発見することは、特に困難である。もう 1 つの困難は、ファームウェアそのものが、逆コンパイルすることが困難になっていることである。シュナイダーエレクトリックの APC PDU のファームウェアは、そのようなコードの一例である。 それは何年も前から存在し、Intel 80286(80年代のチップセット)の古く時代遅れのバージョン用にコンパイルされているため、簡単に読んだり検査したりすることができない。
このブログでは、私たちがシュナイダーエレクトリックの APC PDU のファームウェアをどのように逆アセンブルしたかを示し、デバイスの脆弱性を分析する際にこの問題に遭遇する可能性のある他の人たちに洞察を提供する。
ファームウェア実行ファイルからターゲットファイルを抽出する
この実行ファイルを実行すると、これは実行ファイルというよりもアーカイブファイルであることがわかります。実際、8 つのファイルが抽出され、そのうちの 3 つが注目すべきファイルである:
apc_hw05_aos_682.bin
apc_hw05_bootmon_109.bin
apc_hw05_rpdu2g_680.bin
これらはそれぞれ、APCオペレーティングシステム(AOS)、ブートローダ、ネットワーク管理カードのファームウェアを表している。
AOSバイナリーの解析を開始したが、IDAにロードされると、最初の問題にぶつかる。デバイスケースを開いても、プロセッサーモデルを特定することができなかった。インターネット・フォーラムで調べた結果、PDUはインテル16ビット・プロセッサを採用するために使用されていることがわかりました。別のターゲットを試してみたところ、汎用のx86 16ビット・プロテクト・モード・プロセッサーが最もコードの可読性が高いことがわかりました。この時点で、文字列と多くのコードを見つけることができましたが、逆アセンブラはクロスリファレンスを見つけることができませんでした。
After a further exhaustive search on the internet, we were able to find an article from JSOF that addressed our problem. From that article we learned that the processor is a Turbo186: that CPU runs in extended mode, using 24-bits addressing capability, so the target address of a far call can be computed as (segment_base << 8) + offset.
JSOFの記事で見つけたもう一つの重要な情報は、APCファームウェアの各モジュールのヘッダー構造である。図 1 は、apc_hw05_aos_682.bin ファイルのヘッダーを示している。この図では、いくつかのフィールドが強調表示されている。特に注目すべきは、各ファームウェア・モジュールの開始アドレスを示す「Image base」フィールドである。
図1から、AOSモジュールの開始アドレスが0xC00400であることがわかります。他のファイルを調べれば、rpdu2g (0x900400)とbootmon (0x0C0000)モジュールの開始アドレスがわかります。
この時点で、ファイルヘッダから取得したモジュールを開始アドレスとして指定して、3つのモジュールをIDAにロードすることができる。プロセッサーには、インテル80286プロテクトを選択する必要がある。
バイナリがIDAにインポートされると、文字列やコードへの参照がほとんど生成されないことに注意してください。これはTurbo186アセンブリコードの構造によるものです。実際、Turbo186アセンブリ・コードはブロックで構成されており、命令はblock_base_address + offsetで定義されるパターンでコード・アドレスを参照します。
プログラムを適切に書き換えるには、各コードブロックの開始アドレスを含むテーブルを見つける必要があるが、残念ながら、コード中のその表現はスクランブルされているようで、読むことができない。したがって、手作業で再構築する必要がある。
First, we need to create the strings and convert the firmware bytes to code, whenever it is possible. Then, we leverage the structure of the far calls: Figure 2 shows an example of a far call, where the segment base address is (0x0C004 << 8) and the offset is 0x54C. To find all the segment base addresses we use the “Search for text” command in IDA, and we search the string “call far ptr”. In this way, we get all the far call instructions defined in the code and, with a python parser, we created an ordered list of all the referenced block base addresses. At this point, we wrote an IDA/python script that creates a new IDA segment for each entry of the list. Figure 3 shows a small portion of all the segment we found in the APC firmware.
ピースをまとめるコードとデータの相互参照を再現する
We are now ready to create data and code cross-references; as for the code references, we have already explained how far calls are structured so, once we have set the segments table, we can iterate through the code. For each far call, a new code cross reference (CREF) is created, with the usual target address (segment_base_address << 8) + offset. For better code readability, we can also add a comment near the caller address, specifying the callee address. IDA will automatically reference to it and show a preview of the target code as soon as you hover over the comment with the mouse pointer. Figure 4 shows an example of an address expansion with the corresponding comment.
すべてのコード参照を再作成したら、データ参照に移ることができる。最初の命令は cs レジスタをスタックにプッシュし、2 番目の命令はこれらのプッシュ命令が定義されているセグメントのベースアドレスに対するパラメータアドレスのオフセットをプッシュします。segment_start_address + offset の演算結果が文字列のアドレスになる場合(最初のステップとして、文字列を作成する)、対応する DREF を作成することができる。図5aとbに例を示す。
図5aでは、アドレス0xC0A823に「push cs」があり、それに続く命令は「push 0x92Eh」である。プッシュ命令の即値にセグメントの開始アドレスを追加すると、0xC0A42Eが得られ、これは0xC0A82Aで呼び出される関数の引数として使用される文字列のアドレスである(図5b)。
この作業の最後のステップは、関数の作成である。そのためには、先に調べたすべてのファーコールを記録しておき、そのそれぞれについて、ターゲットアドレスの最初の命令を検討すればよい。これらの命令が関数のプロローグを表すことができれば、関数を作成することができる。さらに、関数のアドレス範囲を適切に設定するために、関数のエピローグを特定する必要があります。
概要
この投稿では、Schneider Electric APC PDU デバイスのファームウェアを解析するために使用された、特定の方法論について概説した。しかし、このアプローチは、この種のプロセッサ用にコンパイルされた他のコードを検査する方法のフ レームワークとして使用することができる。OT/IoT デバイスに対する攻撃の数が増え続けているのを見るにつけ、古いハードウェ ア上でコンパイルされたコード、あるいは開発者によって難読化されたコードを解析する方法を理解するこ とが、ますます重要になってきている。