脆弱性に関する実験:スタックオーバーフロー(その03)
プログラムによる自分自身のプログラムの変更に関する実験
スタック領域に置いたプログラムはそのままでは動かないことが分かりました。では、プログラムから、プログラムを変更して実行できるか実験してみましょう。
もし、これが出来れば色々と便利なのですが脆弱性は極めて高くなりますから、NGだろうとは思うのですが、一応実験です。
#include <stdio.h> int func(int i) { char * addr_ptr; addr_ptr = (char *) &addr_ptr; addr_ptr = addr_ptr + 16; addr_ptr = (char *) *(long *)addr_ptr; *addr_ptr++ = 0x90; // nop *addr_ptr++ = 0x90; // nop *addr_ptr++ = 0x90; // nop return i * 2; } int main(void) { int ans; ans = func(5) + 1; printf("Answer = \"%d\"\n", ans); return 0; }
これは先日の関数からの戻りアドレスを強制的に変更するプログラムをちょっと変更して作りました。先日のプログラムはワード単位での変更でしたのでlong型を使いましたが、今日のはバイト単位での変更なのでchar型を使います。
スタック フレーム上の戻りアドレスを調べ、そのアドレスにある命令をnop(何もしない命令)に書き換えるプログラムです。上手くいけばmain関数の“+1”が無くなりますので、答えは10になるハズです。
では、実行してみましょう。
ubuntu@ubuntu1304d64:~/Temp$ cc -o tricky2 tricky2.c ubuntu@ubuntu1304d64:~/Temp$ ./tricky2 Segmentation fault (core dumped) ubuntu@ubuntu1304d64:~/Temp$
やはり予想どおりアクセス違反で実行できません。
一応、gdbで追いかけて、どこで違反していのか確認してみます。
ubuntu@ubuntu1304d64:~/Temp$ gdb ./tricky2 (gdb) b func Breakpoint 1 at 0x400530 (gdb) run Starting program: /home/ubuntu/Temp/tricky2 warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 Breakpoint 1, 0x0000000000400530 in func () (gdb) disassemble func Dump of assembler code for function func: 0x000000000040052c <+0>: push %rbp 0x000000000040052d <+1>: mov %rsp,%rbp => 0x0000000000400530 <+4>: mov %edi,-0x14(%rbp) 0x0000000000400533 <+7>: lea -0x8(%rbp),%rax 0x0000000000400537 <+11>: mov %rax,-0x8(%rbp) 0x000000000040053b <+15>: mov -0x8(%rbp),%rax 0x000000000040053f <+19>: add $0x10,%rax 0x0000000000400543 <+23>: mov %rax,-0x8(%rbp) 0x0000000000400547 <+27>: mov -0x8(%rbp),%rax 0x000000000040054b <+31>: mov (%rax),%rax 0x000000000040054e <+34>: mov %rax,-0x8(%rbp) 0x0000000000400552 <+38>: mov -0x8(%rbp),%rax 0x0000000000400556 <+42>: movb $0x90,(%rax) 0x0000000000400559 <+45>: add $0x1,%rax 0x000000000040055d <+49>: mov %rax,-0x8(%rbp) 0x0000000000400561 <+53>: mov -0x8(%rbp),%rax 0x0000000000400565 <+57>: movb $0x90,(%rax) 0x0000000000400568 <+60>: add $0x1,%rax 0x000000000040056c <+64>: mov %rax,-0x8(%rbp) 0x0000000000400570 <+68>: mov -0x8(%rbp),%rax 0x0000000000400574 <+72>: movb $0x90,(%rax) 0x0000000000400577 <+75>: add $0x1,%rax 0x000000000040057b <+79>: mov %rax,-0x8(%rbp) 0x000000000040057f <+83>: mov -0x14(%rbp),%eax 0x0000000000400582 <+86>: add %eax,%eax 0x0000000000400584 <+88>: pop %rbp 0x0000000000400585 <+89>: retq End of assembler dump. (gdb)
最初にnopを書き込もうとしているのは +42の movb命令ですので、そこまで進めます。
(gdb) b *0x0000000000400556 Breakpoint 2 at 0x400556 (gdb) c Continuing. Breakpoint 2, 0x0000000000400556 in func () (gdb)
ここまでは、問題なく実行できました。
これから書き込もうとしているアドレスにある命令を確認します。
(gdb) x/i $rax 0x400598 <main+18>: add $0x1,%eax (gdb)
確かにmain関数の中の“+1”の部分です。
では、movb命令を実行してみましょう。
(gdb) ni Program received signal SIGSEGV, Segmentation fault. 0x0000000000400556 in func () (gdb)
やはりココでアクセスエラーを起こしています。
と、いうことで今日は、プログラムでプログラムを変更できないという実験でした。
脆弱性に関する実験:スタックオーバーフロー編(リンク)
脆弱性に関する実験:スタックオーバーフロー(その01) - AIDEMOIRE
スタックへのマシン語の書き込みとその実行に関する実験
脆弱性に関する実験:スタックオーバーフロー(その02) - AIDEMOIRE
戻りアドレスの書き換えによるロジックの変更に関する実験
脆弱性に関する実験:スタックオーバーフロー(その03) - AIDEMOIRE
プログラムによる自分自身のプログラムの変更に関する実験
脆弱性に関する実験:スタックオーバーフロー(その04) - AIDEMOIRE
スタックの書き換えによるシェルの起動
脆弱性に関する実験:スタックオーバーフロー(その05) - AIDEMOIRE
スタックオーバーフローの脆弱性に関する考察
脆弱性に関する実験:スタックオーバーフロー(その06) - AIDEMOIRE
スタック上のプログラムを実行する方法