0x200のプログラミングの基礎は手を動かさず読むだけにした。
0x300のプログラムの脆弱性攻撃から実際に動かしていく。
overflow_example.cについて見ていく。
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv) {
char buffer_one[8], buffer_two[8];
strcpy(buffer_one, "one"); /* "one"をbuffer_oneに設定 */
strcpy(buffer_two, "two"); /* "two"をbuffer_twoに設定 */
printf("[前] buffer_two は %p にあり、その値は \'%s\' です\n", buffer_two, buffer_two);
printf("[前] buffer_one は %p にあり、その値は \'%s\' です\n", buffer_one, buffer_one);
printf("\n[STRCPY] %ld バイトを buffer_two にコピーします\n\n", strlen(argv[1]));
strcpy(buffer_two, argv[1]); /* 最初の引数をbuffer_twoにコピーする */
printf("[後] buffer_two は %p にあり、その値は \'%s\' です\n", buffer_two, buffer_two);
printf("[後] buffer_one は %p にあり、その値は \'%s\' です\n", buffer_one, buffer_one);
}
$
gcc -g -o overflow_example overflow_example.c
$
gdb -q ./overflow_example a
ASLRの有効、無効でアドレス配置が変わるのか気になったので試す。
何もせず(ASLR無効)アドレス配置を表示
ASLRを有効にしてからアドレス配置を表示
(
gdb) set disable-randomization off
[heap], [vvar], [vdso], [stack]の配置のされ方が違うな
vDSO (virtual dynamic shared object) は、注意深く選択された kernel space ルーチン群を、user space 上のアプリケーションにエクスポートするための、カーネルメカニズムである。これにより、アプリケーションは、それらカーネル空間ルーチンを、システムコール インタフェースを使用して、それらの同じカーネル空間ルーチンを呼び出す時に固有の、user mode から カーネルモード への コンテキストスイッチ によるパフォーマンス低下なしに、プロセス内で呼び出せる。[1][2]
vvarが関数呼び出しに使うデータの保存領域で、vdsoが
システムコール呼び出しのための何か
heapやstackの中身の配置のされ方は同じなんかな?
と思って、実行結果を見たんだけど、
配置のされ方は同じみたい
ただ、書籍で書かれてる配置のされ方と違った。
書籍ではbuffer_two, buffer_one,
valueの順にアドレスが大きくなっていたんだけど、手元の環境は逆になってる。
考えられる理由としては、
・スタックのアドレスの進む方向(番地が大きい方に進む or 小さい方に進む)はCPUによってことなる
・異なる関数の変数のプッシュ順はOSによって異なる
・同じ関数内の変数のプッシュ順は
コンパイラによって異なる
以降、ASLR有効、無効はスタックオーバーフローに関係しなさそうなのでASLR無効の方を見ていく(スタックオーバーフローを利用して任意のコードを実行しようとする際はASLR関係しそうだけど)
8バイト突っ込んだときのアドレスが以下で、
9バイト突っ込んだときが以下、
スタックのアドレスが全部-16されてる
9バイト突っ込んだ方は、オーバーフローを検知されて異常終了してるけど、領域的にはスタック領域内だし他のスタックを書き換えたりはしてなさそう
さらに、バイト数を大きくすると、スタックのアドレスが小さい方にシフトして、結局オーバーフローを検知して異常終了
OSかCPUで、オーバーフローで任意のコードを実行できないように、スタックのアドレスを動的に格納するみたいやな
書籍にあったこっちも動かしてみたけど、書籍通りパスワード間違っててもオーバーフローでauth_flag書き換えはできんやった
password_bufferのほうが大きいアドレスに配置されてオーバーフローしてもauth_flagを書き換えられん
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password) {
int auth_flag = 0;
char password_buffer[16];
strcpy(password_buffer, password);
if(strcmp(password_buffer, "brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv) {
if(argc < 2) {
printf("使用方法: %s <パスワード>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1])) {
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" アクセスを許可します。\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
} else {
printf("\nアクセスを拒否しました。\n");
}
}
うーん、書籍に書いてあることと現実が違うと読み進める気失せるな…
この本の続きやるかどうかは、また考えよう。