username / password がそのまま SQL に埋め込まれているので SQL injection ができるfetchone() の1カラム目だけなので、UNION SELECT (何らか), 'dummy' ... と2カラムに揃えるusers への検索条件は OR false を入れて無視することにするsqlite_master にあるであろう CREATE TABLE 文が入っている sql 列を持ってくる
secret_xxxx みたいな形式flag_xxxx を SELECT してくればOKログイン処理の SQL は以下のように組み立てられている。
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}';"
入力をそのまま埋め込んでいるので、username か password に SQL を仕込めばなんでもできる。あとは画面側が fetchone() を使って、1行目・1カラム目を Hello, {user[0]}! の形で返してくる前提で組み立てればよい。
フラグが入っているテーブル名・カラム名はハッシュ化されていてソースからは見えないので、まず SQLite のシステムテーブル sqlite_master から CREATE TABLE 文を引っ張ってくる。
ここで気をつけるのは以下。
users は2カラム (username, password) なので、UNION SELECT も2カラムに揃えて発行するfetchone() の1行目のみ。users 由来の行が先頭に来てほしくないので、OR false を入れて username / password あたりは無視するsqlite_master には自動インデックスの行も入っている。その sql カラムが NULL だったらしく、どうも先頭を読むだけでは table の SQL が得られない
UNION には暗黙のソートが入る。WHERE type='table' で null を消したときの先頭が(偶然)欲しいテーブル名だったand ''=' で、元の SQL のシングルクォートを入れ直すPayload (password に入れる)