DASCTF 2023 & 0X401七月暑期挑战赛-Crypto复现

ezDHKE

题目描述:

from Crypto.Util.number import *
from Crypto.Cipher import AES
from hashlib import sha256
from random import randbytes, getrandbits
from flag import flag
def diffie_hellman(g, p, flag):
    alice = getrandbits(1024)
    bob = getrandbits(1024)
    alice_c = pow(g, alice, p)
    bob_c = pow(g, bob, p)
    print(alice_c , bob_c)
    key = sha256(long_to_bytes(pow(bob_c, alice, p))).digest()
    iv = b"dasctfdasctfdasc"
    aes = AES.new(key, AES.MODE_CBC, iv)
    enc = aes.encrypt(flag)
    print(enc)

def getp():
    p = int(input("P = "))
    assert isPrime(p)
    assert p.bit_length() >= 1024 and p.bit_length() <= 2048
    g = 2
    diffie_hellman(g, p, flag)

getp()

题目分析:

先简化一下题目流程:
y 1 ≡ g a ( m o d p ) y 2 ≡ g b ( m o d p ) k e y ≡ g a ⋅ b ( m o d p ) 已知 y 1 , y 2 , g , p , 求 k e y , 其中 p 自己选择 ( 经典的离散对数问题 D L P ) 只要把 p 选正确了,再使用 d i s c r e t e l o g ( ) , a , b 就能很快出来 当然,这里只要求出 a 或 b 就行,没必要求两个 所以,关键在 p 的选取了 那当然是选 p − 1 光滑数了,因子越小且越多,结果越容易得出 y_1 \equiv g^{a} \pmod{p}\\ y_2 \equiv g^{b} \pmod{p}\\ key \equiv g^{a \cdot b} \pmod{p}\\ 已知y_1,y_2,g,p,求key,其中p自己选择\\ (经典的离散对数问题DLP)\\ 只要把p选正确了,再使用discrete_log(),a,b就能很快出来\\ 当然,这里只要求出a或b就行,没必要求两个\\ 所以,关键在p的选取了\\ 那当然是选p-1光滑数了,因子越小且越多,结果越容易得出 y1ga(modp)y2gb(modp)keygab(modp)已知y1,y2,g,p,key,其中p自己选择(经典的离散对数问题DLP)只要把p选正确了,再使用discretelog()a,b就能很快出来当然,这里只要求出ab就行,没必要求两个所以,关键在p的选取了那当然是选p1光滑数了,因子越小且越多,结果越容易得出
获取光滑数:

# 方法一:
def myPrime(bits):
    while True:
        n = 2
        while n.bit_length() < bits:
            n *= choice(sieve_base)
        if isPrime(n + 1):
            return n + 1
print(myPrime(1024))

# 方法二:
def init():
    primes = []
    p = 1
    while len(primes) < 100:
        p = next_prime(p)
        primes.append(int(p))
    return primes


def genMyPrimeA(bits):
    while True:
        g = 2
        while g < 2 ** bits:
            g *= random.choice(primes)
        g += 1
        if isPrime(g):
            return g
            
primes = init()
print(genMyPrimeA(1024))

# 方法三:
for i in range(10000):
    p = (i<<1024)+1
    if isPrime(p):
        print(p)
        break

方法三为最优解哈哈,毕竟因数中的2又小又多,discrete_log()打一下结果秒出
p出来了,之后便是正常的过程了

