
1. 加密流程服务器会运行 5 轮每轮随机生成initial4 字节和n1 字节用它们构造一个RandomFunction实例RF随机生成 8 字节的nonce用n的 2 字节表示作为 AES 密钥加密 flag输出密文和 nonce允许你查询RF最多 2048 次你给 4 字节输入它返回 4 字节输出查询结束后服务器自己计算 2048 次RF(nonce[4:])nonce 的后 4 字节拼接后做 SHA256 得到KEY用这个KEY解密 flag并检查是否以crypto{开头2. 关键点我们不知道initial和n我们知道nonce8 字节所以知道nonce[4:]我们可以查询RF2048 次我们需要预测RF(nonce[4:])的 2048 次输出才能得到正确的 KEY从而解密 flag解题思路核心问题RF是一个4 字节到 4 字节的置换输入输出空间大小 232232但我们只能查询 2048 次远不足以构建完整的输入输出表。所以需要利用RF的内部结构。RandomFunction 的结构pythondef __call__(self, inp): u inp for _ in range(8): u ^ self.coef1 u helper_cbrt(u ^ self.coef2, 4294967291) u ^ self.coef1 u (u self.coef2) % 4294967291 return u.to_bytes(4, big)每一轮做的是异或coef1异或coef2后求模 p 下的立方根异或coef1加coef2模 p重复 8 轮。其中coef1和coef2是由initial和n以及内部打乱后的数组A计算出来的与输入无关是固定参数。攻击思路由于输入只有 4 字节而我们有 2048 次查询机会一个可行的方向是尝试恢复coef1和coef2如果我们能通过查询推断出这两个参数就可以在本地模拟RF从而计算任意输入的输出。2048 次查询相对于 32 位空间虽然小但如果设计巧妙的差分查询或利用立方根的代数性质有可能恢复参数。直接查询nonce[4:]我们已知 nonce所以知道nonce[4:]的值。可以在 2048 次查询中直接查询nonce[4:]得到一个输出RF(nonce[4:])。但问题是服务器需要的是2048 次连续的RF(nonce[4:])即每次都用上一次的输出作为下一次的输入textRF(nonce[4:]), RF(RF(nonce[4:])), RF(RF(RF(nonce[4:]))), ...所以仅仅知道第一个输出不够我们需要能继续计算后面的。如果能恢复参数就可以本地推算整个链。利用立方根的弱点质数p 4294967291p ≡ 2 mod 3所以立方根是唯一的并且计算公式为pow(a, (2*p-1)//3, p)。这本身是一个双射。但如果我们能找到一个输入使得立方根计算表现出某种线性或差分特征可能有助于恢复参数。实际操作步骤伪代码pythonfrom pwn import remote r remote(archive.cryptohack.org, 35802) for round in range(5): # 接收 enc 和 nonce enc bytes.fromhex(r.recvline().split()[1].strip()) nonce bytes.fromhex(r.recvline().split()[1].strip()) target_input nonce[4:] # 这就是我们需要计算 RF 链的起点 # 策略用 2048 次查询尽量恢复 coef1 和 coef2 # 例如查询 target_input 本身、全 0、全 0xff 等特殊值 queries [target_input, b\x00*4, b\xff*4, ...] outputs {} for q in queries[:2048]: r.sendline(q.hex()) out bytes.fromhex(r.recvline()) outputs[q] out # 用收集到的输入输出对尝试解出 coef1、coef2 # 这一步是难点需要利用立方根的代数关系 coef1, coef2 recover_params(outputs) # 在本地模拟 RF 链 local_rf RandomFunction_reconstructed(coef1, coef2) KEY_material b u target_input for _ in range(2048): KEY_material local_rf(u) u local_rf(u) KEY hashlib.sha256(KEY_material).digest() # 解密 flag flag AES.new(KEY, AES.MODE_CTR, noncenonce).decrypt(enc) print(flag)总结这道题的难点在于如何在 2048 次查询内恢复coef1和coef2从而在本地模拟RF链。一旦恢复了这两个参数就可以计算 2048 次RF(nonce[4:])的连续输出拼接后 SHA256 得到 KEY解密拿到 flag。最终的 flag 格式是crypto{...}。如果你能连接到服务器并拿到具体的交互数据我可以帮你进一步分析如何从查询结果中提取参数。