vsCTF 2024 writeup

2024/06/17に公開

SECCON に時間を費やしていたので、あんまり時間が使えなかったけれども、十分気休めになった。

crypto/not-quite-caesar [595 solves]

Caesar??? Couldn't be this!

import random
random.seed(1337)
ops = [
    lambda x: x+3,
    lambda x: x-3,
    lambda x: x*3,
    lambda x: x^3,
]


flag = list(open("flag.txt", "rb").read())
out = []
for v in flag:
    out.append(random.choice(ops)(v))
print(out)
[354, 112, 297, 119, 306, 369, 111, 108, 333, 110, 112, 92, 111, 315, 104, 102, 285, 102, 303, 100, 112, 94, 111, 285, 97, 351, 113, 98, 108, 118, 109, 119, 98, 94, 51, 56, 159, 50, 53, 153, 100, 144, 98, 51, 53, 303, 99, 52, 49, 128]

ランダムのシード値が公開されているので、同じシードをセットし、opsの逆操作をすればいい。

import random
random.seed(1337)

ops = [
    lambda x: x-3,
    lambda x: x+3,
    lambda x: x//3,
    lambda x: x^3,
]

out = [354, 112, 297, 119, 306, 369, 111, 108, 333, 110, 112, 92, 111, 315, 104, 102, 285, 102, 303, 100, 112, 94, 111, 285, 97, 351, 113, 98, 108, 118, 109, 119, 98, 94, 51, 56, 159, 50, 53, 153, 100, 144, 98, 51, 53, 303, 99, 52, 49, 128]

flag = []

for v in out:
    flag.append(random.choice(ops)(v))

print(''.join( chr(c) for c in flag))

vsctf{looks_like_ceasar_but_isnt_a655563a0a62ef74}

crypto/dream [104 solves]

I hear python MT can be broken with 624 outputs, but I only really need 8 random numbers. Surely you can't break it... right?

chall.py
#!/usr/local/bin/python
if __name__ != "__main__":
    raise Exception("not a lib?")

from os import urandom
# check if seed.txt exists
try:
    seed = open("seed.txt", "rb").read()
except:
    seed = urandom(8)
    open("seed.txt", "wb").write(seed)

seed = int.from_bytes(seed, "big")
import random
random.seed(seed)
from ast import literal_eval
idxs = literal_eval(input(">>> "))
if len(idxs) > 8:
    print("Ha thats funny")
    exit()
for idx in range(624):
    rand_out = random.getrandbits(32)
    if idx in idxs:
        print(rand_out)


key = random.getrandbits(256)
nonce = random.getrandbits(256)
flag = open("flag.txt").read()
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from hashlib import sha256
aes_key = sha256(str(key).encode()).digest()[:16]
aes_nonce = sha256(str(nonce).encode()).digest()[:16]
cipher = AES.new(aes_key, AES.MODE_GCM, nonce=aes_nonce)
ct = cipher.encrypt(pad(flag.encode(), 16))
print(ct.hex())

乱数のシード値を特定する問題に見える。

pythonのrandomはメルセンヌツイスターが使われている。 624 個連続した乱数を手に入れることが出来ればシードを特定できる。

今回の場合は、一度に 8 個までしかシード値を得ることができない。

ただ、実験していて気付いたが、今回の場合もシード値が実質固定されている(何回も同じ要素を確認してみるとわかる)ため、\frac{624}{8} 回かけて連続した乱数を手に入れることによって、シード値を求めることができる。

シード値の特定計算はこのサイトを利用した。

乱数のシード値特定
from pwn import remote
import struct

HOST = 'vsc.tf'
PORT = 5001

def get_random_values(idxs):
    conn = remote(HOST, PORT)
    conn.recvuntil(b'>>> ')
    conn.sendline(str(idxs).encode())
    values = []
    for _ in range(len(idxs)):
        value = conn.recvline().strip()
        values.append(int(value))
    conn.close()
    return values

xs1 = [None] * 624

for i in range(0, 624, 8):
    idxs = list(range(i, i+8))
    values = get_random_values(idxs)
    for j, value in enumerate(values):
        xs1[i + j] = value
mt_state = [untemper(x) for x in xs1]
flagの復元
import random
random.setstate((3, tuple(mt_state + [624]), None))

key = random.getrandbits(256)
nonce = random.getrandbits(256)

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from hashlib import sha256

ct_hex = "cb3cc14d2f5eeac6b5645bb66fa268d88399d1654df20668110e1d04bf135db71930985b5eba307c0197b035f2e9203f"

aes_key = sha256(str(key).encode()).digest()[:16]
aes_nonce = sha256(str(nonce).encode()).digest()[:16]

ct = bytes.fromhex(ct_hex)

cipher = AES.new(aes_key, AES.MODE_GCM, nonce=aes_nonce)

pt = unpad(cipher.decrypt(ct), 16)

print(pt.decode())

vsctf{dream_luck???_5e3ec2f2d338fc9f}

algo/baby-game [345 solves]

$N \times N $ の2次元平面上でプレイヤーA,Bがいる。片方がもう片方を囲めるように最適に動くので、どちらが囲めるかどうかを判定せよ。(みたいな感じの問題)

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    int x1, y1;
    int x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;
    cout << ((abs(x1-x2)+abs(y1-y2))%2 ? "Caring Koala" : "Red Panda") << endl;
}

こういう系は結局座標の偶奇でどうにかなるよなーと思いながらガチャガチャしていた。証明は気が向いたら。

flagメモり忘れました。

upsolveしたい問題

algo/quickmaffs-permutation-puzzle [74 solves]

順列の中で、値が交互に増えたり減ったりする組み合わせを求める問題。

愚直解を作った後に OIES に投げてみたところ、見事マッチする数列を見つけることができた。ただ、これを効率の良いアルゴリズムで構築する方法がわからず、やむなくあきらめた。

Discussion