コンピュータウイルスの解析などに欠かせないリバースエンジニアリング技術ですが、何だか難しそうだな、という印象を抱いている人も多いのではないでしょうか。この連載では、「シェルコード」を例に、実践形式でその基礎を紹介していきます。(編集部)
「なぜ、ソースコードがないのに脆弱性を見つけられるの?」「なぜ、コンピュータウイルスの詳細な動作が分かるの?」
読者の皆様は、日々公開されるゼロデイ攻撃や巷をにぎわせるコンピュータウイルスの解析結果を見て、このような疑問を持ったことはないでしょうか?
これらの多くは「リバースエンジニアリング」という技術を基に行われています。
【関連記事】
用語辞典:リバースエンジニアリング
http://www.atmarkit.co.jp/aig/02security/revengine.html
――ハードウェアやソフトウェア製品に関して、構造や仕様を分析して技術的情報をほぼすべて明らかにしてしまう技術、またはその行為のことをリバースエンジニアリングという。
リバースエンジニアリング技術とはソフトウェアの“解析”技術です。ここではソースコードのないバイナリコードに対して逆アセンブル、逆コンパイルを行い、人が読めるコードにし、それを基にバイナリコードの動作を解析していく技術を意味します。
攻撃者はこのリバースエンジニアリング技術を駆使して、クローズドソースのアプリケーションを解析し、攻撃に利用可能な脆弱性を見つけ出します。一方、防御側はこのリバースエンジニアリング技術を、コンピュータウイルスや攻撃コードの解析に利用し、攻撃防御・検知にと利用しています。
さて、このリバースエンジニアリング技術ですが、一昔前はプログラムを解析して、ソフトウェアプロテクションを破るため、不正利用するために利用されているといった「悪い」イメージがあり、敬遠されていました。
しかし現在、このリバースエンジニアリングなくして日々高度化する攻撃者とやり合っていくことはできません。われわれもこの技術を身に付け、駆使し、攻撃者の一歩先を行くセキュリティ技術を確立していく必要があるのではないでしょうか。
本連載では攻撃コードの解析を通じて、リバースエンジニアリング技術がどのようなものであるかを紹介します。
リバースエンジニアリング技術は一般的に「難しい」技術だと思われていることが多いと思います。これは、「大量のわけの分からないアセンブラコードを読まなければいけない」といったことが1つの要因になっているかと思います。
そこで本連載では、攻撃コードのうち「シェルコード」と呼ばれる部分を解析対象として取り上げます。
通常シェルコードはマルウェアよりもはるかにサイズが小さく、200ステップから大きなものでもだいたい500ステップ程度で書かれています。これくらいのサイズであれば終わりも見えるので、心が折れる前に全ステップを読みこなすことができます。そのため最初のやる気をそのまま持ち続けた状態で解析しきることができるので、達成感を味わいつつ進められるのではと考えました。
また本連載ではフリーで手に入るツールや検体を例にして解説を行っていきます。読者の皆様も頭で理解するだけではなく、ぜひ、ご自身の手を動かして読み進めていただければと思います。
現役のセキュリティエンジニアの方、これからリバースエンジニアリング技術を身に付けてみたい方、攻撃者の技術がどんなものなのかを知っておきたい方などを対象に、できるだけ分かりやすく解説していきたいと思います。
シェルコードの解析に入る前に、脆弱性を狙った攻撃によってマルウェアに感染するメカニズムの解説を行いたいと思います。シェルコードがどの段階で、どのような目的に利用されるものなのかをしっかりと把握しておくことで、解析をする際の手助けになると思います。
ここでは、メモリ破壊系の脆弱性の1つであるバッファオーバーフローの脆弱性を持つサーバをターゲットとしてリモートから攻撃し、マルウェアに感染させる攻撃を例に解説したいと思います。
通常、この攻撃は次の4段階で行われ、ターゲットをマルウェアに感染させています(図1)。
(1)脆弱性を悪用するためのデータを送り込む
(2)ポインタ(注1)を書き換え、シェルコードを実行させる
(3)マルウェア本体をダウンロードさせる
(4)(3)でダウンロードしたマルウェア本体を実行させる
まず攻撃の第一段階として、ターゲットの環境上で脆弱性を発動させる必要があります。バッファオーバーフローの脆弱性の場合、バッファを溢れさせてポインタを書き換える行為がこれに当たります。
具体的には、脆弱性を含むプログラム個所まで処理を進めさせ、脆弱性の原因となっているバッファを処理するコード部分に、このバッファを溢れさせるためのデータを渡します。これにより、そのバッファが溢れ、近辺にあるポインタを書き換えてしまいます(図1-(1))。
このプロセスを落としたい(サービス妨害が目的)だけの場合、ここでポインタの値を無効な値に書き換えてしまえば(例えば0x00000000など)、後ほどこのプロセスが書き換えられたポインタを利用する際にページフォルトなどが発生し、プロセスが異常終了します。
しかし、マルウェアに感染させたい場合はもう少し工夫が必要になります。書き換えるポインタの値を調整し、シェルコードを実行させるように仕向けます。シェルコード自体は、バッファを溢れさせる際に書き込むデータに一緒に入れて送り付けたり、あらかじめ他の処理でヒープ領域に書き込ませたりして、ターゲットのプロセス空間に配置させておきます(図2)。この状態で、この書き換えたポインタを元にコードが呼び出された場合、元のコードではなく、シェルコードが実行されます(図1-(2))。
このシェルコードが実行されると、実行形式のマルウェアをダウンロードし、ファイルとしてターゲットのファイルシステム上に保存します(図1-(3))。さらにシェルコードがダウンロードしたファイルを実行することで、ターゲットのマシン上でマルウェアが動き出します(図1-(4))。この時点でこのターゲットマシンはいわゆる“マルウェアに感染した”状態になります。この「ファイルをダウンロードしてくる」「ダウンロードしたファイルを実行する」という2点がシェルコードの行うべき役割になります(注2)。
【関連記事】
5分で絶対に分かるバッファオーバーフロー
http://www.atmarkit.co.jp/fsecurity/special/110buffer/buffer00.html
――皆さんがよく利用しているアプリケーションにセキュリティホールが見つかり、「悪意のあるコードが実行される可能性がある」というような内容のニュースをよく耳にします。
Copyright © ITmedia, Inc. All Rights Reserved.