# sage
from Crypto.Util.number import *
from hashlib import *
from Crypto.Cipher import AES
p = 2492173519580396207260273583035401721936818532531412721958546193123199183809628015833810174888437077034020940145692006727703873004675257294042241804202346880422458275654783154739252083431240631535327950342922477066016603941003106019151745883988610899820235937614893707165706298487812802385444940496938785114651
A = 715335919016654816446102099094868134297789515423652054915964567424119462834464392719018232157309717997941307040048634222382457383557326993555987301288353821550940739122114753538704714160461728127423897757748356821619714662475183119381090878184436346051629155728469283154001947537385729587825693056234577943934
B = 1190605702713463149128389203183792837151025572940475499564494063011978907664624042211070458987174192231198372936172558351794117665581868503042132460328029054131214174137231671140826217310830060933673159810418076714832162830913822905905701771022323126756138610124206588856655642612360214859329692667930335795861
c = b'\xce\x01 \x8c\xc7p\xb7\x1d)\x0ci[\xd8\xb1\xcc\x86\xaf\x1b\xba.PC\x99a8o\xc2\xfc\xc8\xb5w\xecO\x10\xd6:\x8d\xf0\x16u\xe2\x89\xd9\x00\xa9\x1a\x88u'
G = Zmod(p)
a = discrete_log(G(A), G(2))
key = sha256(long_to_bytes(pow(B,a,p))).digest()
iv = b'dasctfdasctfdasc'
aes = AES.new(key,AES.MODE_CBC,iv)
flag = aes.decrypt(c)
print(flag)
# b'DASCTF{8679096d-8f55-4cf6-8403-14dcafe25e26}'

发现一个有意思的地方,求出来的a.bit_length() = 1018, b.bit_length() = 1021,给出的解释是随机生成的字节转为整数时前导零省略了,这么巧吗,开头能随机生成这么多个0字节。(我去!刚试了几个,还真有这么巧。。。)

ezRSA

题目描述:

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

题目分析:

简化一下:
g i f t = P ⊕ ( Q > > 16 ) c 1 ≡ p o w ( n , e , N ) c 2 ≡ p o w ( s e c r e t , e , n ) c 3 ≡ p o w ( f l a g , e , n ) 已知 g i f t , N , c 1 , c 2 , c 3 , 求 f l a g gift = P \oplus (Q >> 16) \\ c_1 \equiv pow(n, e, N)\\ c_2 \equiv pow(secret, e, n)\\ c_3 \equiv pow(flag, e, n)\\ 已知gift,N,c_1,c_2,c_3,求flag gift=P(Q>>16)c1pow(n,e,N)c2pow(secret,e,n)c3pow(flag,e,n)已知gift,N,c1,c2,c3,flag
只能从gift入手,P高16位已知,又N = P * Q ,那么Q的高位也能得出,考虑到进位问题,可以将Q的高位位数求小一点(假设为x),之后再根据Q的高位和异或性质,又能求出P的高16 + x位,这里取x = 10,如此循环往复,最后大概剩6比特没恢复,枚举下即可,贴下大佬的exp:

gift = 8006730615575401350470175601463518481685396114003290299131469001242636369747855817476589805833427855228149768949773065563676033514362512835553274555294034

pbar = gift >>(512-16)

while True:
    try:
        qbar = (N>>(1024 - pbar.bit_length()*2))//pbar
        print(qbar,qbar.bit_length())
        qbar = qbar>>6
        gifts = gift^(qbar<<(512-16-qbar.bit_length()))
        pbar = gifts >> (512-16-qbar.bit_length())
        print(pbar,pbar.bit_length())
    except:
        break
        
for i in range(64):
    P = (pbar << 6) + i
    if N % P == 0:
        print(P)
        break

(pbar << 6) + i 这一步要注意加括号,+运算级高于<<。我说我怎么总得不到结果,没注意这些细节

之后来到 
flag == b"dasctf{" + secret + b"}"
这一步

已知bytes转long型相当于256进制
假设secret的长度为i
那么

c2 = x ^ 11 
c3 = (bytes_to_long(b'dasctf{' + b'\00' * i + b'}' ) * 256 ** (i + 1) + 256 * x) ^ 11
(其中x = secret , i = len(secret))

典型的Franklin-Reiter相关消息攻击
i i i不知道,爆破一下即可:

