House of Orange
基本情報
コード
ゴール
malloc_printerr
関数を悪用する
必要条件
トップチャンクのサイズを上書きする
Libcとヒープのリーク
背景
この例のコメントから必要な背景情報:
古いバージョンのlibcでは、malloc_printerr
関数が呼び出されると、_IO_list_all
に格納されている _IO_FILE
構造体のリストを反復処理し、実際にその構造体内の命令ポインタを実行していました。
この攻撃では、_IO_list_all
に書き込む偽の _IO_FILE
構造体を作成し、malloc_printerr
を実行させます。
その後、_IO_FILE
構造体のジャンプテーブルに格納されているアドレスを実行し、コード実行を行います
攻撃
攻撃は、アンソートされたビン内のトップチャンクを取得することから始まります。これは、malloc
を現在のトップチャンクサイズよりも大きいサイズで呼び出し、かつ mmp_.mmap_threshold
(デフォルトは128K)より小さいサイズで呼び出すことで達成されます。トップチャンクサイズが変更されるたびに、トップチャンク + サイズ がページアラインされており、かつ prev_inuse ビットが常に設定されていることが重要です。
アンソートされたビン内にトップチャンクを取得するには、トップチャンクを作成するためにチャンクを割り当て、割り当てられたチャンク内でオーバーフローを起こしてトップチャンクサイズを変更し、トップチャンク + サイズ がページアラインされ、かつ prev_inuse ビットが設定されていることを確認する必要があります。その後、新しいトップチャンクサイズよりも大きいチャンクを割り当てます。トップチャンクをアンソートされたビンに入れるために free
が呼び出されないことに注意してください。
古いトップチャンクは今やアンソートされたビンにあります。それを読み取ることができる場合(オーバーフローを引き起こす脆弱性による可能性があります)、そこからlibcアドレスをリークし、_IO_list_all のアドレスを取得することができます。
アンソートされたビン攻撃は、オーバーフローを悪用して topChunk->bk->fwd = _IO_list_all - 0x10
と書き込むことで実行されます。新しいチャンクが割り当てられると、古いトップチャンクが分割され、アンソートされたビンへのポインタが _IO_list_all
に書き込まれます。
次のステップは、古いトップチャンクのサイズを小さなビンに収まるように縮小することです。具体的には、そのサイズを 0x61 に設定します。これには2つの目的があります:
Small Bin 4への挿入:
malloc
がアンソートされたビンをスキャンし、このチャンクを見つけると、その小さなサイズのためにそれをSmall Bin 4に挿入しようとします。これにより、チャンクはSmall Bin 4リストの先頭に配置され、これは_IO_list_all
のチャンクのFDポインタの場所です。アンソートされたビン攻撃を介して_IO_list_all
にクローズアドレスを書き込んだためです。Mallocチェックのトリガー:このチャンクサイズの操作により、
malloc
が内部チェックを実行します。偽のフォワードチャンクのサイズをチェックすると、ゼロになり、エラーが発生してmalloc_printerr
が呼び出されます。
Small Binの操作により、チャンクのフォワードポインタを制御できます。 _IO_list_all との重複を使用して、偽の _IO_FILE 構造体を作成します。この構造体は、_IO_write_base
や _IO_write_ptr
などの重要なフィールドが適切に設定され、libc内部のチェックをパスする値に設定されています。さらに、偽の構造体内にジャンプテーブルが作成され、命令ポインタが任意のコード(たとえば system
関数)を実行できるアドレスに設定されます。
技術の残りの部分を要約すると:
古いトップチャンクを縮小する:古いトップチャンクのサイズを 0x61 に調整して、小さなビンに収めます。
偽の
_IO_FILE
構造体を設定する:古いトップチャンクと重なる偽の _IO_FILE 構造体を設定し、適切にフィールドを設定して実行フローを乗っ取ります。
次のステップは、現在アンソートされたビン内にある古いトップチャンクと重なる偽の _IO_FILE 構造体を作成することです。この構造体の最初のバイトは、実行されるコマンド(たとえば "/bin/sh")へのポインタを慎重に作成されています。
偽の _IO_FILE 構造体内の重要なフィールド、例えば _IO_write_base
や _IO_write_ptr
は、libc内部のチェックをパスする値に設定されています。さらに、偽の構造体内にジャンプテーブルが作成され、命令ポインタが任意のコードを実行できるアドレスに設定されます。通常、これは system
関数のアドレスやシェルコマンドを実行できる他の関数のアドレスになります。
攻撃は、malloc
の呼び出しが操作された _IO_FILE 構造体を介してコードの実行をトリガーするときに頂点に達します。これにより、任意のコードの実行が可能になり、通常はシェルが生成されるか、他の悪意のあるペイロードが実行されます。
攻撃の要約:
トップチャンクを設定する:チャンクを割り当ててトップチャンクのサイズを変更します。
トップチャンクをアンソートされたビンに強制する:より大きなチャンクを割り当てます。
Libcアドレスをリークする:アンソートされたビンから読み取るために脆弱性を使用します。
アンソートされたビン攻撃を実行する:オーバーフローを使用して _IO_list_all に書き込みます。
古いトップチャンクを縮小する:そのサイズを小さなビンに収めます。
偽の _IO_FILE 構造体を設定する:制御フローを乗っ取るために偽のファイル構造体を作成します。
コードの実行をトリガーする:攻撃を実行して任意のコードを実行します。
この手法は、free
を直接呼び出すことなく、ヒープ管理メカニズム、libc情報のリーク、ヒープオーバーフローを悪用してコードの実行を達成します。偽の _IO_FILE 構造体を注意深く作成し、適切な位置に配置することで、通常のメモリ割り当て操作中に制御フローを乗っ取ることができます。これにより、任意のコードの実行が可能になり、通常はシェルや他の悪意のある活動が実行されます。
参考
Last updated