HZNUCTF2023 crypto sec

HZNUCTF2023 预赛

easyDSA


def solve_k(l):
    for i in range(512):
        if (l + 1 + i - 448) % 512 == 0:
            return i

def padding(msg):
    l = len(msg)
    k = solve_k(l)
    l64 = bin(l)[2:].rjust(64, '0')  # what if msg is too long, how?
    # so mention the condition in guideline of Algorithm
    msg = msg + '1' + '0' * k + l64
    assert len(msg) % 512 == 0
    return msg, l, k

def iteration():
    pass

def ring_shift_left(x, num):
    x = bin(x)[2:].rjust(32, '0')               # shift in 32 bit, take care of it
    x = int(x[num:] + x[:num], 2)
    return x

def p0(x):
    return x ^ ring_shift_left(x, 9) ^ ring_shift_left(x, 17)


def p1(x):
    return x ^ ring_shift_left(x, 15) ^ ring_shift_left(x, 23)


def extending(msg):
    WW = []
    for i in range(len(msg) // 512):
        W = ['0' for _ in range(132)]
        msgi = msg[i * 512:(i + 1) * 512]
        # 512 ====> 16 * 32
        for ii in range(len(msgi) // 32):
            W[ii] = msgi[ii * 32:(ii + 1) * 32]
            assert len(W[ii]) == 32
        for j in range(16, 68):
            # how to xor word, change to number or bytes?
            # number, of course
            W[j] = p1(int(W[j - 16], 2) ^ int(W[j - 9], 2) ^ ring_shift_left(int(W[j - 3], 2), 15)) ^ \
                   ring_shift_left(int(W[j - 13], 2), 7) ^ int(W[j - 6], 2)
            W[j] = bin(W[j])[2:]
            W[j] = W[j].rjust(32, '0')
        for j in range(68, 132):
            W[j] = int(W[j - 68], 2) ^ int(W[j - 68 + 4], 2)
            W[j] = bin(W[j])[2:]
            W[j] = W[j].rjust(32, '0')
        WW.append(W)
    return WW


def cons(j):
    if 0 <= j <= 15:
        return 0x79cc4519
    elif 16 <= j <= 63:
        return 0x7a879d8a


def bool_ff(param, j):
    x, y, z = param
    if 0 <= j <= 15:
        return x ^ y ^ z
    elif 16 <= j <= 63:
        return (x & y) | (x & z) | (y & z)


def bool_gg(param, j):
    x, y, z = param
    if 0 <= j <= 15:
        return x ^ y ^ z
    elif 16 <= j <= 63:                # take care of not
        if len(bin(x)[2:]) < 32:
            ans = ''
            x = bin(x)[2:].rjust(32, '0')
            y = bin(y)[2:].rjust(32, '0')
            z = bin(z)[2:].rjust(32, '0')
            for i in range(0, 32):
                if x[i] == '0':
                    ans += str((int(x[i], 2) & int(y[i], 2)) | (1 & int(z[i], 2)))
                elif x[i] == '1':
                    ans += str((int(x[i], 2) & int(y[i], 2)) | (0 & int(z[i], 2)))
            return int(ans, 2)
        elif len(bin(x)[2:]) == 32:
            return (x & y) | (~x & z)


def cf(v, w):
    W, W_ = w[:68], w[68:]
    # for i in W:
    #     print(hex(int(i, 2)), end=' ')
    # for i in W_:
    #     print(hex(int(i, 2)), end=' ')
    # print()
    v = bin(v)[2:].rjust(256, '0')
    A, B, C, D, E, F, G, H = [v[_ * 32:(_ + 1) * 32] for _ in range(256 // 32)]
    for j in range(64):
        # be care of plus, it may surpass 32 bits
        # remember j mod 32, or output will be wrong
        tmp = (ring_shift_left(int(A, 2), 12) + int(E, 2) + ring_shift_left(cons(j), j % 32)) & 0xffffffff
        SS1 = ring_shift_left(tmp, 7)
        SS1 = bin(SS1)[2:].rjust(32, '0')

        SS2 = int(SS1, 2) ^ ring_shift_left(int(A, 2), 12)
        SS2 = bin(SS2)[2:].rjust(32, '0')

        tmp = (bool_ff([int(A, 2), int(B, 2), int(C, 2)], j) + int(D, 2) + int(SS2, 2) + int(W_[j], 2)) & 0xffffffff
        TT1 = bin(tmp)[2:].rjust(32, '0')

        tmp = (bool_gg([int(E, 2), int(F, 2), int(G, 2)], j) + int(H, 2) + int(SS1, 2) + int(W[j], 2)) & 0xffffffff
        TT2 = bin(tmp)[2:].rjust(32, '0')

        D = C.rjust(32, '0')
        C = ring_shift_left(int(B, 2), 9)
        C = bin(C)[2:].rjust(32, '0')
        B = A.rjust(32, '0')
        A = TT1.rjust(32, '0')
        H = G.rjust(32, '0')
        G = ring_shift_left(int(F, 2), 19)
        G = bin(G)[2:].rjust(32, '0')
        F = E.rjust(32, '0')
        E = p0(int(TT2, 2))
        E = bin(E)[2:].rjust(32, '0')

        # print(j, end=' ')
        # for i in [A, B, C, D, E, F, G, H]:
        #     print(hex(int(i, 2)), end=' ')
        # print()
    ans = A + B + C + D + E + F + G + H
    ans = int(ans, 2)
    return ans


def SM3(msg):
    msg = bytes_to_long(msg)
    if msg.bit_length() % 4 != 0:
        msg = (4 - (msg.bit_length() % 4)) * '0' + bin(msg)[2:]
    msg, l, k = padding(msg)

    # extend
    W = extending(msg)

    # iteration
    vi = IV
    n = (l + k + 65) // 512
    for i in range(n):
        res = cf(vi, W[i])
        vi = vi ^ res
    return hex(vi)[2:]

import itertools
def small_roots(f, bounds, m=1, d=None):
    if not d:
        d = f.degree()

        R = f.base_ring()
        N = R.cardinality()

        f /= f.coefficients().pop(0)
        f = f.change_ring(ZZ)

        G = Sequence([], f.parent())
        for i in range(m + 1):
            base = N ^ (m - i) * f ^ i
            for shifts in itertools.product(range(d), repeat=f.nvariables()):
                g = base * prod(map(power, f.variables(), shifts))
                G.append(g)

        B, monomials = G.coefficient_matrix()
        monomials = vector(monomials)

        factors = [monomial(*bounds) for monomial in monomials]
        for i, factor in enumerate(factors):
            B.rescale_col(i, factor)

        B = B.dense_matrix().LLL()

        B = B.change_ring(QQ)
        for i, factor in enumerate(factors):
            B.rescale_col(i, 1 / factor)

        H = Sequence([], f.parent().change_ring(QQ))
        for h in filter(None, B * monomials):
            H.append(h)
            I = H.ideal()
            if I.dimension() == -1:
                H.pop()
            elif I.dimension() == 0:
                roots = []
                for root in I.variety(ring=ZZ):
                    root = tuple(R(root[var]) for var in f.variables())
                    roots.append(root)
                return roots
        return []



from Crypto.Util.number import *
(r1, s1) = (43665657147136977892760835332544097729763754398125679419859037123212964274095, 11372107439153704547599978617809027960018057676066118055075660375442954789009)
(r2, s2) = (29184887007213204285288676779168140587575609668559831035949650649308618592275, 5011738292572181542092375902756977363590922060964162373234404450451520414798)
p = 31961141251107494919420190534228520246958409864267239760354623819192809291490262139213317490432416411403367763443527530375117617196123131270496004125231254335150221348901335274505489844222882171272650010562960614279185073793274638651086760235178963210965828168433516820007716846876686795459738332444629111764967204355463398049697867061034126529189537688874999118692225915790053920062142349951686250122300061810240375783724631961234942175580462986265098353263395579346466921241016500821787793395554444982717141449909744838267161237273856377774256250949274635575801148994817767751541256849860886577256992383324866941911
q = 69375998045163628324086568160767337544901252262545889505892695427466730978301
g = 23095306638137759877487469277470910487928442296144598697677211337473146684728707820084075779044942034329888686699655576145455963231144004571165817481066424910959951439014314776050521403558035997997820617824839889597136772108383034876458141163933312284054415480674388788905935457149956424898637134087874179010376667509489926236214865373552518669840236207944772752416668193786003948717604980584661094548997197117467440864460714843246250800575997370964173558788145639802963655916833143883799542309432910222224223561677245110195809587171802538978009246887077924173034608600837785506594525481696000424121705524449481831586
y = 30195133393879069638917191223585579396119430591488890396938821804398771785068454607425044458865556053274470709839502680269466948174813926392729790863065933078609827279352860810689776644132512095691760326095517755483748554008211568781998662554432781285208646921699265866446498342049913829592480268053599307065979016922204438675164034767731708343084371572648019835171087671868322447023378942812010740490724160077164191297435291229504616686997442254543493394641023587237077429236872101951650325361004443988267286616139798736713430746804524113024341440435623834197278500144543476528466395780355874841379098027115073850819

IV = 0x7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e
default_hm1 = b'HZNUCTFRound#1'
default_hm2 = b'HZNUCTFRound#1'

hm1 = int(SM3(default_hm1), 16)
hm2 = int(SM3(default_hm2), 16)
hm =19905280947443115569469777697852124038269468456842113763109865796452965095134

roots = [15744441039285451081, 631339]
k1 = int(roots[0])
k2 = k1**2+roots[1]
x = (s1*k1-hm)*inverse(r1,q)%q
print(hex(x))
print(x)
print(long_to_bytes(x))
#HZNUCTF{JU57_@N_e@SY_5QU4RE_K!}

用rust写的简单仿射密码


from Crypto.Util.number import *
with open('C:\\Users\\86153\\Desktop\\2.txt','rb') as f:
    ciphertext = f.readlines()

c = b''.join(ciphertext).decode()
print(c)


flag = ''
increment = 52
inc = []
for i in c:
    m = ((ord(i)-increment)*inverse(37,127))%127
    increment += ord(i)%127
    inc.append(52)
    inc.append(increment)
    flag += chr(m)

print(inc)
print(flag)

rabinOT

不经意传输1对1,


from Crypto.Util.number import *
from pwn import remote
from gmpy2 import gcd,iroot
e = 65537

x = 9
for i in range(400):
    re = remote('43.156.230.30', 10001)
    re.recvuntil(b'n = ')

    n  =int(re.recvline().decode().strip())
    re.recvuntil(b'c = ')

    c = int(re.recvline().decode().strip())
    re.sendline(str(x).encode())

    re.recvuntil(b'a =')
    a = int(re.recvline().decode().strip())
    p = gcd(a-3,n)
    print(p)
    if p>1:
        print(a)
        q = n//p
        print(p)
        print(q)
        phi = (p-1)*(q-1)
        d = inverse(e,phi)
        print(long_to_bytes(pow(c,d,n)))
        break

re.interactive()


side-channel

侧信道攻击 ,HITB 2017 Hack in the card I ,新加坡ctf赛中的原题,这里只是换成了高位到低位的快速幂。原题wp阐述的非常详细,这篇不再做过多描述

from random import randint
from Crypto.Util.number import *
e = 16059123945822694365158124950198825543484999519589187888337200553255798544440036832262284563838976515808578129291536616599557509264533073851159103105883072060720056631597958188429575916683604682603283361472163393387285175934398999920713460521688171388856936854606011599111757398384341169379550343011380822651520763942589923801782398753464143234709393484148664365254994860713155476167800961464275898199226390381810969888945023697984476906037653190402170336314921539242675073226168525550913706593532778562652428903475444637793556112819371434228940595513275038805389841060879447191510666943970447241850404552926199602519
n = 23676198353119272891587164142576908690376901293686314942784353146059194045274633032364567515126717515053410457120247729623176193595913335169727137670521232494953911307651873224240918425478358455021118904693825635883056533658237193708645648054998837744523526377375370015128407346231287149043212742478188696880965471297198589414377055357200554254450863749395859378259685909927883404933139662201392110242455689394258060803403855280967030411722557398887495577523627779690177862295719147486918280447692742391730411876509056043404048460252304050528824186627531118009773552255554195757232387432726685926648422550200240296689
c = 0x6a415f728907e12e2af16275da3de93f29640272116b61e36963241eabe10d63b40ace499ed4b5228acc331ff43eabae8c2ffc3ad563397dc9f67e1156000501ca74dddcb540b4de9ef5ddfd1cfc30ff012ea6542bc41c59e694e227d0f2b0e6f3e242df0e1484b02715afefe7ade7375bdd39f9230b705b63efa6d1b11e68caacb5e0dbd83c8204d2cb029722b1cf5ab148a5be7bab10519c0e2f12db9195fa6e2a974f2d6c16aa0cae44d8090c821ee3a344c0756e23cc6d1932155a334648a4a06285b80d4961c3652dba963107cb10e8b7dee5ebd677529486c8b54d230cd65e015767d23b9c79ed2d1a5a068e907b285a4ad65e4b0c2de727252a85f480


with open('C:\\Users\\86153\\Desktop\\data.txt') as f:
    data =f.read().split(',')

data = [eval(i) for i in data]
print('point number:', len(data))

start_point = 0   # 开始分析的点
mid = 50            # 采样点间隔
fence = 228         # 高低电平分界线

bin_array = []

for point_index in range(start_point, len(data), mid):
    if data[point_index] > fence:
        bin_array.append(1)
    else:
        bin_array.append(0)

bin_array2 = []
flag1 = 0
flag2 = 0
for x in bin_array:
    if x:
        if flag1:
            flag2 = 1
        else:
            flag1 = 1
    else:
        if flag2:
            bin_array2.append(1)
        else:
            bin_array2.append(0)
        flag1 = 0
        flag2 = 0

def power_mod(a, b, c):
    ans = 1
    a %= c
    while b > 0:
        if b % 2 == 1:
            ans = (ans * a) % c
        b //= 2
        a = (a * a) % c
    return ans

# d_bin = bin_array2[::-1]
d_bin = bin_array2
d = "".join(str(x) for x in d_bin)
print(d)
d_int = int(d,2)
print(d_int)
print(long_to_bytes(power_mod(c,d_int,n)))
for _ in range(100):
    m = randint(0, n - 1)
    c = pow(m, e, n)
    assert power_mod(c, d_int, n) == m or pow(c,d_int,n)==m
print('d =', d_int)

你的背包

没写出来,有空补一下。

决赛

1.一步到位

pell方程求解,n = b//a,详细情况建议看la佬博客。

from Crypto.Util.number import *

p = 9850212100620338486478343799784951721581757936621772924971218807300819701941457605399099898870264241518769370682330612103782092302148525012450902351701339
q = 10749429992014823019923966246896511618886613763258781706004694804949547801668777655988055847885755337127548775758133804022361510427909703124161450470578543
c = 66847321508502017574023222490247591875197038421108556106531660421662626233521063441647157067220450229816184622038471812597874582613385516838920632450015292570673816423432903604941781889308906893966588610214614726388822851471695742453496232748358301888465563812947038856742838097152549971517159475947566599664
a = 1293023064232431070902426583269468463
b = 105279230912868770223946474836383391725923
x = 706458746417678962043621845971467865659328419354757470370053791959400836343459667183452696881749102272803495561977766862981211957547107314899837090421877848369768126470488899783907413049018564965473411482320934865623338739674557285559179500448017722749519381238449907463225595410791751764554609076679816077991393296019025846726102143252917723127850421484059625048891687413514675773883846965460650321320194376953023128886680992904874680874592398084000621265936352579085146973168432725924933271736161246147210167219273151533549827720768127049
y  = 2475817283208353655376575253824978610318795084158266693908795228437881838494879684898106300899685369783448887135772502375256424485378197701618246093404377305042144695942832777562138071372439272074221303824090898475856064751103068366460943484199764781203337540043797645872932430695841756685875430274803587032466826238694472049259595749567628655789857218392048459841779867350496912185026568130525427572795745011809179300697763422087575586151631990884275732162078972870311906763206309891911795517651173639547353468670434655436993030736217980
assert 1293023064232431070902426583269468463*pow(x,2)==105279230912868770223946474836383391725923*pow(y,2)+1293023064232431070902426583269468463
def solve_pell(N, numTry = 100):
    cf = continued_fraction(sqrt(N))
    for i in range(numTry):
        denom = cf.denominator(i)
        numer = cf.numerator(i)
        if numer^2 - N * denom^2 == 1:
            return numer, denom
    return None, None

N = b//a
x,y = solve_pell(N)

phi = (p-1)*(q-1)
d = inverse(x,phi)
print(long_to_bytes(pow(c,d,p*q)))

checkin

第一步先求出p

接下来就是求24000次二次剩余,基础不扎实,比赛时候没写出来。由于m又padding了大约400比特的字节码,所以m>p,要用到crt扩一下模数,没仔细审题。

n = 22114546923564420607945063747927422619774890007937503484905798897563036431278699718161460968350749338680452479484253816646632515078192048118109272532310403715802657061990320170724360874028667484527150185159662403573637809180151665727445208585725264186578429094937215068881079399747998551453944363665401263
c = 7274219309267176700435453490636404568410293850833252412471984274955007037941820465929958008672185817002749418809077052781794306899476543760452010370102811167685901654480233874375880047900499814304539829706786470978714629827690730256369200773772396109793338097451559255985268375731804819829315168807228186
h = 1463929459818798711929811606552273520156490689917243949474579232718301828387871678397965433435537694532920957475947201372279363554005600100100224291888130
hint = 5610276127312766429915480651516095336201056367031530733662980757514427535721885723009367286870294772595629284861923351543396909892645845139050780691701736
e = 1<<24000

nn = pow(2,hint,n)
p =gcd(nn-h,n)
q = n//int(p)


def square_root_of_quadratic_residue(n, modulo):
    """Square root of quadratic residue

    Solve the square root of quadratic residue using Cipolla's algorithm with Legendre symbol
    Returns:
        int -- if n is a quadratic residue,
                   return x, such that x^{2} = n (mod modulo)
               otherwise, return -1
    """
    if modulo == 2:
        return 1
    if n % modulo == 0:
        return 0
    Legendre = lambda n: pow(n, modulo - 1 >> 1, modulo)
    if Legendre(n) == modulo - 1:
        return -1
    t = 0
    while Legendre(t ** 2 - n) != modulo - 1:
        t += 1
    w = (t ** 2 - n) % modulo
    return (generate_quadratic_field(w, modulo)(t, 1) ** (modulo + 1 >> 1)).x


def generate_quadratic_field(d, modulo=0):
    """Generate quadratic field number class

    Returns:
        class -- quadratic field number class
    """

    assert (isinstance(modulo, int) and modulo >= 0)

    class QuadraticFieldNumber:
        def __init__(self, x, y):
            self.x = x % modulo
            self.y = y % modulo

        def __mul__(self, another):
            x = self.x * another.x + d * self.y * another.y
            y = self.x * another.y + self.y * another.x
            return self.__class__(x, y)

        def __pow__(self, exponent):
            result = self.__class__(1, 0)
            if exponent:
                temporary = self.__class__(self.x, self.y)
                while exponent:
                    if exponent & 1:
                        result *= temporary
                    temporary *= temporary
                    exponent >>= 1
            return result

        def __str__(self):
            return '({}, {} \\sqrt({}))'.format(self.x, self.y, d)
    return QuadraticFieldNumber


res1 = [c]
total = []
res2 = c
for i in tqdm(range(24000)):
    tmp, res1 = res1, []
    for ct in tmp:
        mm = square_root_of_quadratic_residue(ct,p)
        if mm not in total:
            total.append(mm)
        else: continue

        if p-mm not in total:
            total.append(p-mm)
        else: continue

        if mm == 1 or mm == p - 1 or mm == -1: continue
        res1 += [mm,p-mm]
    res1 = list(set(res1))
res1 = res1[-2:]

res2 = c
for i in tqdm(range(24000)):
    res2 = pow(res2, (q + 1) // 4, q)
res2 = [res2, p - res2]

for i in res1:
    for j in res2:
        m = crt([p,q],[i,j])
        m = long_to_bytes(int(m))
        if b'HZNU' in m:
            print(m)

#HZNUCTF{80f937af-6542-4142-b957-09534839da4d}

有限域硬开感觉可行度不高,光开p就要2个小时,开完还要和q的剩余crt。由于下午三点才更新的附件,比赛结束后一个小时才开出来p的剩余。

3.nothing

基于ecDSA 一道hnp问题,借鉴与红明谷2022 的hnp。

最多只能连56次,所以获得55次sign,再得到一次验证。

已知签名式

from pwn import  remote
from tqdm import tqdm
from Crypto.Util.number import *
import string
from hashlib import sha256
from itertools import product
host = '43.156.230.30'
port = 10001
re = remote(host,port)



space = string.digits + string.ascii_letters
re.recvuntil(b'XXXX+')
tail = re.recv(16).decode()
re.recvuntil(b' == ')
proof = re.recv(64).decode()

for i in product(space, repeat=4):
    head = ''.join(i)
    flag = head + tail
    if sha256(flag.encode()).hexdigest() == proof:
        re.sendline(head.encode())
        break

re.recvuntil(b'[*]')
re.sendline(b'y')
rs = []
t = 55
for i in tqdm(range(t)):
    re.recvuntil(b'[*]')
    re.sendline(b'1')
    re.recvuntil(b'[+] U can get my signature\n[+] ')
    rs.append(re.recv(128).decode())
print(rs)

r, s = [int(rs[i][:64], 16) for i in range(t)], [int(rs[i][64:], 16) for i in range(t)]
k_bound = 2**245
p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
M = list(matrix(ZZ,t+2,t+2))
for i in range(t):
    M[i][i] = p
    M[-2][i] = r[i] + s[i]
    M[-1][i] = s[i]
M[-1][-1] = k_bound
M[-2][-2] = 1

M = matrix(ZZ,M)
res = M.LLL()

for i in res:
    if abs(gcd(i[0],i[-1])) == abs(i[-1]//k_bound) and i[0] * i[-1] != 0:
        ki = [int(i[j]) // (int(i[-1]) // k_bound)  for j in range(t)]
        di =[inverse(r[j]+s[j],p)*(ki[j]-s[j])%p for j in range(t-1)]
        if di[0] == di[1] and isPrime(int(di[0])):
            # print(i)
            d = hex(di[0])[2:]
            print(d)
            break
            
d = 'c72c9551d36e5af5c5ca07c573cd766b78fcb4c8dd36669867a69ef08196e03d'
re.recvuntil(b'[*] ')
re.sendline(b'3')
re.recvuntil(b'[*] ')
re.sendline(d.encode())
re.interactive()



# HZNUCTF{7d4a9941-d16e-5710-b2da-95a66a986f2b}




hnp存在一定成功率问题,详解看这位师傅的博客

posted @ 2023-03-28 14:01  顶真珍珠  阅读(189)  评论(0编辑  收藏  举报