Nootropic.me

Return-to-libcまとめ

今回はReturn-to-libcについて検証してみました。

Return-to-libcはスタック上でシェルコードの実行が禁止されていた場合の迂回策になります。

ところで管理人はよく「Return-to-libc」と「Return-into-libc」の2つを見かけるのですがどちらが正しいのでしょうか。

Return-to-libc:検索結果(217,000件)

Return-into-libc:検索結果(91,600件)
これだけで判断するのも愚かですが検索結果から見るとReturn-to-libcが正しいのでしょうか。本記事ではReturn-to-libcを採用したいと思います。

お膳立て

ソースコード

/*rtltest.c*/
#include <stdio.h>
#include <string.h>

void vuln(char *pass){
        char buf[512];
        strcpy(buf,pass);
}
int main(int argc,char** argv){
        vuln(argv[1]);
        return 0;
}

Return-to-Registerで使用したものと同じソースコードです。
vuln関数内で呼ばれているstrcpy関数にバッファオーバーフローの脆弱性があります。

セキュリティ機構とライブラリ
今回セキュリティ機構や攻撃に使用するライブラリは以下のようになっています。

NX有効、ASLR無効、STACKGuard無効。
攻撃に使用するライブラリ「/lib/tls/i686/cmov/libc.so.6」(0xb7e8b000)

スタックの構造把握

リターンアドレスまでの距離524バイト。
524+リターンアドレス(4バイト)=528
528バイトでリターンアドレスを埋められます。
ここもReturn-to-Registerの記事と同じです。

関数の逆アセンブル結果

ライブラリ内の関数のアドレス

system関数のアドレスは0xb7ea9bb0
exit関数のアドレスは0xb7e9d230
のようです。

ライブラリ内の各セクションのアドレス

関数のアドレス

今回攻撃に使用する関数のアドレスです。


今回system関数を使って起動したいのは「/bin/bash」なので残り必要なのは「/bin/bash」という文字列へのアドレスですね。
上記のコマンドで探してみることにします。


見事ヒットしました。前の「SHELL=」という文字列が邪魔なのでアドレスを少し調整することにします。


「/bin/bash」というアドレスが格納されているアドレスは0xbffff72eのようです。
ここで攻撃の為に出揃った情報を一度まとめてみることにします。

さてこれだけの情報が揃えばシェルのゲットはもう目の前です。

Return-to-libc攻撃

スタックの構造を以下の形に持っていけば攻撃を行うことが出来ます。
A*524 system関数のアドレス exit関数のアドレス /bin/bashのアドレス
まず適当なデータで524バイトの領域を埋めた後、
libc関数へのアドレス(今回はsystem)、system関数の戻り先のアドレス(今回はexit)、system関数の引数のアドレス(今回は/bin/bash)を配置します。
この時リターン先のアドレス(system)に飛んだ際のスタックの構造は以下のようになります。

system関数の戻り先(exit)
system関数の引数(/bin/bash)
このスタックの構造はsystem("/bin/bash");を呼び出した時と同じ状態になります。


何故か「/bin/bash」の文字列が一文字ずれて=から始まってしまいました。仕方ないので文字列のアドレスを0xbffff72fにずらしてみると上手くいきました。
相変わらず攻撃に成功したかどうか分かりづらいのもあって感動がないですね。
でもexitコマンドを叩くとgdbに戻るのでどうやら攻撃には成功しているようです。

Return-to-libc手法はASLRが有効な場合はその攻撃を成功させるのが非常に難しいです。
32bitOSの場合はランダマイズしてもパターンは16ビット分のランダム化(65536通り)しかないのでブルートフォース攻撃との連携でシェルを取るのにさほど時間はかかりませんが
64bitOSではその成功確率は目も当てられない事になります。
これを上手いことハックするのがROPと呼ばれる手法です。ROPについてもいずれ勉強してまとめたいと考えています。


Author:ラロ