[writeup]Based - Points: 200 (picoCTF 2019)

Mar 31, 2020 02:32 · 818 words · 2 minute read

今日のお昼にCTFもくもく会でpicoCTF2019の「Based - Points: 200」を解いたのでそのメモ。

$ nc 2019shell1.picoctf.com xxxx

みたいにするとこんな感じで問題文が表示される。

$ nc 2019shell1.picoctf.com xxxx
Let us see how data is stored
street
Please give the 01110011 01110100 01110010 01100101 01100101 01110100 as a word.
...
you have 45 seconds.....

Input:

問題名からもわかる通りこの01110011 01110100 01110010 01100101 01100101 01110100みたいなのを文字列に直して答えるという問題。
2進数のあとは8進数・16進数と続く。

最初はこんな感じで手でコピペして答えを出力するのを続けてフラグをゲットした。

input = '01100011 01101111 01101100 01101111 01110010 01100001 01100100 01101111'
puts input.split.map {|e| e.to_i(2).chr}.join

これだとあんまスマートじゃない感じがするので完全に自動で解くスクリプトを書いてみたのがこちら。

require 'socket'

TCPSocket.open("2019shell1.picoctf.com", xxxx) do |socket|
  input = ''
  loop do
    loop do
      data = socket.gets

      break if data.nil?
      return if data.include? 'picoCTF' # 終わり

      if data.start_with? "Please give"
        m = data.match /Please give( me)? the (?<input>.*) as a word./
        input = m[:input]
        unless input.include? ' '
          input = input.chars.each_slice(2).map {|a| a.join}.join(' ')
        end
      end

      puts data

      break if data.start_with? 'Input:' # 入力待ち
    end

    [2, 8, 16].each do |base|
      begin
        out = input.split.map {|e| e.to_i(base).chr}.join
      rescue => error
        next
      end
      if out.downcase.chars.all? {|c| [*'a'..'z'].include? c}
        puts out
        socket.puts out
      end
    end
  end
end

n進数を文字に直す部分はe.to_i(base).chrの部分。

問題の仕様を知ってる前提の処理が多いのがなぁって感じになってしまった…ある程度は仕方ないけど。

とはいえ、[2, 8, 16].eachみたいにして基数を試す部分も、2, 8, 16が来るとわかってる前提で処理してるにもかかわらず、謎に変換を試みてそれっぽいものになってるか=変換後が英字だけで構成されるかを検証してるのがどっちつかずで微妙な感じがする。

本当はちゃんと判定できたらいいんだけど、1eみたいなのは可能としても01は2, 8, 16のどれかわからないし、結論プレフィックスがないから無理な気がしてる。

それとncコマンドの役割でTCPSocketを使っているけど、対話的に処理する部分がいけてない。特定の文字の出力があること前提で入力と出力を制御してる。IO.selectとか使ってちゃんとできるのかなぁーって感じだったけどよくわからなかった。

こんな感じで妥協の産物になってしまったけど問題は解けたからまぁヨシ!