LinearCasino
加密代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| alarm(120) n, d1, d2 = 100, 60, 50 FLAG = "aliyunctf{REDACTED}" print("😊 LinearCasino is the Game 4 Super Guesser.") for _ in range(100): D1 = random_matrix(GF(2), d1, n) D2 = random_matrix(GF(2), d2, n) A = random_matrix(GF(2), d1+d2, d1+d2) B = [random_matrix(GF(2), d1+d2, 2*n), block_matrix([[D1, D1],[0, D2]])] C = Permutations(2*n).random_element().to_matrix() ct = [A*B[0]*C, A*B[1]*C] decision = randint(0,1) a = int(''.join(map(str, ct[decision].list())),2) print("🎩", int(''.join(map(str, ct[decision].list())),2)) assert input("🎲 ") == str(decision) print(f"🚩 Real Super Guesser! {FLAG}")
|
这题显然是要根据每次输出的ct来判断decision的值
注意到C的生成方式
1
| C = Permutations(2*n).random_element().to_matrix()
|
是一个置换矩阵,我们知道,置换矩阵是满足如下的性质的
C⋅CT=I
然后对于B,又有如下的特殊矩阵
[D10D1D2]
我们先利用上第一个性质,既然给了我们ABC,那么我们再乘上他的转置
A⋅B⋅C⋅CT⋅BT⋅AT=A⋅(B⋅BT)⋅AT
因为B这个矩阵比较特殊,我们尝试计算一下B⋅BT,关于块矩阵的转置和运算大家可以自己去了解一下
[D10D1D2]⋅[D1TD1T0D2T]=[2D1⋅D1TD2⋅D1TD1⋅D2TD2⋅D2T]
因为运算都是在GF(2)下运算的,所以2D1⋅D1T实际上就是0,也就是
B⋅BT=[0D2⋅D1TD1⋅D2TD2⋅D2T]
D1是一个60×100的矩阵,D2是一个50×100的矩阵,那么$D_2\cdot D_1^T $就是一个50×60的矩阵,意味着他的秩最多是五十,那么也就是说D2⋅D1T其实可以通过初等列变换,变成一个前五十列都是线性无关,后面十列都是线性相关的矩阵,又因为D2⋅D1T上面是0,那么对于整个B⋅BT来说,通过初等列变换之后,就一定有10列可以都是其他所有列线性相关的组合,也就是都可以是0向量,那么整个B⋅BT的秩最多就是100
虽然说我们只能得到A⋅(B⋅BT)⋅AT,但其实矩阵运算有如下的性质

也就是说,我们得到的A⋅(B⋅BT)⋅AT的秩最多也就是100,如果B不是那种特殊的矩阵,就不会有这个性质
用这个作为决策即可
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from tqdm import trange from pwn import *
io = remote('121.41.238.106', 60215)
io.recvline()
for _ in trange(100): a = int(io.recvline().replace(b'\xf0\x9f\x8e\xa9 ', b'').decode()) a = bin(a)[2::].zfill(110 * 200) ct = [] for i in range(0, 110 * 200, 200): ct.append(list(map(int, list(a[i:i + 200])))) ct = Matrix(GF(2), 110, 200, ct) test = ct * ct.T io.recv() if test.rank() <= 100: io.sendline(b'1') else: io.sendline(b'0') print(io.recvline())
|
大概一分钟能出