0x00 前言
上一篇文章介绍了RSA的加解密过程及相关的数学基础以及对应的基础CTF题目,下面介绍几种针对加密过程中不同参数的攻击方法.
0x01 加密指数e
Rabin加密
Rabin加密是RSA的衍生算法,e==2是Rabin加密的典型特征,加密过程可以表示为c = m^2 mod n
, 解密过程为m = c^(1/2) mod n
.
详细算法可以参考Rabin加密体制
python实现
1 | def rabin_decrypt(c, p, q, e=2): |
以上函数返回四个数字,其中只有一个是我们想要的明文,需要通过其他的方法进行验证。
典型题目
题目来源: Jarvis OJ hard RSA
题目分析:题目给出了.enc文件和.pem文件,我们使用openssl命令读取后发现该加密的加密指数e=2,属于典型的Rabin加密体系,考虑使用脚本进行解密。
解题脚本:
1 | import gmpy2,libnum |
低加密指数攻击
当加密指数e较小且明文m也不大时,由于m^e=k*n+m
中的k很小甚至为0,我们可以爆破k或者直接开e次方即可.
python实现
1 | def small_msg(e, n, c): |
典型题目
题目来源:Jarvis OJ Extremely hard RSA
题目分析:该题目提供的n为4096位,e=3.我们可以使用上述脚本进行爆破.判断停止条件为开三次方根的结果为整数,使用的开方函数为gmpy2.iroot()
解题脚本:
1 | import gmpy2,binascii,libnum,time |
低加密指数广播攻击
如果模数n和密文c不同,而明文m和加密指数e相同.一般取e=k,k为所给数据的数量且e较小.在题目中会得到多个形如m^e == ci (mod ni)
的加密等式.取e=3,则有:
1 | c1 = m^e mod n1 |
对上述等式运用中国剩余定理,在e=3时,有:cx = m^3 mod n1*n2*n3
对cx进行开方即可求得明文.
典型例题
题目来源:2018强网杯nextrsa-Level9
题目分析: 该题目给出了c1,c2,c3,n1,n2,n3,我们考虑使用中国剩余定理CRT()函数和iroot()函数进行爆破.
解题脚本:
1 | m = random.randint(0x100000000000, 0xffffffffffff) |
CopperSmith Theorem
该定理指出在一个e阶的模n多项式f(x)中,如果有一个根小于$ n^{1/e} $,就可以运用一个$ O(log n) $的算法求出这些根。当本定理运用在rsa算法中,如果e=3并且明文当中有三分之二比特是已知的,这种算法可以还原出明文中所有的比特.
sage实现的解密脚本如下:
1 | n= number1(hex) |
详细解释参考Coppersmith及维基百科说明
e与$\phi$(n)不互素
只有当e与$\phi$(n)互素时,才能保证e的逆元d唯一存在.当二者不互素时,我们通过gcd()操作寻找与$\phi$(n)互素的数,然后进行求解.我们通过一个例题来说明.
题目描述:
1 | n1=0xcfc59d54b4b2e9ab1b5d90920ae88f430d39fee60d18dddbc623d15aae645e4e50db1c07a02d472b2eebb075a547618e1154a15b1657fbf66ed7e714d23ac70bdfba4c809bbb1e27687163cb09258a07ab2533568192e29a3b8e31a5de886050b28b3ed58e81952487714dd7ae012708db30eaf007620cdeb34f150836a4b723L |
题目分析: 题目给出了n1和n2,我们联想到公约数,通过p=gcd(n1,n2)
求得p,进而求出q1和q2.此时我们发现e与$\phi$(n)不互素.
如果我们需要求逆元,则需要找到与$\phi$(n)互素的数.
我们已知b=14,通过上面的推算我们可得a与$\phi$(n)互素,于是求出b*d = gmpy2.invert(a,phin)
,进而求出d.可是经测试发现明文为乱码.
根据推导的最后一个公式,b*d可作为私钥求解出m^14.我们得到一个同余方程组.
进一步推导
可以运用中国剩余定理计算特解m,即m=solve_crt([m1,m2,m3], [q1,q2,p])
.如果模n1,n2不行那么可以转换为模q1*q2,从而有$ res \equiv m^14 mod q1*q2 $.
这样就转换成一个新的rsa问题,且e=14.此时e与$\phi (n)=(q1-1)*(q2-1) $还有公因数2.那么我们参照上述思路,可以得出m^2满足的方程,从而开方求结果.
解题脚本如下:
1 | from libnum import * |
0x02 解密指数d
低解密指数攻击
如果满足条件d < (1/3)*n^(1/4)
,那么一种基于连分数的特殊攻击类型就可以危害RSA的安全.此时需要满足q<p<2*q
,则可以通过Wiener Attack在多项式时间中分解n.常适用于e过大或过小的情况.为了理解这个过程,我们讲解维纳定理相关知识.
Wiener’s Theorem
连分数展开:
[a0,a1,a2,....,an]
)
举例如下:
渐近分数
定理内容
如果N = p*q, q<p<2*q, d<(1/3)*N^(1/4)
,且已知(e,N)满足$ e*d \equiv 1 mod \phi(N) $,攻击者可以从e/N的连分数的渐近分数中找到正确的k/d从而获得密钥d.举例
已知(N,e)=(90581,17993),首先求e/N的连分数的渐近分数:
循环k,d的值,求解方程:
如果方程有两个有效解则分别为p和q.本例中当k=1,d=5时,$\phi$(N)=89964,方程存在两解x1=379,x2=293.
python实现
1 | from Crypto.PublicKey import RSA |
- github参考工具:rsa-wiener-attack
典型例题
题目来源: 2018强网杯nextrsa-Level2
解题脚本:
1 | # e与n的差距过小,考虑使用wiener's attack |
d泄露攻击
如果我们知道一组过期的(N, e1, d1)和一组由新的e2组成的公钥及其加密的密文(N, e2, c),我们可以由(e1, d1)得到模数N的两个因子p和q,然后再反解d2即可求出明文.
private.pem修复攻击
当题目提供的模数N过大不可分解且同时提供破损的私钥文件时,可以考虑private.pem修复.
- 典型题目: Jarvis OJ-God Like RSA
- 参考链接: 私钥文件修复
0x03 模数N
N的特殊分解
针对大整数的分解有很多种算法,包括Fermat方法,Pollard rho p-1方法,试除法,以及椭圆曲线法,连分数法,二次筛选法,数域分析法等等.这里具体介绍Pollard rho和Fermat分解方法.
Pollard rho p-1分解
适用于p和q相差较大的情况.脚本如下:
1 | from gmpy2 import * |
Fermat分解
适用于p和q相差不大的情况.脚本如下:
1 | from gmpy2 import * |
典型题目
分解n = 0x4333AF6B43F36028D8D9650EC3EED3238541EE5C15E626C58C9EC33674A6D08D5B1F2580A1A0B07E9D853536CD994E197889D122701A62BB2A9E79559F3D5281014535F6C54F83CA8D9700EEB67D99AF318D20A5150AD46D622A6A12DE0A758EE7DF75F5D10F2FE2585F2348537787063321FFDAC91BB3C3D1D88CBD04A824ED
.
分解脚本如下:
1 | from random import randint |
共模攻击
当明文m、模数n相同,公钥指数e、密文c不同且gcd(e1,e2)==1
时,我们可以采取共模攻击来直接还原出明文.下面简单进行证明:
python实现
1 | def common_modulus(n, e1, e2, c1, c2): |
典型例题
题目来源: buuctf RSA3
题目分析: 该题给出了c1,c2,e1,e2和n,显然使用共模攻击.
解题脚本:
1 | import gmpy2 as gp |
公共因子攻击
当题目给出同一个(m,e)使用较多组N加密后的多组(N,c)时,可以循环求解这些N的最大公因数.如果存在gcd(Ni,Nj)!= 1
,则可以顺利分解Ni和Nj,从而还原明文.
python实现
1 | def common_primes(data): |
典型题目
题目来源: buuctf RSA5
题目分析: 题目给出了e=65537,以及二十组对应的(n,c).由于所给的信息数量较多,考虑公共因子攻击.构造符合上文所给脚本的数组data,然后寻找拥有公共因子的模数对,最后还原明文.
解题脚本:
1 | import gmpy2 as gp |
0x04 密文c
选择密文攻击
该类攻击适用于可以构造任意密文并获得对应明文的情况.
在一个RSA加密过程中,明文为m,密文为c,模数为n,加密指数为e,选取x以满足gcd(x,n)==1
从而使x模n的逆存在,构造密文c'=c*(x^e)
,使解密后的明文为m'=(m*x)%n
,则m=m'*x^(-1)(mod n)
.
LSB Oracle Attack
假如用户知道公钥中N, e, c,并且可以任意构造密文c1,返回此密文解密后p1的末尾某些比特位的性质(记为函数f).最简单的函数f是表示p1的奇偶性.
攻击者构造密文c'=((2^e)*c)%n=((2^e)*(m^e))%n=((2*m)^e)%n
,返回明文m'=(2*m)%n
及m’的lsb.下面继续分析,因为n是奇数,而2*m是偶数,所以如果lsb=0,则(2\*m)%n
是偶数,没有超过n,从而确定m<n/2.0
,反之则m>n/2.0
.
以此类推,构造密文c''=((4^e)*c)%n
使其解密后有m''=(4*m)%n
,判断m’’的奇偶性就可以知道m和n/4的大小关系.此时我们得到一个在对数时间内将m的范围逼近到一个足够小范围的二分算法.
python实现
1 | import decimal |
题目一 QCTF 2018-XMan选拔赛/Baby RSA
题目描述:
1 | e = 0x10001 |
解题脚本:
1 | solve.py |
题目二:Backdoor CTF2018 BIT-LEAKER
题目描述:
1 | server.py |
解题脚本:
1 | solve.py |
参考链接: RSA Least-Significant-Bit Oracle Attack
0x05 综合应用
通过一道题目分析说明RSA的综合应用.
题目来源: 2019De1CTF BabyRSA
题目描述: 题目提供的脚本如下.
1 | import binascii |
(由于数组n和c中的数字较多,故使用n1-n4,c1-c4代替表示)
题目分析:本题思路非常明确,空行将脚本分成四个部分,我们逐个部分解决.
第一部分: 给出了n1-n4和c1-c4,且sum()函数返回的结果为4,我们可以得到如下同余方程组:
1 | c1 = p^4 mod n1 |
联想到使用中国剩余定理解决,我们得到大素数p.
第二部分: 给出了两个较小的加密指数e1=42
,e2=3
,联想到使用低加密指数攻击.分别使用gmpy2.iroot()
函数进行爆破处理,可以得到e1和e2.
第三部分: 该加密过程没有其他的办法,只能使用yafu进行模数分解.分解结果可以看出,p,q的差距不大,所以使用了Fermat分解法.
第四部分: 经过前三部分的解密,我们得到了如下参数:
1 | 加密指数e1,e2 |
再加上第四部分所给的c1,c2,我们有如下rsa问题:
1 | c1 % p*q1 = flag^e1 % p*q1 |
通过观察发现e1,e2与$\phi(pq1), \phi (pq2) $不互素,属于e与$\phi$(n)不互素的情况.而且我们发现gcd(e1,p*q1)=gcd(e2,p*q2)=14
,按照上面的推导步骤,最终得到$ m^2 \equiv c^(2d) (mod q1q2) $.进行开方爆破,最终得到flag.
解密脚本如下:
1 | #coding:utf8 |