vsCTF 2024 writeup
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はメルセンヌツイスターが使われている。
今回の場合は、一度に
ただ、実験していて気付いたが、今回の場合もシード値が実質固定されている(何回も同じ要素を確認してみるとわかる)ため、
シード値の特定計算はこのサイトを利用した。
乱数のシード値特定
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