附件15、ECDSA nonce 重用 题目内容:我们截获了某平台签发的 10 组 ECDSA 签名...
Создано: 30 мая 2026 г.
Отвечено с использованием GPT-5.5 Thinking от Chat01
Создано: 30 мая 2026 г.
Отвечено с использованием GPT-5.5 Thinking от Chat01
附件15、ECDSA nonce 重用
题目内容:我们截获了某平台签发的 10 组 ECDSA 签名数据,每组包含: 同一个公钥 两条不同消息及其对应的 ECDSA 签名 安全研究员发现,该平台在签名时错误地重复使用了相同的随机数(nonce)k。 请利用这一漏洞,恢复出原始私钥,并提交对应的 flag。 每个压缩包 otp_XX.zip 中包含一个 challenge.json 文件,格式如下: { "public_key_x": "...", "public_key_y": "...", "message1": "57656c636f6d6520746f2074686520435446206368616c6c656e676521", "message2": "506c65617365207265636f766572207468652073656372657420666c61672e", "signature1_r": 123456..., "signature1_s": 789012..., "signature2_r": 123456..., // 注意:与 signature1_r 相同! "signature2_s": 345678..., "curve": "SECP256k1" } Flag 格式:flag{ecdsa_nonce_reuse_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
题目难度:中级
题目分类:CRYPTO
题目分值:300
题目附件:
这是ctf的比赛题帮我解出flag
pythonimport json, hashlib, os, math path="/mnt/data/challenge.json" with open(path,'r') as f: data=json.load(f) data {'public_key_x': 82937147571408969267139200041203360936951744683871440166987788062427108593019, 'public_key_y': 15586372809254615553254077057166913120384173467039862910897487982227597191402, 'message1': '57656c636f6d6520746f2074686520435446206368616c6c656e676521', 'message2': '506c65617365207265636f766572207468652073656372657420666c61672e', 'signature1_r': 79013718241246135302610197377430012073343423894519665480327871129212060301075, 'signature1_s': 103286208825942613961036297876825547961346350046619406322280179767228016529778, 'signature2_r': 79013718241246135302610197377430012073343423894519665480327871129212060301075, 'signature2_s': 79104101230423979234833091375845809917052647390793147016296814906477227009899, 'curve': 'SECP256k1'}
python# secp256k1 parameters p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 r=int(data["signature1_r"]); s1=int(data["signature1_s"]); s2=int(data["signature2_s"]) m1=bytes.fromhex(data["message1"]); m2=bytes.fromhex(data["message2"]) def inv(a, mod): return pow(a % mod, -1, mod) def recover(z1,z2): k=((z1-z2)*inv(s1-s2,n))%n d=((s1*k-z1)*inv(r,n))%n return k,d z1_sha=int.from_bytes(hashlib.sha256(m1).digest(),'big') z2_sha=int.from_bytes(hashlib.sha256(m2).digest(),'big') k,d=recover(z1_sha,z2_sha) hex(d), k ('0xa46b8f59aac5f0d5f1e661827c1cf3a7f536c9ce5a5d2452942215f16480d48b', 64332719384171170839195860344881498998618794359228425336341326330953805041099)
python# Implement scalar multiplication to verify Q = dG O=None def point_add(P,Q): if P is None: return Q if Q is None: return P x1,y1=P; x2,y2=Q if x1==x2 and (y1+y2)%p==0: return None if P==Q: lam=(3*x1*x1)*inv(2*y1,p)%p else: lam=(y2-y1)*inv(x2-x1,p)%p x3=(lam*lam-x1-x2)%p y3=(lam*(x1-x3)-y1)%p return (x3,y3) def scalar_mult(k,P): R=None Q=P while k: if k&1: R=point_add(R,Q) Q=point_add(Q,Q) k//=2 return R Q=scalar_mult(d,(Gx,Gy)) Q, Q==(int(data["public_key_x"]), int(data["public_key_y"])) ((82937147571408969267139200041203360936951744683871440166987788062427108593019, 15586372809254615553254077057166913120384173467039862910897487982227597191402), True)
pythond_hex=f"{d:064x}" k_hex=f"{k:064x}" variants = { "d_hex": d_hex, "d_first32": d_hex[:32], "d_last32": d_hex[-32:], "md5_decimal_d": hashlib.md5(str(d).encode()).hexdigest(), "md5_hex_d_lower64": hashlib.md5(d_hex.encode()).hexdigest(), "md5_0xhex_d": hashlib.md5(hex(d).encode()).hexdigest(), "sha256_decimal_d_first32": hashlib.sha256(str(d).encode()).hexdigest()[:32], "sha256_hex_d_first32": hashlib.sha256(d_hex.encode()).hexdigest()[:32], "k_hex": k_hex, "k_first32": k_hex[:32], "k_last32": k_hex[-32:], } variants {'d_hex': 'a46b8f59aac5f0d5f1e661827c1cf3a7f536c9ce5a5d2452942215f16480d48b', 'd_first32': 'a46b8f59aac5f0d5f1e661827c1cf3a7', 'd_last32': 'f536c9ce5a5d2452942215f16480d48b', 'md5_decimal_d': '8a60068a37c31f2eb87dee8732c03827', 'md5_hex_d_lower64': '0039fe0b1b204bd13bc8c15604a9f01e', 'md5_0xhex_d': '45edfd5a18c2849ab38f37c8fc89f612', 'sha256_decimal_d_first32': '39a366e9585bdf6df1d269222b78ab01', 'sha256_hex_d_first32': '10b45e439a6921a0c72ebebc6034e497', 'k_hex': '8e3b07604d5a19975250e8aeb7314ee4ed64a10606fcf9e6dc5c962ad7f78dcb', 'k_first32': '8e3b07604d5a19975250e8aeb7314ee4', 'k_last32': 'ed64a10606fcf9e6dc5c962ad7f78dcb'}
pythonvariants.update({ "md5_d_bytes": hashlib.md5(d.to_bytes(32,'big')).hexdigest(), "sha256_d_bytes_first32": hashlib.sha256(d.to_bytes(32,'big')).hexdigest()[:32], "ripemd?": "" }) variants["md5_d_bytes"], variants["sha256_d_bytes_first32"] ('92ee0425d84421a483fded24cb65b627', '9d26310cc04df429c2e9e886d1974a51')
附件中两条签名的 r 完全相同,确认是 ECDSA nonce k 重用。按 SECP256k1 和 SHA-256(message) 计算后,恢复出的私钥为:
texta46b8f59aac5f0d5f1e661827c1cf3a7f536c9ce5a5d2452942215f16480d48b
我用该私钥点乘基点后,得到的公钥坐标与附件里的 public_key_x/public_key_y 完全一致,说明私钥正确。(challenge.json)
按题面给出的 32 位后缀格式,flag 应为:
textflag{ecdsa_nonce_reuse_a46b8f59aac5f0d5f1e661827c1cf3a7}
如果提交系统要求完整私钥后缀,则提交这个备选:
textflag{ecdsa_nonce_reuse_a46b8f59aac5f0d5f1e661827c1cf3a7f536c9ce5a5d2452942215f16480d48b}
附件9、ScatterRSA
题目内容:同一份秘密被分散到了三个不同的信道中传输,每个信道都加了不同的噪声……但真的安全吗?
题目难度:中级
题目分类:CRYPTO
题目分值:300
这是一个ctf题,帮我解出flag
解出来了,flag 是:
textflag{82a05145b36137b03a83e7f0ff974472}
依据附件代码,明文 m 被做了线性变换后用 e = 3 加密:c = (a*m + b)^3 mod n,并且同一份 flag 被发到 3 个不同 RSA 模数下。(task.py) 输出文件给出了三组 (n, a, b, c)。(output.txt)
攻击方式是 Hastad 广播攻击的线性填充版本:构造
text(a_i*x + b_i)^3 - c_i ≡ 0 mod n_i
再用 CRT 合成一个模 N = n1*n2*n3 的三次多项式,对其做 Coppersmith 小根求解,得到明文整数,转 bytes 后就是上面的 flag。