ふるつき

私は素直に思ったことを書いてるけど、上から目線だって言われる

OCamlabCTF #6

前回の記事

furutsuki.hatenablog.com

#3, #5 は師匠回で、 #4 は私だったけどあまりに酷い問題しか無かったので無効。 #3 回からは SECCON online や SECCON Final を意識していきたいという感じでやってます。

#5 回は exploit について基礎をやって、 #6 はWeb問をやってくれということだったので、 Web 問題を3問つくりました。うち1問は #4 で酷かったやつの リメイクです。問題数が少ないことを気にしていたので、開始直前と開始後に crypto問題を2問だしました。 今回は3連休をまるごと作問に回したのに問題数さほどだし簡単なものしかできないし本当に作問が難しい。

スコアサーバは少しだけ更新しました。

f:id:Furutsuki:20180925152743p:plain

github.com

Flag Checker [Web 100]

github.com

f:id:Furutsuki:20180925150103p:plain

 簡単な javascript による flag check です。

        let f = function ff(o, c) {
            var x = [248, 184, 216, 178, 213, 181, 210, 146, 197, 126, 232, 173, 235, 160, 224, 172, 238, 155, 209, 160, 203, 169, 223, 141, 225, 161, 201, 143, 200, 130, 224, 144, 213, 135, 192, 126, 207, 143, 161];
            l = ~!(x[0] - x[2] + x[28] - x[10]) + !(x[13] ^ x[19]);
            a = -~a;
            z = c ^ o.charCodeAt(a + l);
            if (x.length === a + l) {
                return true;
            }
            if (z != x[a + l] + (a * l * l)) {
                return false;
            }
            return ff(o, z);
        }

        function check() {
            a = false;
            flg = document.getElementById("flag");
            if (flg != null) {
                a = f(flg.value, 182);
            }
            alert(a ? "OK, THIS IS FLAG" : "BAD...");
        }

 割と読める形をしているので読むと、 flag の文字を 1文字ずつ比較しているだけなので、 適当なところに console.log か breakpoint を挟むだけで解けます。再帰してるのと global な aを使ってるのがチャームポイント。

Remember Me [Web 150]

f:id:Furutsuki:20180925150832p:plain

f:id:Furutsuki:20180925150940p:plain

github.com

 本当はラブライブ!のキャラクターの名前を1億回連続で当てると flag が降ってくるみたいなページなんですが、権利が明らかにだめな画像を使っていたので差し替えています。

 ソースコードも渡していて、 そこからログイン時の「RememberMe」にチェックを入れると、 php のオブジェクトがシリアライズされて cookie に保存されていることがわかるので、 Object Injection をやるだけの問題です。一応ユーザ名を変えないと DBに保存された値が使われてしまって回数を変更できない、みたいな捻りにもなっていない捻りは入れましたが、つまらない感じになりました。

Login Me [Web 200]

github.com

f:id:Furutsuki:20180925151353p:plain

 一応最難の問題。 Login と Register しかなくて、 admin という名前で ログインできると flag が手に入ります。 Login はちゃんとエスケープ処理をしていますが、 Registser ではそれがなく、エラーメッセージも見えます。というところから、 time based sql injection で admin のパスワードを抜く問題でした。テーブル名とかはエスパーです。

 師匠は admin のパスワードの1文字をパスワードにもつユーザを作って、そこに 256回ログインしてみて……という感じで推測したようです。

Crypter1 [Crypto 50]

github.com

開始直前に追加したやつ。 微妙に間違ったスクリプトを配布していて、それでも解けるけど間違っていたので後から修正しました。次のようなスクリプトで、 flag の形式がわかっていると先頭ブロックがわかるので、kを求めて次々にxor するだけになります。

import os
import hashlib
import sys


def enc_block(m, k):
    return m ^ k

def pad(m, a):
    l = a - (len(m) % a)
    return m + chr(l)*l

def enc(m):
    m = pad(m, 8)
    k = int(os.urandom(8).encode("hex"), 16)

    r = ""
    for i in range(len(m)/8):
        c = enc_block(int(m[i*8:(i+1)*8].encode("hex"), 16), k)
        r += hex(c)[2:].rstrip("L")
    return r

if __name__ == '__main__':
    print(enc(sys.argv[1]))

So Cool [Crypto 75]

github.com

 あとから追加したやつ。75でだしましたが、 50 でも高いくらいかも。 次々に文字を c_(i+1) = pow(m, c_i, n) しているだけなので、 m を 256 通り試せばいいだけです。

import os
import sys

def enc(m, k, n):
    c = []
    for x in m:
        y = pow(ord(x), k, n)
        c.append(hex(y).rstrip("L"))
        k = y
    return ",".join(c)

while True:
    k = int(os.urandom(4).encode("hex"), 16)
    if k.bit_length() <= 20:
        break
e = 2147483647

print(enc(sys.argv[1], k, e))

雑感

 作問ひたすら難しい。こういうのって慣れなんですかね、それとも実力不足なのか、どちらでも嫌ですが。CTFがみんなこのくらいの難易度だとすらすら解けそうです。