CTF实战:手把手教你用Python脚本破解RSA共模攻击(附完整代码)
CTF实战:手把手教你用Python脚本破解RSA共模攻击(附完整代码)
当你第一次在CTF比赛中遇到RSA共模攻击题目时,可能会感到无从下手。别担心,这篇文章将带你从零开始,一步步理解攻击原理,并亲手编写Python脚本破解这类题目。我们会以一个真实的CTF题目为例,详细讲解每个步骤和可能遇到的坑。
1. 认识RSA共模攻击
RSA共模攻击(Common Modulus Attack)是一种针对RSA加密系统的攻击方式。它发生在以下场景:
- 相同的明文m用相同的模数n但不同的公钥指数e1和e2加密
- 对应的密文分别为c1和c2
- 攻击者知道n、e1、e2、c1和c2
关键点 :当e1和e2互质(gcd(e1,e2)=1)时,我们可以利用扩展欧几里得算法恢复出明文m,而无需知道私钥d。
注意:这种攻击之所以有效,是因为使用了相同的模数n加密相同的信息。在实际应用中,绝对不应该重复使用相同的模数。
2. 数学原理深入解析
要理解共模攻击,我们需要掌握几个关键数学概念:
2.1 扩展欧几里得算法
这个算法不仅能计算两个数的最大公约数,还能找到满足贝祖等式(Bézout's identity)的整数系数:
对于整数e1和e2,如果gcd(e1,e2)=1,那么存在整数s和t,使得:
e1*s + e2*t = 1
2.2 攻击的核心推导
给定:
c1 ≡ m^e1 mod n
c2 ≡ m^e2 mod n
利用扩展欧几里得算法找到s和t后,我们可以计算:
m = (c1^s * c2^t) mod n
这是因为:
c1^s * c2^t ≡ (m^e1)^s * (m^e2)^t ≡ m^(e1*s + e2*t) ≡ m^1 ≡ m mod n
2.3 处理负指数
在实际计算中,s或t可能为负数。这时需要使用模逆元来处理:
如果s为负数:
c1^s ≡ (c1^{-1})^{-s} mod n
其中c1^{-1}是c1在模n下的逆元。
3. 实战演练:破解SWPUCTF题目
让我们以SWPUCTF 2021新生赛的crypto2题目为例,一步步破解。
3.1 题目分析
题目给出了以下数据:
flag1= 100156221476910922393504870369139942732039899485715044553913743347065883159136513788649486841774544271396690778274591792200052614669235485675534653358596366535073802301361391007325520975043321423979924560272762579823233787671688669418622502663507796640233829689484044539829008058686075845762979657345727814280
flag2= 86203582128388484129915298832227259690596162850520078142152482846864345432564143608324463705492416009896246993950991615005717737886323630334871790740288140033046061512799892371429864110237909925611745163785768204802056985016447086450491884472899152778839120484475953828199840871689380584162839244393022471075
e1= 3247473589
e2= 3698409173
n= 103606706829811720151309965777670519601112877713318435398103278099344725459597221064867089950867125892545997503531556048610968847926307322033117328614701432100084574953706259773711412853364463950703468142791390129671097834871371125741564434710151190962389213898270025272913761067078391308880995594218009110313
3.2 环境准备
首先安装必要的Python库:
pip install gmpy2 pycryptodome
3.3 编写攻击脚本
完整攻击代码如下:
from Crypto.Util.number import long_to_bytes
from gmpy2 import gcdext, invert
# 题目给出的数据
c1 = 100156221476910922393504870369139942732039899485715044553913743347065883159136513788649486841774544271396690778274591792200052614669235485675534653358596366535073802301361391007325520975043321423979924560272762579823233787671688669418622502663507796640233829689484044539829008058686075845762979657345727814280
c2 = 86203582128388484129915298832227259690596162850520078142152482846864345432564143608324463705492416009896246993950991615005717737886323630334871790740288140033046061512799892371429864110237909925611745163785768204802056985016447086450491884472899152778839120484475953828199840871689380584162839244393022471075
e1 = 3247473589
e2 = 3698409173
n = 103606706829811720151309965777670519601112877713318435398103278099344725459597221064867089950867125892545997503531556048610968847926307322033117328614701432100084574953706259773711412853364463950703468142791390129671097834871371125741564434710151190962389213898270025272913761067078391308880995594218009110313
# 使用扩展欧几里得算法找到s和t
gcd, s, t = gcdext(e1, e2)
# 处理负指数情况
if s < 0:
s = -s
c1 = invert(c1, n)
elif t < 0:
t = -t
c2 = invert(c2, n)
# 计算明文
m = (pow(c1, s, n) * pow(c2, t, n)) % n
# 将长整数转换为字节并打印
print(long_to_bytes(m))
3.4 代码逐行解析
-
导入库 :
long_to_bytes:将长整数转换为字节串gcdext:扩展欧几里得算法实现invert:计算模逆元
-
输入数据 :
- 直接复制题目给出的c1、c2、e1、e2和n
-
计算贝祖系数 :
gcdext(e1, e2)返回三个值:gcd(e1,e2),以及满足e1 s + e2 t = gcd(e1,e2)的s和t- 由于题目保证e1和e2互质,gcd应为1
-
处理负指数 :
- 如果s为负,取其绝对值并计算c1的模逆元
- 如果t为负,同理处理
-
计算明文 :
- 使用公式m = (c1^s * c2^t) mod n
- 注意使用模幂运算提高效率
-
输出结果 :
- 将长整数m转换为字节串,即flag
3.5 常见问题与调试技巧
问题1 : gcdext 返回的s和t都是正数怎么办?
这种情况理论上不会发生,因为e1和e2都是大素数,且互质。如果真的发生,可以检查输入是否正确。
问题2 :运行时报 ValueError: invalid literal for int() with base 10 ?
这通常是因为直接从题目复制数据时包含了非数字字符(如换行符)。确保只复制纯数字。
问题3 :结果输出乱码?
可能原因:
- 计算过程中某一步出错,导致m值不正确
- flag本身不是可打印字符
- 需要进一步处理(如base64解码)
可以尝试打印m的十进制值,看看是否合理。
4. 攻击的防御措施
了解攻击方法后,我们更应该知道如何防御:
- 绝不重复使用模数 :每个用户/每次加密都应生成独立的n
- 使用标准填充方案 :如OAEP,避免明文可预测
- 定期更换密钥 :即使没有泄露,也应定期更新密钥对
- 监控异常行为 :如多次加密相同明文
5. 扩展学习:其他RSA攻击方式
除了共模攻击,RSA还有多种攻击方式值得了解:
| 攻击类型 | 条件 | 防御措施 |
|---|---|---|
| 小公钥指数攻击 | e很小(如3) | 使用标准e值(65537) |
| 共享素数攻击 | 两个n有共同因子 | 确保p,q独立随机生成 |
| 侧信道攻击 | 物理访问设备 | 恒定时间实现 |
| 选择密文攻击 | 可提交密文解密 | 使用适当填充方案 |
6. 工具与资源推荐
Python库 :
gmpy2:高性能数学运算pycryptodome:密码学工具集sympy:符号数学计算
学习资源 :
- 《应用密码学:协议、算法与C源程序》
- Cryptopals挑战(https://cryptopals.com/)
- CTF Wiki密码学部分(https://ctf-wiki.org/)
7. 实战技巧与经验分享
在实际CTF比赛中,处理RSA题目时我通常会:
- 首先检查n是否可以被分解(factordb.com)
- 查看e是否异常(特别小或特别大)
- 检查是否有多个密文/公钥(可能共模或共享素数)
- 尝试已知攻击方式的脚本
- 如果所有方法都失败,考虑是否有非标准编码或额外加密层
记得在一次比赛中,题目看起来像共模攻击,但实际需要先对密文进行简单异或处理才能应用标准方法。这提醒我们:永远不要假设数据已经准备好直接使用。
更多推荐

所有评论(0)