1. 程式人生 > 實用技巧 >攻防世界-密碼學-equation-2

攻防世界-密碼學-equation-2

1. 題目資訊

題目描述“RSA私鑰上面的部分被遮蔽了請恢復私鑰並解密檔案”,附件給出私鑰編碼的截圖,但是隻能看見最後5行。

2. 分析

1.OpenSSL私鑰結構

私鑰資訊按如下順序排列:
version | pad | n | pad | e | pad | d | pad | p | pad | q | pad | x1 | pad | x2 | pad | x3
其中,pad是填充資訊,各pad並不同,\(x_{1}= d\ \textrm{mod}\ (p-1),x_{2}= d\ \textrm{mod}\ (q-1),x_{3}=p^{-1}\ \textrm{mod}\ q\)

,填充pad用來註釋接下來的大數的(位元組)長度,\x02為pad開頭的標記,有時後面接\x81或\x82,這用來標記長度值所佔用的位元組(\x81代表佔用1個位元組,\x82代表佔用2個位元組),有時後面不接\x81或\x82而直接放置長度;
例:\x02\x03代表接下來的大數的位元組長度為3個位元組;\x02\x81\x80,首先,\x81代表長度佔用1個位元組,因此\x80就是長度值,即128,表明接下來的大數的位元組長度為128個位元組。

將私鑰資訊按照上述順序排列好之後,再進行base64編碼。

2.利用已知資訊恢復私鑰

截圖可見編碼為

Os9mhOQRdqW2cwVrnNI72DLcAXpXUJ1HGwJBANWiJcDUGxZpnERxVw7s0913WXNtV4GqdxCzG0pG5EHThtoTRbyX0aqRP4U/hQ9tRoSoDmBn+3HPITsnbCy67VkCQBM4xZPTtUKM6Xi+16VTUnFVs9E4rqwIQCDAxn9UuVMBXlX2Cl0xOGUF4C5hItrX2woF7LVS5EizR63CyRcPovMCQQDVyNbcWD7N88MhZjujKuSrHJot7WcCaRmTGEIJ6TkU8NWt9BVjR4jVkZ2EqNd0KZWdQPukeynPcLlDEkIXyaQx

解碼後結合OpenSSL私鑰結構分析可得:x1,x2,x3為已知;但是僅有x1,x2,x3並不能恢復出p,q與d,若我們假設e為常用的指數3,65537等等,則可試出p與q:

\(d\cdot e\equiv 1\ \textrm{mod}\ (p-1)(q-1)\)
則有\(d\cdot e\equiv 1\ \textrm{mod}\ (p-1)\)\(d\cdot e\equiv 1\ \textrm{mod}\ (q-1)\)
\(x_{1}\)\(x_{2}\)的定義可得\(x_{1}\cdot e\equiv 1\ \textrm{mod}\ (p-1)\)\(x_{2}\cdot e\equiv 1\ \textrm{mod}\ (q-1)\)


因此\((p-1)|(x_{1}\cdot e-1)\)
\(x_{1}\cdot e-1=r_{1}\cdot (p-1)\)
由於\(x_{1}= d\ \textrm{mod}\ (p-1)\),則\(x_{1}<(p-1)\)
幾乎可以看做\(x_{1}\cdot e=r_{1}\cdot (p-1)\),那麼必有\(r_{1}<e\)
同理可得\(r_{2}<e\),其中\(x_{2}\cdot e-1=r_{2}\cdot (q-1)\)
可以看到,\(r_{i}<e,i=1,2\),從而可使用試除法求出\(r_{i},i=1,2\)
\(p=(x_{1}\cdot e-1)/r_{1}+1,q=(x_{2}\cdot e-1)/r_{2}+1\)

3. 解題

實現的Python指令碼如下:

from Crypto.Util.number import bytes_to_long,isPrime,inverse
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5

def genKey(X1,X2,X3):
    e=65537L
    N1=X1*e-1
    N2=X2*e-1
    for r in range(e):
        if N1%(e-r)==0:
            p=N1/(e-r)+1
            if isPrime(p):
                break
    for r in range(e):
        if N2%(e-r)==0:
            q=N2/(e-r)+1
            if isPrime(q):
                break
    N=p*q
    phi=(p-1)*(q-1)
    d=inverse(e,phi)
    assert inverse(q,p)==X3
    return RSA.construct((N,e,long(d),p,q))

def solve():
    X1=bytes_to_long('\xd5\xa2%\xc0\xd4\x1b\x16i\x9cDqW\x0e\xec\xd3\xddwYsmW\x81\xaaw\x10\xb3\x1bJF\xe4A\xd3\x86\xda\x13E\xbc\x97\xd1\xaa\x91?\x85?\x85\x0fmF\x84\xa8\x0e`g\xfbq\xcf!;\'l,\xba\xedY')
    X2=bytes_to_long('\x138\xc5\x93\xd3\xb5B\x8c\xe9x\xbe\xd7\xa5SRqU\xb3\xd18\xae\xac\x08@ \xc0\xc6\x7fT\xb9S\x01^U\xf6\n]18e\x05\xe0.a"\xda\xd7\xdb\n\x05\xec\xb5R\xe4H\xb3G\xad\xc2\xc9\x17\x0f\xa2\xf3')
    X3=bytes_to_long('\xd5\xc8\xd6\xdcX>\xcd\xf3\xc3!f;\xa3*\xe4\xab\x1c\x9a-\xedg\x02i\x19\x93\x18B\t\xe99\x14\xf0\xd5\xad\xf4\x15cG\x88\xd5\x91\x9d\x84\xa8\xd7t)\x95\x9d@\xfb\xa4{)\xcfp\xb9C\x12B\x17\xc9\xa41')
    rsa_key=genKey(X1,X2,X3)
    key= PKCS1_v1_5.new(rsa_key)
    with open('flag.enc','rb') as f:
        return key.decrypt(f.read(),'')

if __name__=='__main__':
    print solve()[:-1]

注:這裡之所以猜測e為65537而不是3是因為\(r_{i}<e,i=1,2\),如果e=3可能情況太少。

程式執行結果如下:

$ python solve.py
0ctf{Keep_ca1m_and_s01ve_the_RSA_Eeeequati0n!!!}