CTF中RSA的常见攻击方法

RSA基于一个简单的数论事实,两个大素数相乘十分容易,将其进行因式分解确实困难的。在量子计算机还没有成熟的今天,RSA算法凭借其良好的抵抗各种攻击的能力,在公钥密码体制中发挥着中流砥柱的作用。然而即便RSA算法目前来说是安全可靠的,但是错误的应用场景,错误的环境配置,以及错误的使用方法,都会导致RSA的算法体系出现问题,从而也派生出针对各种特定场景下的RSA攻击方法。

RSA算法描述

RSA算法涉及三个参数,n,e,d,私钥为{n,d},公钥为{n,e}。

$n= p*q$

$φ(n)= (p-1)*(q-1)$

其中n是两个大素数p,q的乘积。

d是e模φ(n)的逆元,φ(n)是n的欧拉函数。

$e^d = 1 mod φ(n)$

c为密文,m为明文,则加密过程如下:

$c = m^e mod φ(n)$ $m = c^d mod φ(n)$

RSA题目类型

在CTF竞赛中,RSA理所当然处在CRYPTO中居多。而且RSA作为典型的加密算法,其出镜率可谓相当高,基本上所有比赛都会有几道RSA的题目出现。

数据处理

在进行做题之前,我们要将数据处理成可以做题的模式。基本上来说,RSA的题目都是围绕着c,m,e,d,n,p,q这几个参数展开的,但是题目一般不会直接给这种样子的参数,而是通过别的方式给出,这里就需要我们使用一些工具或者自己手工将这些参数提取出来。

  • pem文件

对此类文件可以直接使用openssl提取,大概使用过的方式有:

openssl   rsautl -encrypt -in FLAG -inkey public.pem -pubin -out flag.enc
openssl   rsa -pubin -text -modulus -in warmup -in public.pem
  • pcap文件

针对此类文件可以使用wireshark follow一下。这种问题一般都是写了一个交互的crypto系统,所以可能产生多轮交互。

  • PPC模式

这种模式是上述pcap文件的交互版,会给一个端口进行一些crypto的交互,参数会在交互中给出。

第二个需要处理的就是明密文,这个方法多多,不多赘述。

模数分解

解决RSA题目最简单,最暴力,最好使的方法就是分解模数n。如果能够将n分解成功,成功得到p,q的取值,那么可求n的欧拉函数的值。

$φ(n) = (p-1)(q-1)$ $n = p*q$

而通过e,d与n的欧拉函数满足如下关系:

$ed = 1 mod φ(n)$

$e = d^-1 mod φ(n)$

通过欧几里得算法可以通过e与n的欧拉函数的值轻易求出d,从而计算出解密密钥。

即在知道e,p,q的情况下,可以解出d:

