0x00 基本原理
Coppersmith相关攻击与Don Coppersmith紧密相关,他提出一种针对于模多项式(单变量,二元变量,甚至多元变量)找所有小整数根的多项式时间的方法.
这里以单变量为主进行介绍,假设:
- 模数为N,N具有一个因子$ b>=N^ \beta $, $ 0< \beta <=1$
- 多项式F的次数为$\delta$
那么该方法可以在$ O(c \delta ^{5log^9(N)}) $的复杂度内找到该多项式所有的根x0,这里我们要求$|x0| < cN^(\beta ^2/ \delta) $.
在这个问题中,我们的目标是找到模N意义下多项式所有的根,这一问题被认为是复杂的.Coppersmith method主要通过LLL方法找到
- 与该多项式具有相同根x0
- 更小系数
- 定义域为整数域
的多项式g,由于在整数域上找多项式的根是简单的,从而我们就得到了原多项式在模意义下的整数根.
问题的关键则变成从f转换到g,Howgrave-Graham给出了一种思路:
在LLL算法中,有两点是非常有用的
- 只对原来的基向量进行整数线性变换,这可以使得我们在得到g时,仍然以原来的x0为根.
- 生成的新的基向量的模长是有界的,这可以使得我们利用Howgrave-Graham定理.
在这样的基础上,我们再构造出多项式族g就可以了.需要注意的是,由于Coppersmith根的约束,在RSA中的应用时,往往只适用于e较小的情况.
- 攻击脚本:coppersmith.sage
- 参考链接
0x01 Related Message Attack
攻击条件
Alice使用相同的N和e对两个相关的信息m1,m2进行加密,并将加密之后的结果c1,c2发送给Bob.并且m1,m2满足
其中f为一个线性函数,即$ f=a*x+b $.
在具有较小错误概率的情况下,复杂度为$ O(elog^2(N)) $.
攻击原理
首先我们知道$ c1\equiv m1^e mod N $,并且$ m1 \equiv f(m2) mod N $,那么我们可以知道m2是$ f(x)^e \equiv c1 mod N $的一个解,即它是方程$ f(x)^e-c1 $在模N意义下的一个根.同样,m2是$ x^e $模N意义下的一个根.所以说$ x-m2 $同时整除以上两个多项式.因此,我们可以求得两个多项式的最大公因子,如果最大公因子恰好是线性的话,那么我们就求得了m2.需要注意的是,在e=3的情况下,最大公因子一定是线性的.
当e=3,且$ f(x)=a*x+b $的情况下,经过推导我们可以得到:
上面的式子中右边所有的参数都是已知的,所以可以求得对应的信息.
典型例题
题目来源: SCTF RSA3
题目分析: 首先,跟踪TCP流我们发现,加密方式是将明文加上用户的user-id进行加密,而且还存在多组.我们选择模数相同的两个cipher.
解密脚本:
1 | # exp.py |
1 | # exp.sage |
0x02 Known High Bits Message Attack / Stereotyped Messages
攻击条件
普通的RSA解密模型如下:
并且假设我们知道消息m的大部分m0,从而m=m0+x,x即为待求消息.那么我们可以通过该方法进行消息恢复.这里待求的x其实就是满足Coppersmith约束的多项式的根.
攻击原理
我们构造多项式f(x) = (m+x)^e - c
,其在模N条件下的根即为与m的差距.
攻击脚本
1 | # exp.py |
- 注意: XX是根的上界,根据所知的m的位数进行调整
0x03 Factoring with High Bits Known
攻击条件
当我们知道一个公钥中模数N的一个因子的较高位时,就有一定的几率来分解N.
攻击原理
我们在RSA攻击过程中需要进行模数N的分解,当已知其中一个大素数q的部分位时,可以构造函数f(x) = x-q'
,该式在模q条件下的根即为q’与q之间可能的差距.
攻击脚本
1 | load("coppersmith.sage") |
- 注意必须满足
q>=N^beta
,所以这里令beta=0.5,保证两个因数中必有一个满足条件 - XX是
f(x) = q'+x
在模q意义下的根的上界,自然我们可以选择调整它,这里也表明了我们已知的q’与因数q之间可能的差距
典型例题
题目来源: 2017 WHCTF-UNTITLED
题目描述: 下载附件之后得到如下脚本.
1 | from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long |
题目分析: 首先要绕过proof()函数的验证,即要满足hashlib.md5(salt+proof.decode("base64")).hexdigest().startswith("0000")
,这里可以使用脚本爆破,使盐值和输入内容的base64解码开头四位为0000.
将爆破结果随便提交一个,即可通过验证,得到n,e,c,u.
再分析题目所给的py脚本,可以看出:
- c是flag经过RSA加密后的密文,其中n和e已知
- u为m的前8位(即为f)经过RSA加密后的值
1
2
3x=int(hex(m)[2:][0:8]+raw_input("x: "),16)
y=int(raw_input("y: "),16)
pow(x,e,n)==y and pow(y,d,n)==t
将以上三行连起来分析,令x为空,y为u即可通过最后一步if验证,从而得到p的前568位.
现在我们得到了以下信息:
1 | flag加密后的密文c |
容易联想到恢复p再解RSA问题.
这里联想到使用Coppersmith Attack,该方法需要至少576位p,已知568位,差了两个十六进制数,根据官方提示进行爆破,代码如下:
1 | # explore.sage |
得到p之后就可以正常解开RSA得flag了.
0x04 Boneh and Durfee Attack
攻击条件
当d较小时,满足d<N^(0.292)
时,我们可以利用该攻击,比维纳攻击效率高一些.
攻击原理
首先有$ e*d \equiv 1 mod (\phi(N)/2) $
进而转化为$ ed + k\phi(N)/2 = 1 $
即$ k* \phi(N)/2 \equiv 1 mod e $
又$ \phi(N) = (p-1)*(q-1) = N-p-q+1 $
所以$ k*(N-p-q+1)/2 \equiv 1 mod e $
这里令$ A = (N+1)/2 $, $ y = (-p-q)/2 $,原式为:
$ f(k,y) = k*(A+y) \equiv 1 mod e $
其中$ |k| < (2ed)/\phi(N) < (3ed)/N = 3(e/N)d < 3(e/N)N^\delta, |y| < 2*N^0.5 $
这里delta为预估的小于0.292的值.
如果我们可以求出该二元方程的根,就可解一元二次方程N=p*q,p+q=-2*y
来得到p与q.
攻击脚本
攻击工具: boneh_durfee.sage
注意事项:
- 根据boneh_durfee.sage脚本后的使用说明替换解密参数.变量delta是对私钥d的估计.如果不满足
d<N^delta
这一条件,则无解.从delta=0.26开始逐渐递增,最大不超过0.292 - 如果脚本报错: “Try with highers m and t”,则应该增大m的值,相应的计算时间会增加
- 如果由于解密时间太长而不愿增大m,则可以尝试减小x的值,因为可能出现x的设定值与真实值差距过大的情况.
- 如果仍然无法找到delta,m和t,则可以尝试在
d<N^0.292
的范围内进行爆破. - 一旦发现x,y的可能解,则可以将其插入如下等式:
e*d=x*[(N+1)/2+y]+1
典型例题
题目来源: 2015 PlaidCTF Curious
题目分析: 该题目给了一堆N, e, c.简单看一下可以发现e比较大.这个时候可以考虑维纳攻击或Boneh_durfee攻击.
解题脚本(核心代码):
1 | nlist = list() |
0x05 Partial Key Exposure Attack
攻击条件
若e较小,且d的低位已知时,可以考虑使用部分私钥泄露攻击.
攻击原理
部分私钥泄露攻击的原理同前面提到的明文高位泄露以及模数部分泄露原理相同.
攻击脚本
1 | # partial_key_exposure_attack.sage |
其中d0代表已知d的较低位,kbits是已知的d0的位数.
0x06 题目举例
下面以一道CTF题目为例,讲解上述几种攻击方法的具体应用.另外一道综合类RSA题目链接如下: 2018 护网杯-nextrsa
题目来源: 2019 护网杯-Copperstudy
复现地址: buuoj-Crypto-Copperstudy
题目描述: 题目给出了一共六个challenge,只有全部通过验证才可以得到最终的flag.我们针对不同的challenge分别使用sage脚本解题,再逐个提交.
下面进行题目分析
Challenge0 — SHA256 proof
1 | [+]proof: skr=os.urandom(8) |
nc之后返回上述代码,给出了八位字符串经过sha256之后的结果以及前五位的sha256之后的结果,爆破后三位传上去,可以进入下一个挑战.
Challenge1 — Known High Bits Message Attack
1 | [+]Generating challenge 1 |
题目给出的e较小,而且只隐藏了明文m的低72位.联想使用Stereotyped messages攻击
.脚本如下:
1 | n = given_n |
challenge2—Factoring with High Bits Known
1 | [+]Generating challenge 2 |
该挑战给出了其中一个模数p除了低128bits的其他信息.联想使用factoring with high bits known攻击
.脚本如下:
1 | n=given_n |
Challenge3 — Partial Key Exposure Attack
1 | [+]Generating challenge 3 |
题目给出了低加密指数及d的部分位数,于是使用Partial key exposure attack攻击
,脚本如下:
1 | def partial_p(p0, kbits, n): |
Challenge4 — Basic Broadcast Attack
1 | [+]Generating challenge 4 |
题目给出三组使用小指数e=3加密的消息,考虑使用广播攻击.
1 | import gmpy |
Challenge5 — Related Message Attack
1 | [+]Generating challenge 5 |
题目给出m和m+1加密之后的消息c1,c2,且加密指数e=3.考虑到可以使用相关明文攻击.令a=1,b=-1,构造payload如下:
1 | import hashlib |
Challenge6 — Boneh and Durfee Attack
1 | [+]Generating challenge 6 |
根据题目所给信息可知d < N^0.25
,使用低指数解密后提交可得最后的flag.