なおすけの落書き帳

毎日がエブリデイ。

CyberRebeatCTF に参加した #cyberrebeatctf

CyberRebeatCTF というものに参加しました*1
どうやらIT系同人サークルの人たちが主催しているっぽい。

ennach.sakura.ne.jp

これの協力をしている会社Aqutras はその昔アルバイトでお世話になっていた会社で、ActiveDefense研究所については、社長?所長? の方の講演を聞いたりしたこともありFacebookでつながっており、参加してねポストが流れてきたので、興味もあり参加しました。

最終順位は63 /148でした。

以下writeupや所感など

writeup (とけたやつ)

Misc

Readme

image.jpgをダウンロードするとなんか変なものが書かれた画像ファイルが取得できる。
なんか コム? ソロ? ?モムワ? としか読めない…

というわけではなくて、ちょっと前に流行った日本人には読めないフォントで書かれているだけ。

nlab.itmedia.co.jp

一番下の行さえ読めばいい。
CRCTF{YOUCANPLAYCYBERREBEATINBOTHLANGUAGES}

Trivia

Crossword

何も考えずにCrosswordを解いていけばいい。 CRCTF{submarine}

Web

White Page

問題文にURLとログイン情報は書かれている。 しかし、URL先にアクセスすると、ログインボタンしか表示されていない。
f:id:naosuke2dx:20180908204709p:plain:w300

Inspectorを起動すると style="visibility:hidden"が指定されているので、これを消したらフォームが出てくる。 でログインしたらOK

f:id:naosuke2dx:20180909015128p:plain:w300

CRCTF{All I typed were four letters.}

Let's tweet

ツイートしてそのURL貼ったら終わり。
(何かを仕込めそうな気もするけど諦めた)

(そしてflagをメモり忘れた)

Programming

Calculation

指定されたコマンドを打つと、単純な加減算の問題が出てくる。 でそれに答えるとまた次の問題が…、というもの。

(*'-') < nc 59.106.212.75 8080
39 - 62 + 4 - 5 - 28
-52
55 - 19 - 68 - 94 + 51

めんどくさいのでスクリプトにさせましょう

require 'socket'

ADDR = ARGV[0]
PORT = ARGV[1]

socket = TCPSocket.open(ADDR, PORT)

while true
  response = socket.gets.chomp!
  puts response
  ans = eval(response)
  break unless response

  socket.write(ans.to_s + "\n")
end

CRCTF{She calls herself a human calculator}

Prime Factor

上と同様に指定されたアドレスにncすると、ある数字が送られる。
その数字を素因数分解して、最大の素数を答える、というもの。

これも同様にスクリプトでやりましょう。
Rubyには Prime.prime_division という便利なメソッドが生えていました。

require 'socket'
require 'prime'

ADDR = ARGV[0]
PORT = ARGV[1]

socket = TCPSocket.open(ADDR, PORT)

while true
  response = socket.gets.chomp!
  break unless response
  ans =  response.gsub!(/\W/,'')

  prime_max = Prime.prime_division(response.to_i).last.first

  puts prime_max

  socket.write(prime_max.to_s + "\n")
end

CRCTF{I'm a calculating type by nature.}

あと少しのとこまでやったもの

Programming

Visual Novels

あるユーザーは"Reading Power"を持っている。これはVisualNovelを月にどのくらい読めるかを示している。
彼がたくさんのVisualNovelを持っているとき、彼はどの組み合わせでプレイすればその月の満足度を最大にできるか。
最大の満足度を答えよ。

実際にアクセスすると、こんなかんじ。 ちなみに1問目の場合、30以下のサイズで最も満足できるのは28の本なので3と入力すると、次の問題が表示されます。

(*;-;)? < nc 59.106.212.75 8082

Reading Power = 30
Games([size, satisfaction]) =
[28, 3],
[27, 1],
[25, 2],
Answer = ?

3

Reading Power = 100
Games([size, satisfaction]) =
[60, 3],
[68, 3],
[38, 4],
[95, 10],
[81, 2],
Answer = ?

どう見てもナップサック問題じゃん! と思ったけど、プログラミング力がないので泣きながら再帰を書いた…

require 'socket'
require 'open3'

ADDR = ARGV[0]
PORT = ARGV[1]

socket = TCPSocket.open(ADDR, PORT)

reading_flag = false

def rec(i, max, list, memo)
  res = 0;

  if memo[i][max] >= 0
    memo[i][max]
  end

  return res if i == list.length

  if max < list[i][:size]
    res = rec(i + 1,  max, list, memo)
  else
    res = [ rec(i+1, max, list, memo), rec(i+1, max - list[i][:size], list, memo) + list[i][:satisfaction] ].max
  end

  memo[i][max] = res
end

while true
  response = socket.gets
  puts response
  response.gsub!(/[^\w\s]/,'')&.chomp!

  case response
  when /Reading/
    reading_flag = true
    @uplimit = response.split(' ')[2].to_i
    @list = []
]  when /Games/
    next
  when /^\d/
    response = response.split(' ')
    @list << {size: response[0].to_i, satisfaction: response[1].to_i}
  when /Answer/
    next
  else
    next unless reading_flag
    res = rec(0, @uplimit, @list, Array.new(@uplimit + 1, Array.new(@uplimit+1, -1)))
   socket.write(res.to_s + "\n")
    reading_flag = false
  end
end

最初メモ化もせずに書いてタイムアウトし、メモ化してもタイムアウトするので調べてDP使って泣きながら書き直した…

require 'socket'
require 'open3'

ADDR = ARGV[0]
PORT = ARGV[1]

socket = TCPSocket.open(ADDR, PORT)

reading_flag = false

def dp(max, list)
  max_cost = 0
  table = Hash.new
  table[0] = 0

  list.length.times do |ind|
    tmp = table.clone

    table.each do |k, v|
      total_w = k + list[ind][:size]
      total_v = v + list[ind][:satisfaction]

      unless tmp.has_key?(total_w)
        tmp[total_w] = total_v
      end
      if max >= total_w && total_v > max_cost
        max_cost = total_v
      end
    end

    table = tmp.clone
  end

  return max_cost
end

while true
  response = socket.gets
  puts response
  response.gsub!(/[^\w\s]/,'')&.chomp!

  case response
  when /Reading/
    reading_flag = true
    @uplimit = response.split(' ')[2].to_i
    @list = []
  when /Games/
    next
  when /^\d/
    response = response.split(' ')
    @list << {size: response[0].to_i, satisfaction: response[1].to_i}
  when /Answer/
    next
  else
    next unless reading_flag
    res = dp(@uplimit, @list)
    socket.write(res.to_s + "\n")
    reading_flag = false
  end
end

しかしこれだと30要素の答えが合わず…断念。

Web

Uploader

指定されたURLにアクセスするとファイルの一覧が。
ファイル検索っぽい窓があるので、そこに1発 ' OR '1'='1'; -- 打ち込んであげれば秘密のファイルが見れます。
で、秘密のファイルを開こうとしたところパスワードが掛かっており、断念です*2

感想

久しぶりのCTFでした。
100点と200点問題で構成されていましたが、200点問題が解けなかったのが悔しい…!
ただ、簡単かもしれないけどプログラミング問題に挑戦してとけたのは個人的には嬉しいです。
バイナリ系は全く無理なので今後頑張りたい。

そういえば社内CTFあるらしいので勉強しようと思います。

*1:といってもオンラインですが

*2:パスワード総当たりしてるけど間に合わなかった