C言語のgets関数でリターンアドレスを書き換える
C言語のgets()関数は、バッファオーバーランを引き起こすため、使用するなと言われています。このページは、実際にバッファオーバーランを引き起こすためのサンプルです。
読み方
- gets
- げっつえす、げっつ
目次
概要
このページのサンプルでは、gets()関数を使用するプログラムのリターンアドレスを書き換え、プログラムに含まれている別の関数を呼び出します。環境によって、バイトオーダー、アドレス、プログラム改ざんのデータのサイズが変わるのでご注意ください。
gets.c は、以下の動作をします。
- main()がbar()を呼び出す
- bar()は、gets()を呼び出す
- stdin から入力を得る
- gets()関数が返る
- bar()は終わり、main()に返る
- main()が終了し、プログラムも終了する
この実験では、gets()の入力データで、bar()のスタックフレームにあるリターンアドレスを書き換えます。結果、以下の振る舞いとなります。
- main()がbar()を呼び出す
- bar()は、gets()を呼び出す
- stdin から入力を得る
- gets()関数が返る
- gets()の読み込みで、bar()のリターンアドレスがfoo()に書き換わる
- bar()は終わり、foo()が呼び出される
- fooは、puts()でstdoutに文字列を表示し、プログラムを終了(exit)する
実験環境
- FreeBSD 64bit環境
- ただし、バイナリは、32bit ELFで作成する
- gcc コンパイラ 4.2.1
ソース
gets.c
/* * gets.c * Copyright (C) 2014 kaoru <kaoru@bsd> */ #include <stdio.h> #include <stdlib.h> void foo() { (void) puts("OK"); exit(EXIT_SUCCESS); } void bar() { char buf[1]; gets(buf); } int main(int argc, char const* argv[]) { bar(); return 0; }
コンパイル
32bit環境の場合
cc gets.c
64bit環境の場合
cc -m32 gets.c
以下の警告が出ても、今回の実験では無視します。
% cc -m32 gets.c /var/tmp//ccAIkjrl.o: In function `bar': gets.c:(.text+0x2d): warning: warning: this program uses gets(), which is unsafe.
使い方
バッファオーバーランを確認する
最初にデータの書き換えを行う領域を特定するため、バッファをどの程度書き込みをするとバッファオーバーランを引き起こせるか確認します。
% echo -n 'a' | ./a.out warning: this program uses gets(), which is unsafe. % echo -n 'aa' | ./a.out warning: this program uses gets(), which is unsafe. % echo -n 'aaa' | ./a.out warning: this program uses gets(), which is unsafe. % echo -n 'aaaa' | ./a.out warning: this program uses gets(), which is unsafe. % echo -n 'aaaaa' | ./a.out warning: this program uses gets(), which is unsafe. Segmentation fault Exit 139
a が 5 文字になったときに Segmentation fault を引き起こしました。
fooのアドレスを調べる
リターンアドレスを書き換え、意図したプログラムを実行するために、実行したいプログラムのアドレスを調べます。 foo()関数のアドレスを調べます。 どのようなやり方でも構いません。objdumpやgdbなどいくつも方法はあります。
% objdump -d a.out | fgrep foo 08048490 <foo>:
foo()関数のアドレスは、 08048490 とわかりました。
リターンアドレスを書き換える
アドレスを書き換えるために、プログラムのstdinに入力するデータを作成します。 インテルCPUのバイトオーダーは、リトルエンディアンであるため、アドレス 08048490を並び替えます。 プログラム改ざんのデータは、以下のようになります。
aaaaa\x90\x84\x04\x08
それでは、プログラムに上のコードを入力します。foo()が呼び出され、OKが表示されることを確認できました。
% perl -e 'print "aaaaa\x90\x84\x04\x08"' | ./a.out warning: this program uses gets(), which is unsafe. OK
以上のことからgets()関数を使うべきではないということです。
関連項目
- gdbでC言語プログラムのリターンアドレスを書き換える
- C言語でリターンアドレスを書き換える
- C言語のgets関数でリターンアドレスを書き換える
ツイート