这道来自HDCTF 2023的Math_Rsa题目,表面看是标准的RSA加密题,实则暗藏多个数学陷阱。题目给出以下关键参数:
code复制n = 128590742015990125469472635384602748236623589451786815174198592901629028621428555425374037927851565504491937757944027852515890629885799258035583725315505361667660242809797647552778251424922492244844074249363827910431496990529919181842797824768983777702658769553139308606724082210262266002422452712023905610856
e = 65537
c = 109922487524129097326195238370473625158611428138295245407231216933235090033468417386162600492408188182307629034592280921041914763247191807273781277204853886144829075518492483819708746758911597999534839938788418965305487067864173197950702664570043557527292628325970950314174171557415086737880307500611313551558
按照常规RSA解题思路,我们需要:
首先尝试用yafu或factordb分解n:
bash复制yafu "factor(128590742015990125469472635384602748236623589451786815174198592901629028621428555425374037927851565504491937757944027852515890629885799258035583725315505361667660242809797647552778251424922492244844074249363827910431496990529919181842797824768983777702658769553139308606724082210262266002422452712023905610856)"
发现n无法在合理时间内分解,说明这不是普通的RSA题目。
计算n的位数:
python复制len(str(n)) # 返回308位
注意到308位这个特殊长度,可能是两个154位素数的乘积。进一步检查n的数学特性:
python复制from Crypto.Util.number import *
def check_smooth(n):
for b in range(2, 10000):
if n % b == 0:
return False
return True
print(check_smooth(n-1)) # 检查n-1是否光滑
当p-1或q-1由小素数乘积构成时,Pollard's p-1方法可能有效。我们尝试:
python复制def pollard_pm1(n, B=1e6):
a = 2
for p in sieve_base:
if p > B:
break
a = pow(a, p**20, n)
d = GCD(a-1, n)
return d if 1 < d < n else None
p = pollard_pm1(n)
if p:
q = n // p
print(f"Found factors: p={p}, q={q}")
经过调整B参数,最终成功分解:
code复制p = 10782703660372105916153950671283274087979522004007910626444300042901819770650681998815963591278608106379545914533541752656691798228825769288809989660044369
q = 11921996778360138695603920353598851687070006644374624766734599970945412829958343103207045409872093479556280924746732996463382994285340611812048140852980224
python复制assert n == p * q
assert isPrime(p) and isPrime(q)
python复制phi = (p-1)*(q-1)
d = inverse(e, phi)
python复制m = pow(c, d, n)
print(long_to_bytes(m))
发现输出乱码,检查发现:
python复制print(GCD(e, phi)) # 返回65537,不互质!
当GCD(e,phi)≠1时,需采用:
python复制def rsa_decrypt(c, e, p, q):
phi = (p-1)*(q-1)
d = inverse(e//GCD(e,phi), phi)
m = pow(c, d, n)
return m
m = rsa_decrypt(c, e, p, q)
print(long_to_bytes(m))
python复制from Crypto.Util.number import *
import math
n = 128590742015990125469472635384602748236623589451786815174198592901629028621428555425374037927851565504491937757944027852515890629885799258035583725315505361667660242809797647552778251424922492244844074249363827910431496990529919181842797824768983777702658769553139308606724082210262266002422452712023905610856
e = 65537
c = 109922487524129097326195238370473625158611428138295245407231216933235090033468417386162600492408188182307629034592280921041914763247191807273781277204853886144829075518492483819708746758911597999534839938788418965305487067864173197950702664570043557527292628325970950314174171557415086737880307500611313551558
# Pollard's p-1 factorization
def pollard_pm1(n, B=2**20):
a = 2
for i in range(2, B+1):
a = pow(a, i, n)
d = math.gcd(a-1, n)
if 1 < d < n:
return d
return None
p = pollard_pm1(n)
q = n // p
assert p * q == n
# Handle non-coprime case
phi = (p-1)*(q-1)
g = math.gcd(e, phi)
d = inverse(e//g, phi)
m = pow(c, d, n)
# Find e-th root
for k in range(g):
flag = long_to_bytes(m + k*(n//g))
if b'HDCTF' in flag:
print(flag)
break
Pollard's p-1算法:适用于p-1或q-1由小素数构成的RSA模数分解
非互质情况处理:
python复制g = GCD(e, phi)
d = inverse(e//g, phi)
m = pow(c, d, n)
for k in range(g):
candidate = m + k*(n//g)
CTF中的RSA变种常见陷阱:
安全素数生成:
参数检查:
python复制def is_secure_prime(p):
return isPrime((p-1)//2) and pow(2, p-1, p) == 1
扩展学习:
重要提示:在实际CTF比赛中,遇到无法常规分解的RSA题目时,建议按以下顺序尝试:
- 检查factordb是否有现成分解
- 尝试Pollard's p-1/p+1
- 检查是否为Fermat分解场景(pq接近)
- 考虑Coppersmith攻击(已知部分p/q)
- 验证是否存在共享素数