Posts Tagged Security Hole

スタックオーバーフローをテスト

最近、「C言語ポインタ完全制覇 (標準プログラマーズライブラリ)」という本を読んでる。
タイトルの通り、C言語の、それもポインタだけに的を絞った本。
これがなかなかどうして面白くて、文字も大きいのですらすら進んでしまう。
ただ、簡単かと言われれば全く違って、興味深くて考えさせられるような話題ばかり。

ようやく100ページまで進んだ。
そしたらセキュリティーホールの温床になると言うスタックオーバーフローをテストしようという楽しいコードが書いてあった。
で、試したくなるじゃないですか。
若干情報多めに書いて写経してみたのが下記。

// stack_overflow_test.c
#include <stdio.h>
void hello()
{
static int i = 0;
fprintf( stderr, "stderr:\tHello xD\n" );
printf( "\tNo.%d Hello:P\n", i++ );
}
void func()
{
void        *buf[10];
static int  i;
printf( "buf\t%p\n*buf\t%p\n&i\t%p\n&hello\t%p\n\n", buf, *buf, &i, &hello );
for ( i = 0; i < 16; ++i ) {
buf[i] = hello;
printf( "buf[%d]\t%p\n", i, &buf[i] );
}
printf( "\n\tEnd of func()\n\n" );
}
int main()
{
int buf[1000];
printf( "Start\n\n" );
func();
printf( "End\n\n" );
return 0;
}

で、実行するとこう。

% gcc -o stack_overflow_test stack_overflow_test.c
% ./stack_overflow_test
Start
buf     0x7fff5fbfdf20
*buf    0x100000eee
&i      0x10000108c
&hello  0x100000d1c
buf[0]  0x7fff5fbfdf20
buf[1]  0x7fff5fbfdf28
buf[2]  0x7fff5fbfdf30
buf[3]  0x7fff5fbfdf38
buf[4]  0x7fff5fbfdf40
buf[5]  0x7fff5fbfdf48
buf[6]  0x7fff5fbfdf50
buf[7]  0x7fff5fbfdf58
buf[8]  0x7fff5fbfdf60
buf[9]  0x7fff5fbfdf68
buf[10] 0x7fff5fbfdf70
buf[11] 0x7fff5fbfdf78
buf[12] 0x7fff5fbfdf80
buf[13] 0x7fff5fbfdf88
buf[14] 0x7fff5fbfdf90
buf[15] 0x7fff5fbfdf98
End of func()
zsh: segmentation fault  ./stack_overflow_test

あれ~?
hello()が実行されてない・・・??
と言うわけで、落ちる前に一度hello()を呼んでみた。

#include <stdio.h>
void hello()
{
static int i = 0;
fprintf( stderr, "stderr:\tHello xD\n" );
printf( "\tNo.%d Hello:P\n", i++ );
}
void func()
{
void        *buf[10];
static int  i;
printf( "buf\t%p\n*buf\t%p\n&i\t%p\n&hello\t%p\n\n", buf, *buf, &i, &hello );
hello();
for ( i = 0; i < 16; ++i ) {
buf[i] = hello;
printf( "buf[%d]\t%p\n", i, &buf[i] );
}
printf( "\n\tEnd of func()\n\n" );
}
int main()
{
int buf[1000];
printf( "Start\n\n" );
func();
printf( "End\n\n" );
return 0;
}

するとこうなる。

% gcc -o stack_overflow_test stack_overflow_test.c
Start
buf     0x7fff5fbfdf20
*buf    0x100000eee
&i      0x10000108c
&hello  0x100000d10
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfdf20
buf[1]  0x7fff5fbfdf28
buf[2]  0x7fff5fbfdf30
buf[3]  0x7fff5fbfdf38
buf[4]  0x7fff5fbfdf40
buf[5]  0x7fff5fbfdf48
buf[6]  0x7fff5fbfdf50
buf[7]  0x7fff5fbfdf58
buf[8]  0x7fff5fbfdf60
buf[9]  0x7fff5fbfdf68
buf[10] 0x7fff5fbfdf70
buf[11] 0x7fff5fbfdf78
buf[12] 0x7fff5fbfdf80
buf[13] 0x7fff5fbfdf88
buf[14] 0x7fff5fbfdf90
buf[15] 0x7fff5fbfdf98
End of func()
stderr: Hello xD
No.1 Hello:P
stderr: Hello xD
No.2 Hello:P
stderr: Hello xD
No.3 Hello:P
stderr: Hello xD
No.4 Hello:P
stderr: Hello xD
No.5 Hello:P
zsh: segmentation fault  ./stack_overflow_test

