こんとろーるしーこんとろーるぶい

週末にカチャカチャッターン!したことを貼り付けていくブログ

BSides Delhi CTF 2018 - Auth3ntication

問題文

Login if you can. It is protected by so called secure algorithm. Let's see how secure it is...

f:id:graneed:20181026232358p:plain

writeup

HTMLソースを読むと、暗号化された文字列がある。

var func = "\x0d\x0d\x45\x17\x48\x09\x5e\x4b\x17\x3c\x1a\x1f\x2b\x1b\x7a\x0c\x1f\x66\x0b\x1a\x3e\x51\x0b\x41\x11\x58\x17\x4d\x55\x16\x42\x01\x52\x4b\x0f\x5a\x07\x00\x00\x07\x06\x40\x4d\x07\x5a\x07\x14\x19\x0b\x07\x5a\x4d\x03\x47\x01\x13\x43\x0b\x06\x50\x06\x13\x7a\x02\x5d\x4f\x5d\x18\x09\x41\x42\x15\x59\x48\x4d\x4f\x59\x1d\x43\x10\x15\x00\x1a\x0e\x17\x05\x51\x0d\x1f\x1b\x08\x1a\x0e\x03\x1c\x5d\x0c\x05\x15\x59\x55\x09\x0d\x0b\x41\x0e\x0e\x5b\x10\x5b\x01\x0d\x0b\x55\x17\x02\x5a\x0a\x5b\x05\x10\x0d\x52\x43\x40\x15\x46\x4a\x1d\x5f\x4a\x14\x48\x4b\x40\x5f\x55\x10\x42\x15\x14\x06\x07\x46\x01\x55\x16\x42\x48\x10\x4b\x49\x16\x07\x07\x08\x11\x18\x5b\x0d\x18\x50\x46\x5c\x43\x0a\x1c\x59\x0f\x43\x17\x58\x11\x04\x14\x48\x57\x0f\x0a\x46\x17\x48\x4a\x07\x1a\x46\x0c\x19\x12\x5a\x22\x1f\x0d\x06\x53\x43\x1b\x54\x17\x06\x1a\x0d\x1a\x50\x43\x18\x5a\x16\x07\x14\x4c\x4a\x1d\x1e";

Usernameに入力した文字列を使用して、暗号化された文字列にXORをかけて、evalで実行する処理のようだ。なお、Passwordは特に何も使用していない。

Usernameは9文字でないと、エラー表示になる。

暗号化前の文字列に含まれる9文字以上の文字列を予測し、暗号化された文字列とでXORをとって復号すれば、復号した文字列のどこかに9文字のキーが含まれているはずである。

キーは9文字であることがわかっているため、bluteforce可能なレベル。

元の文字列に何が出現しているかだが、認証結果を表示するエリアであるid=cresponseのdivタグがあるため、 ここに結果を表示するために、cresponseというワードが入っていると予測する。

bluteforceのスクリプトを書いて実行。
なお、普段はこの手のスクリプトpythonで書いているが、復号した文字列に対してevalを実行する場合があるかもと思いJavaScriptで書いた。

<script>
    function a(h) {
        if (h.length != 2) {
            h = "\x30" + h;
        }
        return "\x5c\x78" + h;
    }

    function x(d) {
        if (d < 0) {
            d = 0xFFFFFFFF + d + 1;
        }
        return d.toString(16).toUpperCase();
    }

    var func =
        "\x0d\x0d\x45\x17\x48\x09\x5e\x4b\x17\x3c\x1a\x1f\x2b\x1b\x7a\x0c\x1f\x66\x0b\x1a\x3e\x51\x0b\x41\x11\x58\x17\x4d\x55\x16\x42\x01\x52\x4b\x0f\x5a\x07\x00\x00\x07\x06\x40\x4d\x07\x5a\x07\x14\x19\x0b\x07\x5a\x4d\x03\x47\x01\x13\x43\x0b\x06\x50\x06\x13\x7a\x02\x5d\x4f\x5d\x18\x09\x41\x42\x15\x59\x48\x4d\x4f\x59\x1d\x43\x10\x15\x00\x1a\x0e\x17\x05\x51\x0d\x1f\x1b\x08\x1a\x0e\x03\x1c\x5d\x0c\x05\x15\x59\x55\x09\x0d\x0b\x41\x0e\x0e\x5b\x10\x5b\x01\x0d\x0b\x55\x17\x02\x5a\x0a\x5b\x05\x10\x0d\x52\x43\x40\x15\x46\x4a\x1d\x5f\x4a\x14\x48\x4b\x40\x5f\x55\x10\x42\x15\x14\x06\x07\x46\x01\x55\x16\x42\x48\x10\x4b\x49\x16\x07\x07\x08\x11\x18\x5b\x0d\x18\x50\x46\x5c\x43\x0a\x1c\x59\x0f\x43\x17\x58\x11\x04\x14\x48\x57\x0f\x0a\x46\x17\x48\x4a\x07\x1a\x46\x0c\x19\x12\x5a\x22\x1f\x0d\x06\x53\x43\x1b\x54\x17\x06\x1a\x0d\x1a\x50\x43\x18\x5a\x16\x07\x14\x4c\x4a\x1d\x1e";

    function exec(k) {
        buf = "";
        for (var i = 0, j = 0; i < func.length; i++) {
            c = parseInt(func.charCodeAt(i));
            c = c ^ k.charCodeAt(j);
            if (++j == k.length) {
                j = 0;
            }
            buf += eval('"' + a(x(c)) + '"');
        }
        return buf;
    }

    function blute() {
        keyword = 'cresponse';
        for (var i = 0; i < keyword.length; i++) {
            cyclekeyword = keyword.substr(i, keyword.length - i) + keyword.substr(0, i);
            console.log("-----------------cyclekeyword:" + cyclekeyword + "-----------------");
            r = exec(cyclekeyword);
            for (var j = i; j < r.length - keyword.length; j = j + keyword.length) {
                tmpkey = r.substr(j, keyword.length);
                console.log(exec(tmpkey));
            }
        }
    }

    blute();
</script>

実行結果は以下の通り。
Chromeの開発者ツールで実行した。

auth.html:36 -----------------cyclekeyword:cresponse-----------------
(snip)
auth.html:40 ix(u == "XorIsNotSooS3cur3") { if(document.location.href.indexOf("?p=") == -1) { document.location = document.location.href + "?p=" + u; } } else {  $("#cresponse").html("<div class='error'>Wrong password sorry.")}
(snip)

http://35.200.218.73/?p=XorIsNotSooS3cur3にアクセスしたところ、flagが表示された。 flag{586f7249734e6f74536f6f533363757233}