def egcd(a, b):
    if a == 0:
      return (b, 0, 1)
    else:
      g, y, x = egcd(b % a, a)
      return (g, x - (b // a) * y, y)
  def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
      raise Exception('modular inverse does not exist')
    else:
      return x % m

modinv函数即为求模拟的函数,在知道e,p,q的情况下,可以:

d=modinv(e,(p-1)*(q-1))

即可求出解密密钥。

写到这里,要提出一个细节问题,在利用python进行rsa的题目求解过程中:

$c = m^e mod φ(n)$

​ 此类运算需要使用pow函数来进行求解,而不是直接m**e % n,这样会慢死的。Python在处理此类运算进行了优化。比如刚才在已知p,q的时候利用modinv函数求出了d,然后就可以利用pow函数求出明文:

m=pow(c,d,n)

例题:

https://www.jarvisoj.com (very easy RSA)

题目中直接给了p,q,e。

可以直接求出d:

p = 3487583947589437589237958723892346254777
q = 8767867843568934765983476584376578389
e = 65537
d = modinv(e, (p-1)*(q-1))
print d

直接分解n

素数分解问题是困难的,但是可以通过计算机进行暴力分解。1999年,名为Cray的超级计算机用了5个月时间分解了512bit的n。2009年,一群研究人员成功分解了768bit的n。2010年,又提出了一些针对1024bit的n的分解的途径,但是没有正面分解成功。通常意义上来说,一般认为2048bit以上的n是安全的。现在一般的公钥证书都是4096bit的证书。

如果n比较小,那么可以通过工具进行直接n分解,从而得到私钥。如果n的大小小于256bit,那么我们通过本地工具即可爆破成功。例如采用windows平台的RSATool2v17,可以在几分钟内完成256bit的n的分解。

如果n在768bit或者更高,可以尝试使用一些在线的n分解网站,这些网站会存储一些已经分解成功的n,比如: http://factordb.com

通过在此类网站上查询n,如果可以分解或者之前分解成功过,那么可以直接得到p和q。然后利用前述方法求解得到密文。

题目识别

此类问题一般是分值较小的题目,提取出n之后可以发现n的长度小于等于512bit,可以直接取分解n。如果大于512bit,建议在使用每个题目都用后面所说的方法去解题。

例题

比如在某次竞赛中,发现:

n=87924348264132406875276140514499937145050893665602592992418171647042491658461

利用factordb分解:

n = 275127860351348928173285174381581152299*319576316814478949870590164193048041239

利用公约数

如果在两次公钥的加密过程中使用的n1和n2具有相同的素因子,那么可以利用欧几里得算法直接将n1和n2分解。

通过欧几里得算法可以直接求出n1和n2的最大公约数p:

gcd(n1,n2)=p

可以得出

$n1 = pq_1$ $n2 = pq_2$

直接分解成功。而欧几里得算法的时间复杂度为:O(log n)。这个时间复杂度即便是4096 bit也是秒破级别。

def gcd(a, b):
   if a < b:
     a, b = b, a
   while b != 0:
     temp = a % b
     a = b
     b = temp
   return a

题目识别

识别此类题目,通常会发现题目给了若干个n,均不相同,并且都是2048bit,4096bit级别,无法正面硬杠,并且明文都没什么联系,e也一般取65537。

例题

在一个题目中,你拿到了两个n,e都为65537,两个n分别为:

n1=9051013965404084482870087864821455535159008696042953021965631089095795348830954383127323853272528967729311045179605407693592665683311660581204886571146327720288455874927281128121117323579691204792399913106627543274457036172455814805715668293705603675386878220947722186914112990452722174363713630297685159669328951520891938403452797650685849523658191947411429068829734053745180460758604283051344339641429819373112365211739216160420494167071996438506850526168389386850499796102003625404245645796271690310748804327
n2=13225948396179603816062046418717214792668512413625091569997524364243995991961018894150059207824093837420451375240550310050209398964506318518991620142575926623780411532257230701985821629425722030608722035570690474171259238153947095310303522831971664666067542649034461621725656234869005501293423975184701929729170077280251436216167293058560030089006140224375425679571181787206982712477261432579537981278055755344573767076951793312062480275004564657590263719816033564139497109942073701755011873153205366238585665743

通过直接分解,上factordb都分解失败。通过尝试发现:

print gcd(n1,n2)

打印出:

1564859779720039565508870182569324208117555667917997801104862601098933699462849007879184203051278194180664616470669559575370868384820368930104560074538872199213236203822337186927275879139590248731148622362880471439310489228147093224418374555428793546002109

则此致即为两个n共有的素因子p,然后进一步求出q,求解完毕。

Fermat方法与Pollard rho方法

针对大整数的分解有很多种算法,性能上各有优异,有Fermat方法,Pollard rho方法,试除法,以及椭圆曲线法,连分数法,二次筛选法,数域分析法等等。其中一些方法应用在RSA的攻击上也有奇效。

在p,q的取值差异过大,或者p,q的取值过于相近的时候,Format方法与Pollard rho方法都可以很快将n分解成功。

此类分解方法有一个开源项目yafu将其自动化实现了,不论n的大小,只要p和q存在相差过大或者过近时,都可以通过yafu很快地分解成功。

题目识别

在直接分解n无望,不能利用公约数分解n之后,都应该使用yafu去试一下。

例题

https://www.jarvisoj.com (Medium RSA)

此题首先从pem中提取N和e,然后上yafu,直接分解成功。

低加密指数攻击

在RSA中e也称为加密指数。由于e是可以随意选取的,选取小一点的e可以缩短加密时间,但是选取不当的话,就会造成安全问题。

e=3时的小明文攻击

当e=3时,如果明文过小,导致明文的三次方仍然小于n,那么通过直接对密文三次开方,即可得到明文。

即:

$c = m^e mod φ(n)$

如果e=3,且 m^e < n,那么:

$c = m^e$ $e = 3$ $m = \sqrt{3} c$

如果明文的三次方比n大,但是不是足够大,那么设k,有:

$c = m^e+kn$

爆破k,如果$c-kn$能开三次根式,那么可以直接得到明文。

题目识别

在e=3的时候首先尝试这种方法

例题

https://www.jarvisoj.com (Extremely hard RSA)

关键代码如下:此题通过不断给明文+n开三次方即可求得:

i=0
   while 1:
   if(gmpy.root(c+i*N, 3)[1]==1):
     print gmpy.root(c+i*N, 3)
     break
   i=i+1

低加密指数广播攻击

如果选取的加密指数较低,并且使用了相同的加密指数给一个接受者的群发送相同的信息,那么可以进行广播攻击得到明文。

即,选取了相同的加密指数e(这里取e=3),对相同的明文m进行了加密并进行了消息的传递,那么有:

$ c_1 = m^e$ $mod$ $n_1$

$c_2 = m^e$ $mod$ $n_2$

$ c_3 = m^e$ $mod$ $n_3$

对上述等式运用中国剩余定理,在e=3时,可以得到:

$ c_x = m^3$ $mod$ $n_1n_2n_3$

通过对$ c_x $进行三次开方可以求得明文。

题目识别

一般来说都是给了三组加密的参数和明密文,其中题目很明确地能告诉你这三组的明文都是一样的,并且e都取了一个较小的数字。

例题:

SCTF2016,CODE300

题目第二轮中通过流量包的方式给了广播攻击的参数。

直接给国外类似一题的网址:http://codezen.fr/2014/01/16/hackyou-2014-crypto-400-cryptonet

Coppersmith定理攻击

Coppersmith定理指出在一个e阶的mod n多项式f(x)中,如果有一个根小于,就可以运用一个O(log n)的算法求出这些根。

Coppersmith定理指出在一个e阶的mod n多项式f(x)中,如果有一个根小于$ n^frac{1}{e} $,就可以运用一个O(log n)的算法求出这些根。

这个定理可以应用于RSA算法。如果e = 3并且在明文当中只有三分之二的比特是已知的,这种算法可以求出明文中所有的比特。

并未找到真题。

低解密指数攻击

与低加密指数相同,低解密指数可以加快解密的过程,但是者也带来了安全问题。

那么一种基于连分数(一个数论当中的问题)的特殊攻击类型就可以危害RSA的安全。此时需要满足:

$q<$$p$$<2q$

如果满足上述条件,通过Wiener Attack可以在多项式时间中分解n。

rsa-wiener-attack的攻击源码开源在了github中,采取python编写,可以很容易使用。

题目识别

e看起来很大就行了。

例题

直接github用工具就行。https://github.com/pablocelayes/rsa-wiener-attack

这里注意一个细节问题,如果在运行脚本的时候报错,请在脚本前加上:

import   sys
sys.setrecursionlimit(10000000)

共模攻击

如果在RSA的使用中使用了相同的模n对相同的明文m进行了加密,那么就可以在不分解n的情况下还原出明文m的值。即:

$ c_1=m^{e_1}$ $mod$ $n$

$ c_2=m^{e_2}$ $mod$ $n$

即存在$ s_2 $,$ s_2 $使得:

$ s1^{e_1} + s2^{e_2} = 1 $

又因为

$ c_1= m^{e_1}$ $mod$ $n$

$ c_2 = m^{e_2}$ $mod$ $n$

明文解出。

题目识别

非常简单,若干次加密,每次n都一样,明文根据题意也一样即可。

例题

https://www.jarvisoj.com (very hard RSA)

如果已知:n1,n2,c1,c2,e1,e2,并且其中n1=n2的话:

s = egcd(e1, e2)
 s1 = s[1]
 s2 = s[2]
   print s
 n=n1
   if s1<0:
     s1 = - s1
     c1 = modinv(c1, n)
   elif s2<0:
     s2 = - s2
     c2 = modinv(c2, n)
 m=(pow(c1,s1,n)*pow(c2,s2,n)) % n
Researcher of NTA

My research interests include Hacking, Cryptography, Reverse Analysis, and NLP

Next
Previous