别光会做题:从Bugku RSA题出发,聊聊CTF中RSA的常见‘套路’与Python密码学库实战
·
从CTF实战到密码学工具箱:RSA题型深度解析与Python自动化破解
RSA算法作为现代密码学的基石,在CTF竞赛中占据着不可撼动的地位。不同于教科书式的理论讲解,CTF中的RSA题目往往藏着出题人精心设计的"陷阱"和简化设定。本文将带你从解题者的实战视角,系统梳理CTF中RSA题型的常见套路,并构建可复用的Python密码学工具链。
1. CTF中的RSA题型分类学
在真实的CTF赛场上,RSA题目很少会直接给出所有标准参数。出题人通常会通过隐藏关键信息或设置特殊条件来增加挑战性。经过对上百道RSA题目的分析,我们可以将其归纳为以下几种典型模式:
1.1 基础题型:已知p、q、e、c求明文
这是最基础的RSA题目类型,正如Bugku这道题所示。解题步骤清晰明确:
- 计算模数n = p × q
- 计算欧拉函数φ(n) = (p-1)(q-1)
- 求私钥d ≡ e⁻¹ mod φ(n)
- 解密明文m ≡ cᵈ mod n
from Crypto.Util.number import long_to_bytes
import gmpy2
def basic_rsa_decrypt(p, q, e, c):
n = p * q
phi = (p-1)*(q-1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
return long_to_bytes(m)
1.2 进阶题型:已知n、e、c但n可分解
当题目只给出n、e、c时,解题的关键在于分解n得到p和q。在CTF环境中,n通常被设计为容易分解的形式:
- 小素数分解 :n较小时(通常<512位),可用在线工具或本地算法快速分解
- 特殊结构分解 :当n=p×q中的p和q接近时,可用费马分解法
- 已知部分信息 :给出p或q的高位或低位,可配合Coppersmith方法恢复完整素数
from sympy import factorint
def factorize_and_decrypt(n, e, c):
factors = factorint(n)
if len(factors) != 2:
raise ValueError("n should be product of two primes")
p, q = factors.keys()
return basic_rsa_decrypt(p, q, e, c)
1.3 特殊参数题型:非常规e值攻击
当公钥指数e取值不当时,可能导致系统脆弱:
| e值 | 攻击方法 | 适用条件 |
|---|---|---|
| e=3 | 低指数攻击 | m^e < n |
| e与φ(n)不互质 | 共模攻击 | 多组密文使用相同n |
| e很大 | Wiener攻击 | d < (1/3)n^(1/4) |
def low_exponent_attack(c, e, max_m):
for k in range(0, 100000):
m, exact = gmpy2.iroot(c + k*n, e)
if exact:
return long_to_bytes(int(m))
return None
2. Python密码学工具链构建
成熟的CTF选手不会每次遇到RSA题目都从头写代码。建立一个可复用的密码学工具箱能极大提升解题效率。
2.1 核心库选择与性能优化
gmpy2 是处理大数运算的不二之选,相比Python原生整数运算有百倍以上的性能提升。关键操作对比:
# 原生Python
d = pow(e, -1, phi) # Python 3.8+
# gmpy2优化版
d = gmpy2.invert(e, phi) # 快3-5倍
Crypto.Util.number 提供实用的数字与字节转换:
from Crypto.Util.number import bytes_to_long, long_to_bytes
m = b"flag{example}"
c = pow(bytes_to_long(m), e, n)
recovered = long_to_bytes(pow(c, d, n))
2.2 自动化解密脚手架
将常见操作封装成工具函数,形成RSA解题"瑞士军刀":
class RSASolver:
def __init__(self, n=None, e=None, p=None, q=None, c=None):
self.n = n
self.e = e
self.p = p
self.q = q
self.c = c
self.phi = None
self.d = None
def factorize(self):
"""自动分解n为p和q"""
# 实现多种分解算法...
return self.p, self.q
def compute_phi(self):
if self.p and self.q:
self.phi = (self.p-1)*(self.q-1)
return self.phi
def compute_d(self):
if not self.phi:
self.compute_phi()
self.d = gmpy2.invert(self.e, self.phi)
return self.d
def decrypt(self):
if not self.d:
self.compute_d()
m = pow(self.c, self.d, self.n)
return long_to_bytes(m)
2.3 常见攻击模式实现
将典型攻击方法实现为类方法,方便随时调用:
def wiener_attack(self):
"""实现Wiener攻击"""
# 连分数展开算法...
return d
def hastad_broadcast(self, ciphertexts):
"""实现Hastad广播攻击"""
# 中国剩余定理应用...
return m
def franklin_reiter(self, c1, c2, a, b):
"""Franklin-Reiter相关消息攻击"""
# 多项式相关计算...
return m
3. CTF出题人视角:RSA题目设计模式
理解出题思路能帮助我们更快识别题目类型。常见设计模式包括:
3.1 参数隐藏技巧
- 部分密钥泄露 :给出p的高位或低位,要求恢复完整密钥
- 错误参数注入 :故意提供错误的φ(n)值或损坏的密钥
- 多素数变种 :使用n=p×q×r等多素数形式增加分解难度
3.2 加密流程改造
- 填充方案缺陷 :使用不安全的PKCS#1 v1.5填充
- 混合加密系统 :RSA仅用于加密AES密钥,需要组合破解
- 时间侧信道 :通过响应时间差异泄露密钥信息
3.3 数学陷阱设置
- 共享模数 :多组密文使用相同n但不同e
- 相似素数 :p和q非常接近,可用费马分解
- 小私钥指数 :d取值过小,适用Wiener攻击
4. 实战演练:从Bugku到现实场景
让我们回到最初的Bugku题目,用工具链思维重新审视:
# 初始化解题环境
solver = RSASolver(
p=0xED7FCFABD3C81C78E212323329DC1EE2BEB6945AB29AB51B9E3A2F9D8B0A22101E467,
q=0xAD85852F9964DA87880E48ADA5C4487480AA4023A4DE2C0321C170AD801C9,
e=65537,
c=0x75AB3202DE3E103B03C680F2BEBBD1EA689C8BF260963FE347B3533B99FB391F0A358FFAE5160D6DCB9FCD75CD3E46B2FE3CFFE9FA2E9508702FD6E4CE43486631
)
# 自动化解密流程
flag = solver.decrypt()
print(flag.decode())
这个过程中,我们实际上构建了一个可复用的RSA解题框架。当遇到新题目时,只需替换参数即可快速测试:
# 新题目参数
new_params = {
"n": 0xABCD...1234,
"e": 0x10001,
"c": 0xDEF...5678
}
# 复用解题框架
new_solver = RSASolver(**new_params)
if new_solver.factorize(): # 尝试自动分解
print(new_solver.decrypt())
在真实的CTF比赛中,时间就是分数。通过预先准备好的工具库,我们能够将常规RSA题目的解题时间从30分钟缩短到3分钟,这在激烈的竞赛环境中是决定性的优势。
更多推荐

所有评论(0)