CTF密码学实战:手把手教你用Python解一道RSA题(附完整脚本)
CTF密码学实战:从数学推导到Python脚本破解RSA
第一次参加CTF比赛时,我盯着那道RSA密码题整整两小时毫无头绪。直到一位资深选手拍了拍我肩膀说:"CTF中的RSA从来不是考加密算法本身,而是考察你如何从看似杂乱的信息中重建数学关系。"这句话彻底改变了我解题的思路。今天,我们就以一道典型CTF题为例,完整展示从题目分析到脚本编写的全流程思维。
1. 题目分析与数学关系拆解
拿到题目后,我们首先看到三个关键输出:密文c、神秘参数z和模数n。根据题目描述:
p=getPrime(1024)
q=getPrime(1024)
n=p*q
z=Fraction(1,Derivative(arctan(p),p))-Fraction(1,Derivative(arth(q),q))
关键突破点 在于理解z的数学含义。通过查阅sympy文档和数学手册,我们发现:
Derivative(arctan(p),p)实际上是求arctan(p)对p的导数,即1/(1+p²)Derivative(arth(q),q)是反双曲正切函数的导数,等于1/(1-q²)- 因此z可以简化为:z = (1+p²) - (1-q²) = p² + q²
这个转换将看似复杂的符号计算简化为清晰的数学关系。现在我们有:
n = p × q
z = p² + q²
2. 从z和n推导p、q的数学方法
有了这两个方程,我们可以构建一个二元二次方程组来解出p和q。具体推导过程如下:
- 我们知道 (p + q)² = p² + q² + 2pq = z + 2n
- 同理 (p - q)² = p² + q² - 2pq = z - 2n
- 因此可以得到:
p_plus_q = isqrt(z + 2*n) # 整数平方根 p_minus_q = isqrt(z - 2*n) # 整数平方根 - 最后解出:
p = (p_plus_q + p_minus_q) // 2 q = (p_plus_q - p_minus_q) // 2
这里需要注意 大整数平方根计算 的精度问题。普通Python的math.sqrt对超大整数会有精度损失,必须使用gmpy2库的iroot函数:
from gmpy2 import iroot
p_plus_q, exact = iroot(z + 2*n, 2)
if not exact:
print("Warning: 可能不是完全平方数")
3. 完整Python解密脚本实现
结合以上分析,我们可以编写完整的解密脚本。这里使用Python的Crypto库和gmpy2库:
from Crypto.Util.number import long_to_bytes
from gmpy2 import iroot, invert
# 题目给出的数据
c = 7922547866857761459807491502654216283012776177789511549350672958101810281348402284098310147796549430689253803510994877420135537268549410652654479620858691324110367182025648788407041599943091386227543182157746202947099572389676084392706406084307657000104665696654409155006313203957292885743791715198781974205578654792123191584957665293208390453748369182333152809882312453359706147808198922916762773721726681588977103877454119043744889164529383188077499194932909643918696646876907327364751380953182517883134591810800848971719184808713694342985458103006676013451912221080252735948993692674899399826084848622145815461035
z = 32115748677623209667471622872185275070257924766015020072805267359839059393284316595882933372289732127274076434587519333300142473010344694803885168557548801202495933226215437763329280242113556524498457559562872900811602056944423967403777623306961880757613246328729616643032628964072931272085866928045973799374711846825157781056965164178505232524245809179235607571567174228822561697888645968559343608375331988097157145264357626738141646556353500994924115875748198318036296898604097000938272195903056733565880150540275369239637793975923329598716003350308259321436752579291000355560431542229699759955141152914708362494482
n = 15310745161336895413406690009324766200789179248896951942047235448901612351128459309145825547569298479821101249094161867207686537607047447968708758990950136380924747359052570549594098569970632854351825950729752563502284849263730127586382522703959893392329333760927637353052250274195821469023401443841395096410231843592101426591882573405934188675124326997277775238287928403743324297705151732524641213516306585297722190780088180705070359469719869343939106529204798285957516860774384001892777525916167743272419958572055332232056095979448155082465977781482598371994798871917514767508394730447974770329967681767625495394441
e = 65537
# 计算p和q
p_plus_q, _ = iroot(z + 2*n, 2)
p_minus_q, _ = iroot(z - 2*n, 2)
p = (p_plus_q + p_minus_q) // 2
q = (p_plus_q - p_minus_q) // 2
# 验证计算是否正确
assert p * q == n, "p和q计算有误!"
# 标准RSA解密流程
phi = (p-1)*(q-1)
d = invert(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print(flag.decode())
4. 实战中的常见问题与调试技巧
在实际解题过程中,可能会遇到各种意外情况。以下是几个常见问题及解决方法:
问题1:平方根计算结果不是整数
p_plus_q, exact = iroot(z + 2*n, 2)
if not exact:
# 检查z的计算是否正确
# 可能需要重新审视题目给出的z表达式
问题2:解出的p和q乘积不等于n
if p * q != n:
# 可能是计算过程中整数除法的问题
# 尝试调整计算顺序或检查中间变量类型
# 有时需要+1或-1微调
for delta in [-2, -1, 0, 1, 2]:
p_candidate = (p_plus_q + p_minus_q + delta) // 2
if n % p_candidate == 0:
p = p_candidate
q = n // p
break
问题3:解密后的flag乱码
可能原因及解决方法:
- 字节序问题 :尝试不同的编码方式
print(flag.decode('utf-8', errors='ignore')) - 解密错误 :检查d的计算是否正确
assert (e * d) % phi == 1, "d值计算有误" - 数据截断 :可能需要处理填充字节
flag = flag.replace(b'\x00', b'')
5. CTF密码学进阶技巧
掌握了基础解法后,我们可以进一步优化解题流程:
技巧1:自动化数学关系识别
对于复杂的数学表达式,可以使用SymPy进行符号计算:
from sympy import symbols, diff, atan, atanh
p, q = symbols('p q')
expr1 = 1/diff(atan(p), p)
expr2 = 1/diff(atanh(q), q)
z_simplified = expr1 - expr2 # 输出p**2 + q**2
技巧2:多线程爆破辅助
当部分参数不确定时,可以结合爆破:
from concurrent.futures import ThreadPoolExecutor
def try_solve(delta):
p_candidate = (p_plus_q + p_minus_q + delta) // 2
if n % p_candidate == 0:
return p_candidate
with ThreadPoolExecutor() as executor:
results = executor.map(try_solve, range(-10, 10))
p = next(r for r in results if r is not None)
技巧3:RSA参数快速验证
建立一套参数检查流程:
def validate_rsa_params(p, q, n, e, d=None):
checks = [
('n == p*q', n == p*q),
('p是素数', is_prime(p)),
('q是素数', is_prime(q)),
('e与phi互质', gcd(e, (p-1)*(q-1)) == 1)
]
if d is not None:
checks.append(('d有效', (e*d) % ((p-1)*(q-1)) == 1))
for name, result in checks:
print(f"{name}: {'✓' if result else '✗'}")
6. 密码学题目资源与训练建议
想要在CTF密码学领域快速提升,需要系统的训练方法:
推荐学习路径
-
基础理论 :
- 《应用密码学》Bruce Schneier
- Cryptopals挑战题(https://cryptopals.com/)
-
CTF专项训练 :
- CTFtime上的RSA分类题目
- PicoCTF的密码学题库
-
工具掌握 :
# 常用工具列表 pip install gmpy2 pycryptodome sage sympy
训练方法建议
- 每周至少解3道中等难度密码题
- 建立自己的密码学代码片段库
- 参加线上CTF比赛实战演练
- 复盘每道题的至少两种解法
典型题目特征分析
| 题目类型 | 关键特征 | 解题思路 |
|---|---|---|
| 基础RSA | 给出p,q,e,c | 直接计算私钥d解密 |
| 参数关系 | 给出n和p,q的关系式 | 建立方程求解 |
| 小指数攻击 | e很小(如3) | 直接开方或Coppersmith |
| 共模攻击 | 相同n不同e | 扩展欧几里得算法 |
| 选择密文 | 可查询解密oracle | 利用乘法性质攻击 |
在CTF比赛中,密码学题目往往是最容易"一分耕耘一分收获"的领域。通过系统性的数学训练和编程实践,大多数RSA相关问题都能在30分钟内解决。记住,关键在于培养从复杂表达式中识别数学关系的能力,这比记忆特定攻击方法更重要。
更多推荐
所有评论(0)