AIDEMOIRE

【アイデモワール】

脆弱性に関する実験:スタックオーバーフロー(その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
スタック上のプログラムを実行する方法