from gmpy2 import *
from Crypto.Util.number import *
N = 75000029602085996700582008490482326525611947919932949726582734167668021800854674616074297109962078048435714672088452939300776268788888016125632084529419230038436738761550906906671010312930801751000022200360857089338231002088730471277277319253053479367509575754258003761447489654232217266317081318035524086377
gift = 8006730615575401350470175601463518481685396114003290299131469001242636369747855817476589805833427855228149768949773065563676033514362512835553274555294034
c1 = 14183763184495367653522884147951054630177015952745593358354098952173965560488104213517563098676028516541915855754066719475487503348914181674929072472238449853082118064823835322313680705889432313419976738694317594843046001448855575986413338142129464525633835911168202553914150009081557835620953018542067857943
c2 = 69307306970629523181683439240748426263979206546157895088924929426911355406769672385984829784804673821643976780928024209092360092670457978154309402591145689825571209515868435608753923870043647892816574684663993415796465074027369407799009929334083395577490711236614662941070610575313972839165233651342137645009
c3 = 46997465834324781573963709865566777091686340553483507705539161842460528999282057880362259416654012854237739527277448599755805614622531827257136959664035098209206110290879482726083191005164961200125296999449598766201435057091624225218351537278712880859703730566080874333989361396420522357001928540408351500991
e = 11
P = 8006847171912577069085166877758626954304824756138758266557706391662987806065132448544117840031499707938227955094109779732609035310252723066470330862622641
Q = N // P
d = invert(e,(P-1)*(Q-1))
n1 = int(pow(c1,d,N))
# print(n1)
# n1 = 8410363083727227985204019150296233995423906412694890252698371563789022268553444336554986979907257458547381598181369620318848637391220240378808211998052306324620364339595355706922325759625785590466818309839146408927226283350419069859849879835884942537531811470537915106995685907400782213608736735862576031042
n = 83410392685813224685786027640778560521035854332627839979281105731457044069408118952629284089869335506983096270269822559619624906180108256504440296527471536363057103101146262613593336072556587341466840510200003498265457285439149541137127199088938421905041387224795918868443175561632999479925818053898100117419
def GCD(a,b):
    if b == 0:
        return a.monic()
    else:
        return GCD(b,a % b)
PR.<x> = PolynomialRing(Zmod(n))
for i in range(50):
    f1 = x ^ 11 - c2
    f2 = (bytes_to_long(b'dasctf{' + b'\x00' * i + b'}') + 256 * x) ^ 11 - c3
    if GCD(f1,f2)[0] != 1:
        print(long_to_bytes(int(n - GCD(f1,f2)[0])))

注意此时得到的n1并不是最后的n, 看到最后一位是2就知道,两个素数相乘怎么可能得出一位偶数,所以这里再加个N即可得到n,这样也便分解不了了

ezAlgebra

题目描述:

from Crypto.Util.number import getPrime, bytes_to_long

def YiJiuJiuQiNian(Wo, Xue, Hui, Le, Kai):
    Qi = 1997
    Che = Wo+Hui if Le==1 else Wo*Hui
    while(Xue):
        Qi += (pow(Che, Xue, Kai)) % Kai
        Xue -= 1
    return Qi
    
