sekai013's blog

JavaScriptとかを勉強する

KSNCTF #6 Login

問題

ksnctf - 6 Login

考えたこと

指定されたページに飛ぶと admin としてログインしろと言われたのでとりあえず ID, Pass 両方 admin で送信するとダメだよと言われる。
ちなみに ID, Pass 両方 admin にしたのは大学から借りた MacBook Air がそうなっていたからで、つい手が動いた。

気を取り直して SQL インジェクションを試してみた(本で読んだ)。
ID, Pass に 'or 1=1-- を入力して送信したらあっさり通った。ドヤ顔でページ読んだら、「旗は admin のパスワードだよ」ってことと、ヒントとして認証ページのソースが書いてあった。認証突破するだけじゃダメらしい。

<?php //認証部分抜粋
    function h($s){return htmlspecialchars($s,ENT_QUOTES,'UTF-8');}
    
    $id = isset($_POST['id']) ? $_POST['id'] : '';
    $pass = isset($_POST['pass']) ? $_POST['pass'] : '';
    $login = false;
    $err = '';
    
    if ($id!=='')
    {
        $db = new PDO('sqlite:database.db');
        $r = $db->query("SELECT * FROM user WHERE id='$id' AND pass='$pass'");
        $login = $r && $r->fetch();
        if (!$login)
            $err = 'Login Failed';
    }
?>

SELECT pass FROM user でいけるのでは?という気がしたけど出力されないから pass を知る手段がない。
SQL さっぱりわからん。ブルートフォースアタックは流石にキツそう。
ググったらブラインド SQL インジェクションというのがあることを知った。

WHERE id='' OR (SELECT length(pass) FROM user WHERE id='admin') <= length

みたいな WHERE 句を作ればパスワードの長さを絞っていける。長さが分かったら

WHERE id='' OR substr((SELECT pass FROM users), n, 1)='a'

みたいなコードで頑張れる。 JavaScript っぽく書くと pass.charAt(n) === 'a' みたいなことをやる。 頑張れそうだけど、手動では頑張れないのでコード書いた。

https://github.com/sekai013/ksnctf/blob/master/login/login.rb

二分探索でパスワード文字数を調べて、あとは総当りで何文字目が何なのか調べる。 走らせたら何分かで旗出てきた。

思ったこととか

SQL は全部ググった。substr とか length みたいなメソッドがあることも知らなかった。
流石に SQL 読み書きできるようになりたい。 SQL さえ知っていればググらずに思いつけた気がする。頑張りたい。