CTF实战笔记:当RSA的dp参数泄露时,我是如何用Python脚本5分钟拿到flag的
·
CTF实战笔记:当RSA的dp参数泄露时,我是如何用Python脚本5分钟拿到flag的
第一次参加CTF比赛时,遇到一道RSA题目卡了整整三小时。直到队友提醒"检查dp参数泄露",我才意识到自己错过了关键突破口。这次经历让我深刻理解到: RSA的dp泄露简直是CTF中的"速通密码" 。本文将用最直白的语言,带你复现从懵逼到破解的全过程。
1. 理解dp泄露的本质
dp是RSA私钥d对(p-1)取模的结果,即 dp ≡ d mod (p-1) 。正常情况下它应该和私钥一样保密,但某些题目会故意泄露这个参数。为什么这很危险?因为通过dp我们可以反推出p的值。
数学原理 :
根据定义 e*d ≡ 1 mod φ(n) 和 dp ≡ d mod (p-1) ,可以推导出:
e*dp ≡ e*d ≡ 1 mod (p-1)
=> e*dp - 1 = k*(p-1) (k为整数)
这意味着我们只需要遍历k的可能值,就能暴力破解出p。具体操作时,k的取值范围其实非常有限——通常e不会太大(比如常见的65537),这使得爆破成为可能。
注意:实际编码时要处理的是
(e*dp -1)能被k整除的情况,同时要满足n能被候选p整除。
2. 实战解题步骤拆解
以BUUCTF平台上的[RSA2]题目为例,给定参数:
- n = 248254007851526241177721526698901802985832766176221609612258877371620580060433101538328030305219918697643619814200930679612109885533801335348445023751670478437073055544724280684733298051599167660303645183146161497485358633681492129668802402065797789905550489547645118787266601929429724133167768465309665906113
- e = 65537
- dp = 905074498052346904643025132879518330691925174573054004621877253318682675055421970943552016695528560364834446303196939207056642927148093290374440210503657
- c = 140423670976252696807533673586209400575664282100684119784203527124521188996403826597436883766041879067494280957410201958935737360380801845453829293997433414188838725751796261702622028587211560353362847191060306578510511380965162133472698713063592621028959167072781482562673683090590521214218071160287665180751
2.1 爆破p值的Python实现
关键点在于确定k的遍历范围。由于 k = (e*dp -1)/(p-1) ,而p是n的大素数因子,所以k的取值不会超过e:
import gmpy2
n = 248254007851526241177721526698901802985832766176221609612258877371620580060433101538328030305219918697643619814200930679612109885533801335348445023751670478437073055544724280684733298051599167660303645183146161497485358633681492129668802402065797789905550489547645118787266601929429724133167768465309665906113
e = 65537
dp = 905074498052346904643025132879518330691925174573054004621877253318682675055421970943552016695528560364834446303196939207056642927148093290374440210503657
c = 140423670976252696807533673586209400575664282100684119784203527124521188996403826597436883766041879067494280957410201958935737360380801845453829293997433414188838725751796261702622028587211560353362847191060306578510511380965162133472698713063592621028959167072781482562673683090590521214218071160287665180751
for k in range(1, e): # 遍历1到e-1
if (e * dp - 1) % k == 0: # 检查是否能整除
p = (e * dp - 1) // k + 1
if n % p == 0: # 验证p是否是n的因子
q = n // p
break
2.2 完整解密流程
得到p和q后,就是标准的RSA解密过程了:
phi = (p-1)*(q-1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(bytes.fromhex(hex(m)[2:]))
常见坑点 :
- 整数除法要用
//而不是/,否则会得到浮点数导致后续计算错误 - 遍历范围上限设为e即可,不需要更大(节省计算时间)
- 得到p后一定要验证
n%p==0,避免假阳性结果
3. 性能优化技巧
虽然这个解法已经很快,但在更复杂的场景下可能需要优化:
3.1 并行计算加速
对于更大的e值,可以使用多进程加速爆破:
from multiprocessing import Pool
def crack(k):
if (e * dp - 1) % k == 0:
p = (e * dp - 1) // k + 1
if n % p == 0:
return p
return None
with Pool(4) as p: # 使用4个进程
results = p.map(crack, range(1, e))
primes = [x for x in results if x]
if primes:
p = primes[0]
3.2 数学范围缩小
通过分析可知k必须是 (e*dp-1) 的约数,可以先计算这个数的所有因数:
from math import gcd
temp = e * dp - 1
factors = set()
for i in range(1, int(temp**0.5)+1):
if temp % i == 0:
factors.add(i)
factors.add(temp//i)
for k in sorted(factors):
if k >= e:
continue
p = temp // k + 1
if n % p == 0:
break
4. 防御措施与题目变种
在实际系统中,dp泄露之所以危险,是因为它让攻击者绕过了大数分解的难题。防护措施包括:
- 永远不要存储或传输dp参数
- 使用标准密钥生成工具 (如OpenSSL)
- 定期更换密钥
CTF中常见的变种题型:
- 给出dq(d mod q-1)而不是dp
- 结合其他攻击方式(如共模攻击)
- 隐藏dp的真实含义(需要先逆向分析)
遇到这类题目时,我的经验是:
- 先确认所有给定参数的含义
- 搜索参数组合的已知攻击方式
- 尝试用小规模数据验证猜想
更多推荐

所有评论(0)