検索
連載

動的メモリ管理に関する脆弱性(その2)もいちど知りたい、セキュアコーディングの基本(7)(2/2 ページ)

今回は動的メモリ管理に関する脆弱性の続きです。unlinkテクニックと呼ばれる古典的な攻撃手法を紹介します。

PC用表示 関連情報
Share
Tweet
LINE
Hatena
前のページへ |       

バッファーオーバーフローによるデータ構造の改変とリンク書き換え操作の悪用

 空きメモリのブロックが2個、メモリ上に並んでいるとしましょう。この2個のブロックは空きメモリリスト上では隣合わせのブロックかもしれませんし、全く離れた位置にあるものかもしれません。リスト上の位置関係はどうでもよくて、重要なのは、アドレス空間上で隣接した位置にあり、バッファーオーバーフローが発生した場合にその影響を受けるということです。


図7 隣接した2個のブロック

 まずblock1がmalloc()で割り当てられたとします。メモリ管理ライブラリの中ではunlink(block1)が実行され、 block1->spaceがmalloc()の返り値となります。これによりプログラムはblock1への読み書きを行うようになるわけですが、ここでバッファーオーバーフローが発生したとしましょう。具体的には、malloc()で確保したサイズを超えるデータが(空きメモリ部分)に書き込まれてしまったとします。

 すると、この状況ではblock2まではみ出してデータを書き込むことになり、block2のsize、next、prevといった管理用のデータが上書きされることになるでしょう。


図8 バッファーオーバーフローによる管理情報の上書き

 プログラムの実行が進んで次のmalloc()でblock2が割り当てられたとします。するとunlink(block2)が実行されますが、このときすでにblock2の領域はバッファーオーバーフローでデータが改変されてしまっています。unlink()は上書きされた値を使った操作をしてしまうわけです。unlink()の実行内容は

  1. node n3 = block2->next;
  2. node n1 = block2->prev;
  3. n3->prev = n1;
  4. n1->next = n3;

 となり、n1、n3どちらもblock2の領域にあるポインター値を使っていることが分かります。

 では、block2をどのようなデータで上書きするのでしょうか? 例えばこのようにします。

  • リンク書き換え操作で書き換えたいアドレス情報がprevポインターとして見えるように(仮想的な)ブロックのアドレス位置を決める
  • (仮想的な)ブロックのアドレス位置をblock2のnextポインターに書き込む
  • リンク書き換え操作で書き込ませたい値をblock2のprevポインターに書き込む

 ここで、書き換えたい場所として(攻撃者にとって)魅力的なのは、ライブラリ関数のエントリポイントを指す関数ポインターです。

 多くのプログラムはライブラリ関数とリンクされて実行されますが、プログラム実行時には、プログラムにリンクされているライブラリ関数の先頭アドレス一覧が、メモリ上の一定の場所に用意されます。関数呼び出しを行うと、この一覧から先頭アドレスを拾い出して関数本体に実行を移すようになっているわけです。

 そこで、特定のライブラリ関数のエントリポイントをあらかじめ用意したシェルコードの先頭アドレスに書き換えておくと、そのライブラリ関数の呼び出しは、実際にはシェルコードを実行することになります。

 この手法を使ってヒープバッファーオーバーフローの脆弱性を悪用した攻撃が行われる場合、次のような流れになるでしょう。

1.実行させたいコードを入力データなどの形で与えてメモリ上に置かせる

2.次にヒープバッファーオーバーフローを起こすような入力データを与えてヒープメモリ上の管理情報を上書きする

3.管理情報が上書きされた状態でさらにヒープメモリのアロケーションや解放が行われ、ライブラリ関数のエントリポイントが上書きされる

4.ライブラリ関数の呼び出しによって、攻撃者が用意したコードが実行される

実例を見る

 ヒープバッファーオーバーフローの実例として有名なのが、WindowsのJPEGファイルの取り扱いに関する脆弱性(CVE-2004-0200、MS04-028)です。この脆弱性に対する攻撃がどのように行われるかを、順を追って説明しましょう。

(1)数値演算の不具合

(2)演算結果を使って不適切な大きさのメモリ割り当て

(3)割り当てたメモリ領域へのデータコピーによってバッファーオーバーフローが発生

(4)ライブラリ関数の呼び出しがシェルコード呼び出しに化ける

(1)数値演算の不具合

 JPEGファイルには、画像データとともにさまざまなメタ情報を付加できるように形式が定められています。メタ情報の1つがコメント情報で、JPEGファイル中では、コメント情報の始まりを示すデータ(0xFFFE)のあと、コメント情報の長さを表す2バイトのフィールドに続き、コメント情報自体を表す文字列が置かれます。


