from pwn import * from hashlib import sha1 from sage.allimport * import re
# 橢圓曲線參數 (secp256k1) p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f a_curve = 0 b_curve = 7 n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
# LCG 參數 a = 0x5deece66d c = 0xb
defget_example_signature(conn): """獲取示例簽名""" conn.sendline(b'1') response = conn.recvuntil(b'Choice: ').decode() # 解析 r 和 s 值 r_match = re.search(r'r=0x([0-9a-f]+)', response) s_match = re.search(r's=0x([0-9a-f]+)', response) if r_match and s_match: r = int(r_match.group(1), 16) s = int(s_match.group(1), 16) print(f"[*] Received example: msg='example_msg', r=0x{r:x}, s=0x{s:x}") return {'r': r, 's': s} else: raise ValueError("無法解析簽名")
defsolve_for_private_key(sig1, sig2, h, n, a, c): """求解私鑰""" r1, s1 = sig1['r'], sig1['s'] r2, s2 = sig2['r'], sig2['s'] # 建立線性同餘方程求解私鑰 # s2^-1 * (h + r2 * d) ≡ a * s1^-1 * (h + r1 * d) + c (mod n) s1_inv = pow(s1, -1, n) s2_inv = pow(s2, -1, n) # 重新整理方程式 # s2_inv * h + s2_inv * r2 * d ≡ a * s1_inv * h + a * s1_inv * r1 * d + c (mod n) # (s2_inv * r2 - a * s1_inv * r1) * d ≡ a * s1_inv * h - s2_inv * h + c (mod n) coeff_d = (s2_inv * r2 - a * s1_inv * r1) % n rhs = (a * s1_inv * h - s2_inv * h + c) % n # 求解 d coeff_d_inv = pow(coeff_d, -1, n) d = (coeff_d_inv * rhs) % n return d
defpredict_next_k(sk, sig1, h, n, a, c): """預測下一個隨機數""" r1, s1 = sig1['r'], sig1['s'] # 計算實際的 k1 s1_inv = pow(s1, -1, n) k1_actual = (s1_inv * (h + r1 * sk)) % n print(f"[*] Calculated k1_actual: 0x{k1_actual:x}") # 計算 k2 k2_actual = (a * k1_actual + c) % n # 預測 k3 k3_for_flag = (a * k2_actual + c) % n print(f"[*] Predicted k3 for flag signing: 0x{k3_for_flag:x}") return k3_for_flag
defforge_signature(k, h, sk, G, n): """偽造簽名""" # 計算 r point = k * G r = point.xy()[0] % n # 計算 s k_inv = pow(k, -1, n) s = (k_inv * (h + r * sk)) % n return r, s
defmain(): # 連接到服務器 print("[+] Opening connection to chals1.ais3.org on port 19000: Done") conn = remote('chals1.ais3.org', 19000) # 跳過初始訊息 conn.recvuntil(b'Choice: ') # 獲取第一個示例簽名 print("[*] Getting first example signature...") example1 = get_example_signature(conn) # 獲取第二個示例簽名 print("[*] Getting second example signature...") example2 = get_example_signature(conn) # 建立橢圓曲線 E = EllipticCurve(GF(p), [a_curve, b_curve]) G = E(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8) # 計算 example_msg 的哈希 h_ex = int.from_bytes(sha1(b"example_msg").digest(), 'big') print(f"[*] h_ex (for 'example_msg') = 0x{h_ex:x}") # 求解私鑰 sk = solve_for_private_key(example1, example2, h_ex, n, a, c) print(f"[+] Recovered private key (sk): 0x{sk:x}") # 預測下一個隨機數 k3_predicted = predict_next_k(sk, example1, h_ex, n, a, c) # 計算 give_me_flag 的哈希 h_flag = int.from_bytes(sha1(b"give_me_flag").digest(), 'big') print(f"[*] h_flag (for 'give_me_flag') = 0x{h_flag:x}") # 偽造簽名 r_flag, s_flag = forge_signature(k3_predicted, h_flag, sk, G, n) print(f"[+] Forged signature for 'give_me_flag': r=0x{r_flag:x}, s=0x{s_flag:x}") # 提交偽造的簽名 print("[*] Sending forged signature to verify...") conn.sendline(b'2') conn.sendline(hex(r_flag)[2:].encode()) conn.sendline(hex(s_flag)[2:].encode()) # 接收回應 print("[*] Server response:") response = conn.recvall().decode() print(response) # 關閉連接 conn.close()
defread_input(filename="output.txt"): withopen(filename, "r") as f: lines = f.read().strip().splitlines() n = int(lines[0].split(" = ")[1]) e = int(lines[1].split(" = ")[1]) c = int(lines[2].split(" = ")[1]) return n, e, c
defgenerate_prime_list(): return [p for p inrange(3, 5000) if gmpy2.is_prime(p)]
defpollard_p_minus_1(n, prime_list): a = gmpy2.mpz(2) for _ inrange(86): a = gmpy2.powmod(a, 2, n) for p in prime_list: for _ inrange(85): a = gmpy2.powmod(a, p, n) return gmpy2.gcd(a - 1, n)
defextract_factor(gcd_val, n, expected_power=1): if gcd_val in [1, n]: returnNone if gmpy2.is_prime(gcd_val): returnint(gcd_val) temp = gcd_val factors = [] for p in [2] + generate_prime_list(): while temp % p == 0: factors.append(p) temp //= p if temp == 1: break if temp > 1and gmpy2.is_prime(temp): factors.append(int(temp)) counts = Counter(factors) for f, count in counts.items(): if count >= expected_power and gmpy2.is_prime(f): return f returnNone
deflucas_V(k, P, N): if k == 0: return2 if k == 1: return P V0, V1 = 2, P for bit inbin(k)[3:]: if bit == "0": V1 = (V0 * V1 - P) % N V0 = (V0 * V0 - 2) % N else: V0 = (V0 * V1 - P) % N V1 = (V1 * V1 - 2) % N return V1
defwilliams_p_plus_1(n, prime_list, P=3): V = P for _ inrange(86): V = lucas_V(2, V, n) for p in prime_list: for _ inrange(85): V = lucas_V(p, V, n) return gmpy2.gcd(V - 2, n)
deffermat(n, max_iter=200_000_000): a = gmpy2.isqrt(n) + 1 for i inrange(max_iter): b2 = a * a - n b = gmpy2.isqrt(b2) if b * b == b2: returnint(a + b), int(a - b) if i % 1_000_000 == 0and i > 0: print(f" [Fermat] 嘗試 {i:,} 次...") a += 1 returnNone, None
defrsa_decrypt(n, e, c, po, wi, fp, fq): phi = po * (po - 1) * (wi - 1) * (fp - 1) * (fq - 1) d = gmpy2.invert(e, phi) m = gmpy2.powmod(c, d, n) flag = int(m).to_bytes((m.bit_length() + 7) // 8, 'big').decode() return phi, d, m, flag
defmain(): print("[*] 讀取 RSA 公開參數") n, e, c = read_input() primes = generate_prime_list()
print("[*] Step 1: Pollard p-1") g = pollard_p_minus_1(n, primes) po = extract_factor(g, n, expected_power=2) ifnot po: raise Exception("找不到 po") n1 = n // (po * po)
print("[*] Step 2: Williams p+1 or fallback") wi = None for base in [3, 5, 7, 11, 13, 17, 19, 23]: g = williams_p_plus_1(n1, primes, base) candidate = extract_factor(g, n1) if candidate and gmpy2.is_prime(candidate): wi = candidate break ifnot wi: print("[!] Williams p+1 失敗,改用 Fermat") f1, f2 = fermat(n1) if f1 and gmpy2.is_prime(f1): wi, n2 = f1, f2 elif f2 and gmpy2.is_prime(f2): wi, n2 = f2, f1 else: raise Exception("找不到 wi") else: n2 = n1 // wi