window.onload=function(){ /*页面加载完成之后生成博客目录*/ BlogDirectory.createBlogDirectory("cnblogs_post_body","h2","h3",20); }

DASCTF 2023 & 0X401七月暑期挑战赛——ezRSA

1、题目描述:

 

查看代码
from Crypto.Util.number import *
from secret import secret, flag
def encrypt(m):
    return pow(m, e, n)
assert flag == b"dasctf{" + secret + b"}"
e = 11
p = getPrime(512)
q = getPrime(512)
n = p * q
P = getPrime(512)
Q = getPrime(512)
N = P * Q
gift = P ^ (Q >> 16)
print(N, gift, pow(n, e, N))
print(encrypt(bytes_to_long(secret)),
    encrypt(bytes_to_long(flag)))

N = 75000029602085996700582008490482326525611947919932949726582734167668021800854674616074297109962078048435714672088452939300776268788888016125632084529419230038436738761550906906671010312930801751000022200360857089338231002088730471277277319253053479367509575754258003761447489654232217266317081318035524086377
gift = 8006730615575401350470175601463518481685396114003290299131469001242636369747855817476589805833427855228149768949773065563676033514362512835553274555294034
c1 = 14183763184495367653522884147951054630177015952745593358354098952173965560488104213517563098676028516541915855754066719475487503348914181674929072472238449853082118064823835322313680705889432313419976738694317594843046001448855575986413338142129464525633835911168202553914150009081557835620953018542067857943
c2 = 69307306970629523181683439240748426263979206546157895088924929426911355406769672385984829784804673821643976780928024209092360092670457978154309402591145689825571209515868435608753923870043647892816574684663993415796465074027369407799009929334083395577490711236614662941070610575313972839165233651342137645009
c3 = 46997465834324781573963709865566777091686340553483507705539161842460528999282057880362259416654012854237739527277448599755805614622531827257136959664035098209206110290879482726083191005164961200125296999449598766201435057091624225218351537278712880859703730566080874333989361396420522357001928540408351500991

2、题目分析:

源代码很短,分析一下可以发现分了两个RSA,一个用小写表示,一个用大写表示,小写n用大写加密,大写的给出了P和Q>>16的提示。

原理很简单:爆破+裁剪

因为xor只有4种情况,对结果0是两个0或者两个1,对1是01或10然后递归下去。

关键是裁剪,就是让一些情况提示返回失败,这里也是两种情况,对于已知的tp,tq

如果tp*tq>N或者(tp+(1<<k)-1)*(tq+(1<<k)-1)<N就会直接退出(提前判断一下),然后由于Q只是一部分,直接爆破到N%P==0即可

from Crypto.Util.number import *
 
N = 75000029602085996700582008490482326525611947919932949726582734167668021800854674616074297109962078048435714672088452939300776268788888016125632084529419230038436738761550906906671010312930801751000022200360857089338231002088730471277277319253053479367509575754258003761447489654232217266317081318035524086377 
gift = 8006730615575401350470175601463518481685396114003290299131469001242636369747855817476589805833427855228149768949773065563676033514362512835553274555294034 
C =14183763184495367653522884147951054630177015952745593358354098952173965560488104213517563098676028516541915855754066719475487503348914181674929072472238449853082118064823835322313680705889432313419976738694317594843046001448855575986413338142129464525633835911168202553914150009081557835620953018542067857943
C1 = 69307306970629523181683439240748426263979206546157895088924929426911355406769672385984829784804673821643976780928024209092360092670457978154309402591145689825571209515868435608753923870043647892816574684663993415796465074027369407799009929334083395577490711236614662941070610575313972839165233651342137645009 
C2 =46997465834324781573963709865566777091686340553483507705539161842460528999282057880362259416654012854237739527277448599755805614622531827257136959664035098209206110290879482726083191005164961200125296999449598766201435057091624225218351537278712880859703730566080874333989361396420522357001928540408351500991
 
ph = bin(gift)[2:][:16] + '0'*(512-16)
ph = int(ph,2)
x = bin(gift)[2:][17:]
 
def fac(x,tp,tq):
    if len(x) == 0:
        return
    if tp*tq>N:
        return 
    if N%(tp+1)==0:
        print(tp+1)
        return
    
    v = x[0]
    r = x[1:]
    l = len(r)
    
    if (tp+(1<<(l+1)))*(tq+(1<<(l+17)))<N:
        print(bin(tp)[:50])
        print(bin(tq)[:50])
        print(l)
        return
        
        
    if v == '0':
       fac(r, tp, tq)
       fac(r, tp+(1<<l), tq+(1<<(l+16)))
    else:
       fac(r, tp+(1<<l), tq)
       fac(r, tp, tq+(1<<(l+16)))
 
#q第1位为1 x[0]=='0'
tq = 1<<511
tp = ph + (1<<(512-16-1))
 
fac(x,tp,tq)
 
P = 8006847171912577069085166877758626954304824756138758266557706391662987806065132448544117840031499707938227955094109779732609035310252723066470330862622641
Q = N//P 
e = 11 
#pow(n, e, N) = C 
n = pow(C,invert(e, (P-1)*(Q-1)), N)
#8410363083727227985204019150296233995423906412694890252698371563789022268553444336554986979907257458547381598181369620318848637391220240378808211998052306324620364339595355706922325759625785590466818309839146408927226283350419069859849879835884942537531811470537915106995685907400782213608736735862576031042

如图:

求出n以后是个关联信息攻击,只不过平时见到的一般与短填充攻击一起,两个差非常小,而这个非常大。

同时由于flag长度未知,小于n的话只能是5字节,显然不正确需要爆破一下。

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x*256+diff)^e - c2
    
    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()
    
    return -gcd(g1, g2)[0]
 
for i in range(5,200):
    sl = i*8
    diff = bytes_to_long(b"dasctf{")*2^(sl+8) + bytes_to_long(b"}")
    #print(long_to_bytes(diff))
    v = related_message_attack(C1, C2, diff, e, n)
    v = long_to_bytes(int(v))
    if all(0x20<=k<=0x7f for k in v):
        print(v)
 
 
#C0pper_Sm1th_Mak3s_T1ng5_Bet4er
#DASCTF{C0pper_Sm1th_Mak3s_T1ng5_Bet4er}

 

posted @ 2023-08-10 19:46  Kicky_Mu  阅读(208)  评论(0编辑  收藏  举报