l = 512
m = bytes_to_long(flag)
p = getPrime(l)
q = getPrime(l//2)
r = getPrime(l//2)
n = p * q * r
t = getrandbits(32)
c1 = YiJiuJiuQiNian(t, 4, p, 1, n)
c2 = YiJiuJiuQiNian(m, 19, t, 0, q)
c3 = YiJiuJiuQiNian(m, 19, t, 1, q)
print(f"n = {n}")
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"c3 = {c3}")

"""
n = 119156144845956004769507478085325079414190248780654060840257869477965140304727088685316579445017214576182010373548273474121727778923582544853293534996805340795355149795694121455249972628980952137874014208209750135683003125079012121116063371902985706907482988687895813788980275896804461285403779036508897592103
c1 = 185012145382155564763088060801282407144264652101028110644849089283749320447842262397065972319766119386744305208284231153853897076876529326779092899879401876069911627013491974285327376378421323298147156687497709488102574369005495618201253946225697404932436143348932178069698091761601958275626264379615139864425
c2 = 722022978284031841958768129010024257235769706227005483829360633993196299360813
c3 = 999691052172645326792013409327337026208773437618455136594949462410165608463231
"""

题目分析:

简化一下:
n = p q r c 1 = ( t + p ) 4 + ( t + p ) 3 + ( t + p ) 2 + ( t + p ) + 1997 ( m o d n ) c 2 = ( m t ) 19 + ( m t ) 18 + ⋯ + ( m t ) 2 + ( m t ) + 1997 ( m o d q ) c 3 = ( m + t ) 19 + ( m + t ) 18 + ⋯ + ( m + t ) + 1997 ( m o d q ) 已知 n , c 1 , c 2 , c 3 , 求 m n = pqr \\ c_1 = (t + p)^{4} + (t + p)^{3} + (t + p)^{2} + (t + p) + 1997 \pmod{n} \\ c_2 = (mt)^{19} + (mt)^{18} + \cdots + (mt)^{2} + (mt) + 1997 \pmod{q}\\ c_3 = (m + t)^{19} + (m + t)^{18} + \cdots + (m + t) + 1997 \pmod{q}\\ 已知n,c_1,c_2,c_3,求m n=pqrc1=(t+p)4+(t+p)3+(t+p)2+(t+p)+1997(modn)c2=(mt)19+(mt)18++(mt)2+(mt)+1997(modq)c3=(m+t)19+(m+t)18++(m+t)+1997(modq)已知n,c1,c2,c3,m
c 1 = t 4 + t 3 + t 2 + t + 1997 ( m o d p ) = > k p = c 1 − ( t 4 + t 3 + t 2 + t + 1997 ) 联想到 p 的高位攻击 此处相当于高位已知,求低位, c o p p e r 解未知数 t 求出后带入原式, g c d ( k p , n ) 求出 p c_1 = t^{4} + t^{3}+ t^{2}+ t + 1997 \pmod{p}\\ =>\\ kp = c_1 - (t^{4} + t^{3}+ t^{2}+ t + 1997)\\ 联想到p的高位攻击 \\此处相当于高位已知,求低位,copper解未知数t\\ 求出后带入原式,gcd(kp,n)求出p c1=t4+t3+t2+t+1997(modp)=>kp=c1(t4+t3+t2+t+1997)联想到p的高位攻击此处相当于高位已知,求低位,copper解未知数t求出后带入原式,gcd(kp,n)求出p

# sage
from Crypto.Util.number import *
n =  119156144845956004769507478085325079414190248780654060840257869477965140304727088685316579445017214576182010373548273474121727778923582544853293534996805340795355149795694121455249972628980952137874014208209750135683003125079012121116063371902985706907482988687895813788980275896804461285403779036508897592103
c1 = 185012145382155564763088060801282407144264652101028110644849089283749320447842262397065972319766119386744305208284231153853897076876529326779092899879401876069911627013491974285327376378421323298147156687497709488102574369005495618201253946225697404932436143348932178069698091761601958275626264379615139864425
c2 = 722022978284031841958768129010024257235769706227005483829360633993196299360813
c3 = 999691052172645326792013409327337026208773437618455136594949462410165608463231
PR.<x> = PolynomialRing(Zmod(n))
f = x ^ 4 + x ^ 3 + x ^ 2 + x + 1997 - c1
t = f.small_roots(X = 2 ^ 32,beta = 0.4)[0]
# print(t)
t = 2915836867
kp = t ^ 4 + t ^ 3 + t ^ 2 + t + 1997 - c1
p = gcd(kp,n)
qr = n // p

观察到 c 2 = ( m t ) 19 + ( m t ) 18 + ⋯ + ( m t ) 2 + ( m t ) + 1997 ( m o d q ) c 3 = ( m + t ) 19 + ( m + t ) 18 + ⋯ + ( m + t ) + 1997 ( m o d q ) 这形式不跟题二差不多,不过是项数多了点 但 ( x − m ) 还是 c 2 , c 3 的解,公因子还是 ( x − m ) 观察到\\ c_2 = (mt)^{19} + (mt)^{18} + \cdots + (mt)^{2} + (mt) + 1997 \pmod{q}\\ c_3 = (m + t)^{19} + (m + t)^{18} + \cdots + (m + t) + 1997 \pmod{q}\\ 这形式不跟题二差不多,不过是项数多了点\\但(x-m)还是c_2,c_3的解,公因子还是(x-m) 观察到c2=(mt)19+(mt)18++(mt)2+(mt)+1997(modq)c3=(m+t)19+(m+t)18++(m+t)+1997(modq)这形式不跟题二差不多,不过是项数多了点(xm)还是c2,c3的解,公因子还是(xm)
可利用groebner_basis()求出q(理解groebner_basis()可参考此文章
我的理解:打印出的结果是多项式的解集,最后的那位纯数字是多项式理想G所对应的代数集合的维度,即所生成的代数集合中的点的个数,正好可对应于q,这样好像好理解点

from Crypto.Util.number import *
n =  119156144845956004769507478085325079414190248780654060840257869477965140304727088685316579445017214576182010373548273474121727778923582544853293534996805340795355149795694121455249972628980952137874014208209750135683003125079012121116063371902985706907482988687895813788980275896804461285403779036508897592103
c1 = 185012145382155564763088060801282407144264652101028110644849089283749320447842262397065972319766119386744305208284231153853897076876529326779092899879401876069911627013491974285327376378421323298147156687497709488102574369005495618201253946225697404932436143348932178069698091761601958275626264379615139864425
c2 = 722022978284031841958768129010024257235769706227005483829360633993196299360813
c3 = 999691052172645326792013409327337026208773437618455136594949462410165608463231
PR.<x> = PolynomialRing(Zmod(n))
f = x ^ 4 + x ^ 3 + x ^ 2 + x + 1997 - c1
t = f.small_roots(X = 2 ^ 32,beta = 0.4)[0]
# print(t)
t = 2915836867
kp = t ^ 4 + t ^ 3 + t ^ 2 + t + 1997 - c1
p = gcd(kp,n)
qr = n // p

P.<x,y> = PolynomialRing(Zmod(n))
f1 = 1997 - c3
f2 = 1997 - c2
for i in range(1,20):
    f1 += (x + t)^i
    f2 += (x * t)^i
G = [f1,f2]
B = Ideal(G).groebner_basis()
res = [x.constant_coefficient() for x in B]
q = res[1]
m = -res[0] % q
print(m)
print(q)

求出m转字符,发现得不到结果,结果是m % q,猜测比q大,爆破一下。这里其实有点。。。爆破结果是以小写dasctf开头,上传的答案又要求大写DASCTF。难说。。。

q = 87038069032840052005520908272237788908169043580221040711149494083975743478969
x = 56985796272753226120469211992443340429346162287195965942430959147227534853120
for i in range(10000000):
    flag = long_to_bytes(x + i * q)
    if b'dasctf' in flag :
        print(flag)
        print(i)
# dasctf{ShangPoXiaPoYaSiLeYiQianDuo} 
# i = 8751845 24bits

也可以像上一题一样用GCD求,把域换成q就行
至此,复现完毕!

浅记一下

关键词:光滑数,Franklin-Reiter相关消息攻击,copper求t,groebner_basis(),+运算级高于<< (bushi

测试了一下,第二题也能用groebner_basis()求解,前提是i已知,i得出是31,用groebner_basis()求解挺快的哈哈。
第二题i未知的话肯定不适合这个的方法,但若是正常求解的话,题三的求解方法可能更应该考虑其中🤔(个人想法)。注意 此方法至少要传入两个参数x,y不然会报错

P.<x,y> = PolynomialRing(Zmod(n))
f1 = x ^ 11 - c2
f2 = (bytes_to_long(b'dasctf{' + b'\x00' * 31 + b'}') + 256 * x) ^ 11 - c3  
G = [f1,f2]
B = Ideal(G).groebner_basis()
res = [x.constant_coefficient() for x in B]
print(B)
print(long_to_bytes(int(n - res[0])))

之前做过相关消息攻击的题,但都是那种一眼就能看出来的,这次遇到两道不寻常的,值得记录,求解方法也更加多元化了哈哈,只能说还有待提高,继续加油吧!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值