これでしっかり動いた。
5回勝手(不正)に実行されちょる。

本の説明によるとメモリの状態は下記のような感じとなるらしい。
–ここ移行に別の変数が追加されていく–
buf[0] 0x7fff5fbfdf20
buf[1] 0x7fff5fbfdf28
buf[2] 0x7fff5fbfdf30
buf[3] 0x7fff5fbfdf38
buf[4] 0x7fff5fbfdf40
buf[5] 0x7fff5fbfdf48
buf[6] 0x7fff5fbfdf50
buf[7] 0x7fff5fbfdf58
buf[8] 0x7fff5fbfdf60
buf[9] 0x7fff5fbfdf68
==他の自動変数==
==関数終了時に戻るアドレス情報など==
==その他の領域==

考察:buf[10]~buf[15]までの6個分余分に配列があると仮定して上がいてます、と(・・アレ?5回しか実行されてない??)。
自動変数を食い破り、呼び出し元の関数情報を破壊して行きます、と。
あ、わかった。。。
funcに自動変数iがあるから1回少ないんだ。
funcをこう変えた。

void func()
{
void        *buf[10];
static int  i;
int a, b, c, d, e; // 追加
printf( "buf\t%p\n*buf\t%p\n&i\t%p\n&hello\t%p\n\n", buf, *buf, &i, &hello );
hello();
for ( i = 0; i < 15; ++i ) {
buf[i] = hello;
printf( "buf[%d]\t%p\n", i, &buf[i] );
}
printf( "\n\tEnd of func()\n\n" );
}

結果はこう。
(ちなみに、ちょっとプログラムいじっちゃったからアドレスと表記が若干違う。。)

Start
&func   0x100000d3f
buf     0x7fff5fbfeea0
*buf    0xffffffffffffffff
&i      0x10000108c
&hello  0x100000cf8
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfeea0
buf[1]  0x7fff5fbfeea8
buf[2]  0x7fff5fbfeeb0
buf[3]  0x7fff5fbfeeb8
buf[4]  0x7fff5fbfeec0
buf[5]  0x7fff5fbfeec8
buf[6]  0x7fff5fbfeed0
buf[7]  0x7fff5fbfeed8
buf[8]  0x7fff5fbfeee0
buf[9]  0x7fff5fbfeee8
buf[10] 0x7fff5fbfeef0
buf[11] 0x7fff5fbfeef8
buf[12] 0x7fff5fbfef00
buf[13] 0x7fff5fbfef08
buf[14] 0x7fff5fbfef10
End of func()
End
zsh: segmentation fault  ./stack_overflow_test

ほら、実行されない。
ただ不思議なことに、

int a;

だけにした場合と、

int a, b, c, d;

までにした場合、

Start
&func   0x100000d3f
buf     0x7fff5fbfeeb0
*buf    0x7365745f776f6c66
&i      0x10000108c
&hello  0x100000cf8
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfeeb0
buf[1]  0x7fff5fbfeeb8
buf[2]  0x7fff5fbfeec0
buf[3]  0x7fff5fbfeec8
buf[4]  0x7fff5fbfeed0
buf[5]  0x7fff5fbfeed8
buf[6]  0x7fff5fbfeee0
buf[7]  0x7fff5fbfeee8
buf[8]  0x7fff5fbfeef0
buf[9]  0x7fff5fbfeef8
buf[10] 0x7fff5fbfef00
buf[11] 0x7fff5fbfef08
buf[12] 0x7fff5fbfef10
buf[13] 0x7fff5fbfef18
buf[14] 0x7fff5fbfef20
End of func()
stderr: Hello xD
No.1 Hello:P
stderr: Hello xD
No.2 Hello:P

となる。
エラーは起きない。
けど変。

結論:戻り先アドレスまで上がいたら、そこにある情報を実行しちゃいました、的な感じの認識。

まぁ、何にせよ、バッファが溢れないように注意しないといつか痛い目見ますよ、と。
精進します。

アマゾンのサーバでエラーが起こっているかもしれません。
一度ページを再読み込みしてみてください。

Post to Twitter

, , , ,

No Comments