図9 JPEGのコメント情報

 長さフィールドの値は、それ自身のバイト数も含めた値と規定されているので、2以上の値が入るべきです。もし0や1の値だった場合は、不正な値としてエラー処理すべきでしょう。しかしこの問題が発見された当時、Windowsのコードではこの値が2以上であることを確認せずに使っていました。

(2)演算結果を使って不適切な大きさのメモリ割り当て

 JPEGデータからコメント文字列を取り出すためには、次のような処理が行われます。

  1. 2バイトの長さフィールドを取り出し、その値lengthを得る
  2. (length - 2)を計算し、実際のコメント文字列の長さを得る
  3. malloc(length - 2 + 1)してコピー先メモリ領域を確保する(+1はnull終端文字を考慮したもの)
  4. 確保した領域にコメント文字列(length - 2)バイトをコピーし、末尾にnull終端文字を置き、Cの文字列関連の標準ライブラリ関数で扱えるような形にする

 ここで、コメント文字列の長さフィールドに1が入っていたらどうなるでしょうか。length - 2 + 1 == 0となり、malloc()には0が渡されます。Windowsのmalloc()関数では、ヒープ上に長さ0の領域を確保し、それへのポインターを返します。

(3)割り当てたメモリ領域へのデータコピーによってバッファーオーバーフローが発生

 ヒープメモリ上に確保した領域に、コメント文字列をmemcpy()でコピーします。上記のように長さフィールドの値が1だった場合、コピーする長さは(length - 2)== -1ですが、memcpy()関数では長さを表す引数を符号なし整数(size_t型)と解釈するので、-1は実際にはUINT_MAXとかULONG_MAXといった値として解釈されてしまいます。

void *memcpy(void *dest, const void *src, size_t count);

 本来、malloc(0)で確保された領域にはデータを書き込んではいけないはずなのですが、ここにコピーを行うことでヒープメモリの管理データが上書きされます。この上書き操作を見越して、あらかじめJPEGファイルに都合のよいデータを入れておきます。また、コピーされるデータの中には実行させたいシェルコードも仕込んでおき、一緒にコピーさせます。

(4)ライブラリ関数のエントリポイントが改ざんされ、ライブラリ関数の呼び出しがシェルコード呼び出しに化ける

 プログラムは正しくコメント文字列をコピーしたつもりです。そして実行が進み、malloc()やfree()が繰り返されます。その中で空きメモリリストの操作が行われ、ライブラリ関数のエントリポイントが上書きされてしまいます。

 さらにプログラムの実行が進み、問題のライブラリ関数を呼び出そうとします。しかしライブラリ関数のエントリポイントは書き換えられてしまっているため、実際には先ほどヒープメモリ上にコピーされていたシェルコードが実行されることになります。

メモリ管理の脆弱性に対する攻撃手法の発展を促したunlinkテクニック

 今回説明したunlinkテクニックを扱った最初の文献は、著名なセキュリティ研究者Solar Designer氏が公開した“JPEG COM Marker Processing Vulnerability”という論文だったそうです。その中では、Linuxのメモリ管理の仕組みを解析し、それを悪用する手法について解説しています。

 今回の解説では、細かい部分を省略して分かりやすい説明を試みましたがいかがでしたか?

 unlinkテクニックが生み出されたのは、ちょうどWindows XPが提供され始めたころです。ヒープメモリのバッファーオーバーフローに対する攻撃手法がいろいろと考案される中、Windows XPは格好の実験台となりました。

 これに対して Microsoftでは、それらの手法を無効化するための仕組みを組み込んでいきました。冒頭で紹介したFFRIのレポート「Heap Exploitのこれまでと現状」を見ると、この攻防の様子が感じられるのではないでしょうか。この内容をもっと詳しく理解したい、という熱心な方には、下記の参考文献に挙げているPhrack Magazineの記事や、Microsoftのブログ記事をお勧めします。

参考文献

[1] FFRI Monthly Report

Heap Exploit のこれまでと現状

[2] Openwall articles

JPEG COM Marker Processing Vulnerability

[3] Phrack Magazine Volume 0x0d, issue 0x42, Phile #0x0A of 0x11

MALLOC DES-MALEFICARUM

[4] TechNet Blogs - Security Research & Defense

Preventing the exploitation of user mode heap corruption vulnerabilities

[5] TechNet Blogs - Security Research & Defense

Software Defense: mitigating heap corruption vulnerabilities


Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る