题目内容: Let me tell you a story about zkRSA. 附件下载 容...
Creato il: 1 novembre 2026
Creato il: 1 novembre 2026
题目内容:
Let me tell you a story about zkRSA.
附件下载
容器地址: nc 39.106.39.224 22612。给我flag
这道 zkRSA 是一道密码学 CTF 题。根据附件 task.py,可以确定存在一个关键漏洞:
GQProof.verify() 没有重新计算 Fiat–Shamir 哈希挑战值,而是直接信任传入的 chal。
另外,Verify 分支中的挑战值来自:
因此,攻击重点是预测或利用椭圆曲线伪随机数生成器 ECGenerator,并构造满足以下条件的证明:
只有服务端输出 Good job! Here is your flag:,才能确认拿到了真实 flag。
目前还没拿到真实 flag。
我尝试连接题目服务:
39.106.39.224:22612
但返回了 ConnectionRefusedError: [Errno 111] Connection refused,说明当前无法建立连接。
附件中的 flag{test_flag} 只是本地测试占位符,不是真实答案。
请确认题目容器仍在运行,或重新启动容器并把新的 IP:端口 发给我。我可以继续分析利用方法并尝试获取真实 flag。
可以连上,能连上应该怎么做
既然你本地可以连接,那就可以继续做这道 zkRSA。
根据 task.py,不需要直接分解 512 位 RSA 模数。更值得尝试的方向是利用 P 接口泄露的信息,结合 V 接口的验证逻辑。
先确认服务端交互:
连接后会先出现 PoW:
需要爆破前四个字符,不能随便输入。
通过 PoW 后,会出现:
这里有两个关键点:
P 会返回 (com, chal, res),其中 com = r² mod n,r 来自椭圆曲线生成器。V 会泄露 y = FLAG^x mod n,然后要求提交 (com, res),但挑战值 chal 由服务端生成。目前从我的执行环境连接仍然返回 ConnectionRefusedError,但你本地能连接的话,可以先运行下面这个脚本自动完成 PoW,并收集 P 接口输出。
保存为 solve.py:
安装依赖并运行:
注意:这只是第一阶段的数据采集脚本,不是最终的 flag 利用脚本。 还需要研究如何从生成器输出中恢复状态或构造可通过验证的证明。特别是 chal = next(gen) % x,不能简单假设挑战值为 0。
把运行得到的 proofs.txt 或终端输出发给我,我就可以继续分析下一阶段的利用。
+] Opening connection to 39.106.39.224 on port 22612: Done
[] PoW line: sha256(XXXX + zZ4CzxNan5ivjzYj) == 948f30fadccae77fa41e9d9f9f1bb0328ef31a55cd3a9859854a838137ef2f70
[] suffix=zZ4CzxNan5ivjzYj, target=948f30fadccae77fa41e9d9f9f1bb0328ef31a55cd3a9859854a838137ef2f70
[*] 开始爆破PoW...
[+] PoW ok: LCdK。prefix = "zf6D"。我得到了这些
收到,你已经成功连接并完成 PoW,而且上传的 proofs.txt 包含 20 组证明数据。
不过,你的日志里有一个需要注意的地方:
这两个前缀不一致。对于同一道 PoW,真正有效的前缀必须满足:
既然已经进入菜单并成功获取 20 组证明,说明至少有一次 PoW 验证成功。
更重要的是,你上传的数据中,chal 都是 0 或 1,这与源码中的 e = 2 一致。接下来可以从这些证明中恢复 RSA 模数 n。
已经从你的数据中恢复出 RSA 模数 n:
8240715461965143246624188112668693682213807472237215000622262576542930609117445578979592671196959644578894998611919715592152739539050859468819390848855081
[复制 n]
这是利用 chal = 0 时的关系:
计算多个 res² - com 的最大公约数得到的。
下一阶段需要解决的是预测 V 分支的挑战值,或者找到不需要预测挑战值的伪造方法。 目前还不能保证直接提交某组 (com, res) 就能得到 flag。
另外,P 和 V 必须在同一条连接中进行,因为每次重新连接都会生成新的 n、x 和椭圆曲线状态。你上传的这些证明可以用于离线研究,但不能直接复用于新连接。
目前已经完成第一步模数恢复,还没有真实 flag。
好,持续进行,拿到flag
我们继续。现在已经确认两个重要结果:
n:已通过 14 组 chal=0 的证明恢复,512 bit。x² mod n:已通过 chal=1 的证明恢复。下一步重点是恢复椭圆曲线生成器的状态,从而预测 V 分支的挑战值。
请在一个新的连接中完成 PoW,然后连续执行 200 次 P,不要中途断开。
上传生成的 proofs200.txt。
另外,暂时不要执行 V,也不要关闭连接。
我们需要解决三个问题:
x 的候选值。A 和增量点 Ds。V 的 chal,构造满足验证等式的 res。最终目标是:
目前还没有拿到真实 flag,但 RSA 模数恢复已经完成。 接下来重点就是生成器状态恢复。
┌──(root㉿kali)-[~/桌面]
└─# python3 '/root/桌面/exp.py'
[+] Opening connection to 39.106.39.224 on port 22612: Done
sha256(XXXX + Jy0ybAADx8RCvx0j) == 1e20331a2c5e93b84be29880707c1e449168828dd8a1acec9b5fc8e386b29077
[+] PoW: SeCx
[+] collected 0
[+] collected 10
[+] collected 20
[+] collected 30
[+] collected 40
[+] collected 50
[+] collected 60
[+] collected 70
[+] collected 80
[+] collected 90
[+] collected 100
[+] collected 110
[+] collected 120
[+] collected 130
[+] collected 140
[+] collected 150
[+] collected 160
[+] collected 170
[+] collected 180
[+] collected 190
[+] Saved proofs200.txt
[*] Switching to interactive mode
==========
[P]rove
[V]erify
[Q]uit
很好!(200 组证明采集成功)
而且最重要的是:你现在还停留在 io.interactive(),连接没有主动关闭。
先不要输入 v 或 q,保持这个连接打开。
请把 Kali 中生成的 proofs200.txt 上传到这里:
我接下来会做以下分析:
chal=0 样本的 GCD,恢复当前连接的 RSA 模数。chal=1 样本恢复 。V 所需的 com 和 res。注意:这 200 组数据必须和当前保持打开的连接对应,不能重新连接后混用。
把文件上传,我继续做实际求解。
好了
已经收到 proofs200.txt,我对 200 组数据做了实际计算。
(数据分析完成)
注意:这批数据来自新的连接,因此必须使用这批数据自己的模数,不能沿用之前 20 组数据的模数。
计算结果:
| 参数 | 结果 |
|---|---|
| 证明数量 | 200 |
chal=0 | 104 |
chal=1 | 96 |
| RSA 模数长度 | 511 bit |
x² mod n | 已恢复 |
当前模数:
恢复的平方:
96 组 chal=1 的数据全部得到相同的平方结果,交叉验证通过。
目前还没有恢复:
Ds。V 调用使用的生成器输出。这意味着现在直接提交 V,仍然有很大概率失败,而且失败会退出程序。
你运行 exp.py 的那个 Kali 终端,现在是否还停留在下面这个界面?
如果还在,请不要关闭终端,也不要输入 V。
我会以当前连接的参数为准继续分析,避免前面不同连接的数据混用。目标仍然是拿到真实的 flag{...}。
给我提供在新设备上继续进行拿到缺失地方的方法及脚本
可以。这次我给你一套可以在新设备直接运行的脚本,而不只是操作步骤。
脚本会完成 PoW、连续采集证明、恢复当前连接的 n 和 、校验结果,并保存会话数据。它还会保留与服务器的交互连接,方便后续尝试攻击。
需要明确一点:现有证明只能恢复 ,不能直接恢复 。而且新设备重新连接后,旧连接的随机状态无法继承。因此,脚本会从新会话重新采集,不会错误地混用旧参数。
zk_collect.py
已生成并通过 Python 语法检查
适用于 Kali Linux、Ubuntu 或其他安装了 Python 3 的 Linux 系统。
将脚本放到当前目录,然后运行:
它会自动连接:
PoW 搜索最多需要尝试 个候选,因此可能需要一些时间。
成功采集后,目录结构为:
parameters.json 包含当前连接恢复的模数、平方值、证明数量和一致性验证结果。
如果只想分析已经采集的旧文件,不需要连接服务器:
| 目标 | 现有方法 | 是否已解决 |
|---|---|---|
恢复 n | 对 chal=0 的证明计算 GCD | |
| 恢复 | 使用 chal=1 的证明 | |
恢复 x | 需要新的密码分析突破 | |
| 恢复椭圆曲线状态 | 需要重建曲线与 16 个增量点 | |
构造 V 的有效证明 | 依赖进一步的攻击结果 | |
| 获得 flag | 尚未完成 |
还有一个重要的数学限制:对于 chal=1 的证明,我们知道
这能验证 ,但不能直接求出 x。即使恢复了椭圆曲线的下一个输出,也不等于自动获得合法的 res。
当前脚本是经过语法检查的采集与分析工具,尚未经过远程服务器端到端验证,也不是完整出 flag 的 EXP。
运行脚本并保持交互连接。如果程序正常输出 validated: true,说明当前会话的 RSA 参数已完成交叉验证。
然后把 parameters.json 和 proofs.txt 上传到对话中。下一阶段需要针对 x 的恢复和 V 的证明构造寻找真正可行的攻击,而不是盲目提交导致连接退出。