羊城杯网络安全大赛初赛 2023

Easy_3L

由于\(s_3=c+kp-sh\)相对于\(c,p,h\)等较小,而\(s\)也只有512比特,因此可以建立如下格:

\[\begin{bmatrix} c & 0 & 2^{512} \\ p & 0 & 0 \\ h & 1 & 0 \end{bmatrix} \tag{lattice} \]

系数为\((1,k,s)\),短向量为\((s_3,s,2^{512})\)

这样可以把\(s_3\)求出来,联合lcg的其他输出求出模数,乘数和增量,最后恢复seed即flag。

from Crypto.Util.number import *
from gmpy2 import *

S1 = 28572152986082018877402362001567466234043851789360735202177142484311397443337910028526704343260845684960897697228636991096551426116049875141
S2 = 1267231041216362976881495706209012999926322160351147349200659893781191687605978675590209327810284956626443266982499935032073788984220619657447889609681888
S4 = 9739918644806242673966205531575183334306589742344399829232076845951304871478438938119813187502023845332528267974698273405630514228632721928260463654612997
S5 = 9755668823764800147393276745829186812540710004256163127825800861195296361046987938775181398489372822667854079119037446327498475937494635853074634666112736
p = 25886434964719448194352673440525701654705794467884891063997131230558866479588298264578120588832128279435501897537203249743883076992668855905005985050222145380285378634993563571078034923112985724204131887907198503097115380966366598622251191576354831935118147880783949022370177789175320661630501595157946150891275992785113199863734714343650596491139321990230671901990010723398037081693145723605154355325074739107535905777351
h = 2332673914418001018316159191702497430320194762477685969994411366563846498561222483921873160125818295447435796015251682805613716554577537183122368080760105458908517619529332931042168173262127728892648742025494771751133664547888267249802368767396121189473647263861691578834674578112521646941677994097088669110583465311980605508259404858000937372665500663077299603396786862387710064061811000146453852819607311367850587534711
c = 20329058681057003355767546524327270876901063126285410163862577312957425318547938475645814390088863577141554443432653658287774537679738768993301095388221262144278253212238975358868925761055407920504398004143126310247822585095611305912801250788531962681592054588938446210412897150782558115114462054815460318533279921722893020563472010279486838372516063331845966834180751724227249589463408168677246991839581459878242111459287
S3 = 10700695166096094995375972320865971168959897437299342068124161538902514000691034236758289037664275323635047529647532200693311709347984126070052011571264606

t0 = S2 - S1
t1 = S3 - S2
t2 = S4 - S3
t3 = S5 - S4

tmp1 = t2*t0 - t1*t1
tmp2 = t3*t1 - t2*t2

n = gcd(tmp1,tmp2)
assert isPrime(n)
a = (S3 -S2) * invert(S2-S1,n) % n
b = (S2 - a*S1)%n
m = (S1 - b) * invert(a,n) % n
print(long_to_bytes(m))

Danger_RSA

根据get_key的s = getRandomRange(pow(2, a ** 2 - a + 4), pow(2, a ** 2 - a + 5))可知,s和t的比特数应该是一致的,那么s和t都应该是17比特(因为e=st是34比特)。稍微试一试可以发现就两种可能:\(s=5741 \cdot 21\)\(s=5741\cdot19\)。利用\(a^2-a+5=17\)可以解出a为4。而\(\sqrt[a] {(x^a+s)(y^a+t)}\)取整就能得到\(x\cdot y\),那么设两个未知数\(i,j\),联立起来用gb基就能出了。

# https://www.zhihu.com/people/ZM_________J/posts
from Crypto.Util.number import *

N = 20289788565671012003324307131062103060859990244423187333725116068731043744218295859587498278382150779775620675092152011336913225797849717782573829179765649320271927359983554162082141908877255319715400550981462988869084618816967398571437725114356308935833701495015311197958172878812521403732038749414005661189594761246154666465178024563227666440066723650451362032162000998737626370987794816660694178305939474922064726534186386488052827919792122844587807300048430756990391177266977583227470089929347969731703368720788359127837289988944365786283419724178187242169399457608505627145016468888402441344333481249304670223
e = 11079917583
c = 13354219204055754230025847310134936965811370208880054443449019813095522768684299807719787421318648141224402269593016895821181312342830493800652737679627324687428327297369122017160142465940412477792023917546122283870042482432790385644640286392037986185997262289003477817675380787176650410819568815448960281666117602590863047680652856789877783422272330706693947399620261349458556870056095723068536573904350085124198592111773470010262148170379730937529246069218004969402885134027857991552224816835834207152308645148250837667184968030600819179396545349582556181916861808402629154688779221034610013350165801919342549766

a = 4
r = 5741 * 21
s = e//r
xy = floor(N^(1/a))

PR.<i, j> = PolynomialRing(ZZ)

f1 = i * j - xy^a
f2 = (i + r) * (j + s) - N

print(Ideal([f1, f2]).groebner_basis())

PR.<y> = PolynomialRing(ZZ)
f3 = 120561*y^2 - 948332745914726488248328981194859346908046365000200456951397011146994307058832478357232073359436344663173247276027251189170318093864618179158166473762678075605794530450533379399031342448823677066080128206278927580027634528271117321568828703002589457393465199155431679915663793163542939559128871243591605854719744*y + 1864692438550863016141513798266000457602215683433224185531339342064589113224894044383669854278154803113718866902994046297896336190499782613371882623108002469480950940164568578157835087851546395647804456836849393066035483723135754830910841251184687860129924668496392145025949962081507154567185557187395275125682979541696902825970897240275753338656369490224836320602268935569182448491632842336329824790467175952818105830205293157667738604308136292812715042893168532524955924995200131653889109432383462500230193043265225289229578761358749908792513075932245110484896336714915203401925339453476546946214380126883703133241088
print(f3.roots())

后边解密参考[*CTF 2021] Crypto - little case 的打法即可。

from Crypto.Util.number import *
import itertools

N = 20289788565671012003324307131062103060859990244423187333725116068731043744218295859587498278382150779775620675092152011336913225797849717782573829179765649320271927359983554162082141908877255319715400550981462988869084618816967398571437725114356308935833701495015311197958172878812521403732038749414005661189594761246154666465178024563227666440066723650451362032162000998737626370987794816660694178305939474922064726534186386488052827919792122844587807300048430756990391177266977583227470089929347969731703368720788359127837289988944365786283419724178187242169399457608505627145016468888402441344333481249304670223
e = 11079917583
c = 13354219204055754230025847310134936965811370208880054443449019813095522768684299807719787421318648141224402269593016895821181312342830493800652737679627324687428327297369122017160142465940412477792023917546122283870042482432790385644640286392037986185997262289003477817675380787176650410819568815448960281666117602590863047680652856789877783422272330706693947399620261349458556870056095723068536573904350085124198592111773470010262148170379730937529246069218004969402885134027857991552224816835834207152308645148250837667184968030600819179396545349582556181916861808402629154688779221034610013350165801919342549766


m = 4
r = 5741 * 21
s = e//r

bm = 3891889986375336330559716098591764128742918441309724777337583126578227827768865619689858547513951476952436981068109005313431255086775128227872912287517417948310766208005723508039484956447166240210962374423348694952997002274647622939970550008327647559433222317977926773242269276334110863262269534189811046416
q = bm + s
p = N // q

assert p*q == N
print(p)
def get_oneroot(p, e):
    while True:
        Zp = Zmod(p)
        g = Zp.random_element()
        g = g^((p-1) // e)
        for mult in divisors(e):
            if (mult != e):
                g2 = g^mult
                if (g2 == 1):
                    break
        else:
            return g

def decrypt(p, c, e):
    w = gcd(e, p-1)
    e1, p1 = e // w, (p-1) // w
    d = inverse_mod(e1, p1)
    c1 = pow(c, d, p)
    g, a, b = xgcd(p1, w)
    g = get_oneroot(p, w)
    m = pow(c1, b, p)
    return [ZZ(m * g^i) for i in range(w)]

mp_list = decrypt(p, c, e)
print('Find root p OK')
mq_list = decrypt(q, c, e)
print('Find root q OK')
for mp, mq in itertools.product(mp_list, mq_list):
    m = crt([mp, mq], [p, q])
    msg = long_to_bytes(int(m))
    if b'DAS' in msg:
        print(msg)
        break
        
print('done')

esyRSA

这么抽象的题我第一次见,不知道是不是故意的,出题人把n复制了两遍连在一起。发现这一点就很简单了,n和d大小接近,维纳分解n即可。

def possible(e,alist,N): 
    for x in alist:
        if x==0:
            continue
        phi = floor(e*(1/x))
        if (N-phi+1)%2==0 and sqrt(pow((N-phi+1)//2,2)-N).is_integer():
                (p,q)=var('p,q')
                x=solve([(p-1)*(q-1)==phi, p*q==N],p,q)
                return int(str(x[0][0]).split('==')[1])
        else:
            continue

def wiener_attack(e,N):
    c=continued_fraction(e/N)
    alist=c.convergents()
    return possible(e,alist,N)

n = 80642592772746398646558097588687958541171131704233319344980232942965050635113860017117519166348100569115174644678997805783380130114530824798808098237628247236574959152847903491509751809336988273823686988619679739640305091291330211169194377552925908412183162787327977125388852329089751737463948165202565859373
d = 14218766449983537783699024084862960813708451888387858392014856544340557703876299258990323621963898510226357248200187173211121827541826897886277531706124228848229095880229718049075745233893843373402201077890407507625110061976931591596708901741146750809962128820611844426759462132623616118530705745098783140913

p=wiener_attack(d,n)
print(p)

XOR贯穿始终

给了一串社会主义核心价值观编码,解密得到压缩包的key:C0ngr4tulati0n5_y0u_fou^d_m3,解开压缩包,给了pri.pem,用python代码或openssl去解析都会报错,猜测是修改了格式,于是参考OPENSSL中RSA私钥文件格式进行对照,发现他加了一些东西,也找不到d的标志位,但是大部分信息还是在的,只需要找到n,p,e解密即可。解出来是乱码,根据题意用压缩包密码异或一下即可。

import base64
import binascii
from Crypto.Util.number import *
from Crypto.Util.strxor import strxor

c = 91817924748361493215143897386603397612753451291462468066632608541316135642691873237492166541761504834463859351830616117238028454453831120079998631107520871612398404926417683282285787231775479511469825932022611941912754602165499500350038397852503264709127650106856760043956604644700201911063515109074933378818
pri = '''MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALmtMy+2uH1ZtbIL
SuiAukFthyQRH5mp7UmLyzZQkdg9zEP9/5tgffikQ7ytx5kHySHnazgAO1sOzmYE
N4Axlev6uafiP8B1Eij97v5VkYJ1I9e3mtBNheTbXKoT8op+ASQ1fQaF4A8UzLuW
eZeZI8JTH/SH+bolAK3kiZXDFdkTAgMBAAECgYEAl067LaC7Cvs2A5cMPhfYsESv
IgcKN1CwW4Sd3u8dSphhgu7TgyzIuvwxbuo2g1BC6WwKhaI6vGN+csfw6nh98GEn
/p3D0huNroAYvf/DRRB9UnHdttX7wB+Mv3P0RBDWHgBiCDVvHFuFUV78cIs0tnbn
jxjU07aPV2XRC3AfA2ECQQDqWUNPVg3i6vTyHCL7EGkbeUheYpAAfcKCQrxjc5+5
X6A+XtgHAA1JHwykPlCpHUOmlA85DJF1ejuoImzlgRLJAkEAytTCnQF+MN2r1gaA
UETZyj5qMYT7Th8zKEVVVJjDawLnuX4usJ2FyRnjCkk86U75QSJhw5mMc0QnG25u
Gz3++w=='''

origin = binascii.hexlify(base64.b64decode(pri))
print(origin)
n = 0x00b9ad332fb6b87d59b5b20b4ae880ba416d8724111f99a9ed498bcb365091d83dcc43fdff9b607df8a443bcadc79907c921e76b38003b5b0ece660437803195ebfab9a7e23fc0751228fdeefe5591827523d7b79ad04d85e4db5caa13f28a7e0124357d0685e00f14ccbb9679979923c2531ff487f9ba2500ade48995c315d913
e = 0x010001
p = 0x00ea59434f560de2eaf4f21c22fb10691b79485e6290007dc28242bc63739fb95fa03e5ed807000d491f0ca43e50a91d43a6940f390c91757a3ba8226ce58112c9
q = n//p
assert p * q == n
phi = (p-1)*(q-1)
d = inverse(e,phi)
m = long_to_bytes(pow(c,d,n))
print(m,len(m))
print(strxor(m[12:],b'C0ngr4tulati0n5_y0u_fou^d_m3'))

MCeorpkpleer

看到p没给全,先已知高位打一下,同时尝试一下经典背包格,发现密度问题打不了。

ph_ = 139540788452365306201344680691061363403552933527922544113532931871057569249632300961012384092481349965600565669315386312075890938848151802133991344036696488204791984307057923179655351110456639347861739783538289295071556484465877192913103980697449775104351723521120185802327587352171892429135110880845830815744
ph = ph_ >> 435
n = 22687275367292715121023165106670108853938361902298846206862771935407158965874027802803638281495587478289987884478175402963651345721058971675312390474130344896656045501040131613951749912121302307319667377206302623735461295814304029815569792081676250351680394603150988291840152045153821466137945680377288968814340125983972875343193067740301088120701811835603840224481300390881804176310419837493233326574694092344562954466888826931087463507145512465506577802975542167456635224555763956520133324723112741833090389521889638959417580386320644108693480886579608925996338215190459826993010122431767343984393826487197759618771
c = 156879727064293983713540449709354153986555741467040286464656817265584766312996642691830194777204718013294370729900795379967954637233360644687807499775502507899321601376211142933572536311131955278039722631021587570212889988642265055045777870448827343999745781892044969377246509539272350727171791700388478710290244365826497917791913803035343900620641430005143841479362493138179077146820182826098057144121231954895739989984846588790277051812053349488382941698352320246217038444944941841831556417341663611407424355426767987304941762716818718024107781873815837487744195004393262412593608463400216124753724777502286239464
pubkey = 
en_e = 31087054322877663244023458448558
pbits=1024

print(ph.nbits())
kbits=pbits - ph.nbits()
ph = ph << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + ph
roots = f.small_roots(X=2^kbits,beta=0.40)
print(roots)

p = ph_ + 22279478575805637289061098350801418725939755105414714905065078232409620860952900304322034404385073099026861643396875749287858710167
q = n//p
print(p)
assert p*q == n

ph_ = 139540788452365306201344680691061363403552933527922544113532931871057569249632300961012384092481349965600565669315386312075890938848151802133991344036696488204791984307057923179655351110456639347861739783538289295071556484465877192913103980697449775104351723521120185802327587352171892429135110880845830815744
ph = ph_ >> 435
n = 22687275367292715121023165106670108853938361902298846206862771935407158965874027802803638281495587478289987884478175402963651345721058971675312390474130344896656045501040131613951749912121302307319667377206302623735461295814304029815569792081676250351680394603150988291840152045153821466137945680377288968814340125983972875343193067740301088120701811835603840224481300390881804176310419837493233326574694092344562954466888826931087463507145512465506577802975542167456635224555763956520133324723112741833090389521889638959417580386320644108693480886579608925996338215190459826993010122431767343984393826487197759618771
c = 156879727064293983713540449709354153986555741467040286464656817265584766312996642691830194777204718013294370729900795379967954637233360644687807499775502507899321601376211142933572536311131955278039722631021587570212889988642265055045777870448827343999745781892044969377246509539272350727171791700388478710290244365826497917791913803035343900620641430005143841479362493138179077146820182826098057144121231954895739989984846588790277051812053349488382941698352320246217038444944941841831556417341663611407424355426767987304941762716818718024107781873815837487744195004393262412593608463400216124753724777502286239464
pubkey = [18143710780782459577, 54431132342347378731, 163293397027042136193, 489880191081126408579, 1469640573243379225737, 4408921719730137677211, 13226765159190413031633, 39680295477571239094899, 119040886432713717284697, 357122659298141151854091, 1071367977894423455562273, 3214103933683270366686819, 9642311801049811100060457, 28926935403149433300181371, 86780806209448299900544113, 260342418628344899701632339, 781027255885034699104897017, 2343081767655104097314691051, 7029245302965312291944073153, 21087735908895936875832219459, 63263207726687810627496658377, 189789623180063431882489975131, 569368869540190295647469925393, 1708106608620570886942409776179, 601827224419797931380408071500, 1805481673259393794141224214500, 893952418336266652976851386463, 2681857255008799958930554159389, 3523079163584485147344841221130, 1524252287869625983140881149316, 50264262166963219975822190911, 150792786500889659927466572733, 452378359502668979782399718199, 1357135078508006939347199154597, 4071405235524020818041597463791, 3169230503688232995231149877299, 462706308180869526799807117823, 1388118924542608580399421353469, 4164356773627825741198264060407, 3448085117999647764701149667147, 1299270151115113835209806487367, 3897810453345341505629419462101, 2648446157152195057994615872229, 3422845870014670444537026359650, 1223552407160181874717436564876, 3670657221480545624152309694628, 1966986461557807413563286569810, 1378466783231507511243038452393, 4135400349694522533729115357179, 3361215846199738142293703557463, 1038662335715384967987468158315, 3115987007146154903962404474945, 302975818554635252993570910761, 908927455663905758980712732283, 2726782366991717276942138196849, 3657854499533237101379593333510, 1928578295715881845245137486456, 1263242285705730806288591202331, 3789726857117192418865773606993, 2324195368467747797703678306905, 2450093503961328663664213663678, 2827787910442071261545819733997, 3960871129884299055190637944954, 2837628186769067706678271320788]
en_e = 31087054322877663244023458448558
pbits=1024

print(ph.nbits())
kbits=pbits - ph.nbits()
ph = ph << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + ph
roots = f.small_roots(X=2^kbits,beta=0.40)
print(roots)

p = ph_ + 22279478575805637289061098350801418725939755105414714905065078232409620860952900304322034404385073099026861643396875749287858710167
q = n//p
print(p)
assert p*q == n
# 接下来用背包格也可以打,比赛时写错一个地方。
N = ceil(1 / 2 * sqrt(n))

l = len(pubkey)
A = Matrix(ZZ, l+1, l+1)
for i in range(l):
    A[i,i] = 2
    A[i,l] = pubkey[i]
    
for i in range(l):
    A[l,i] = 1
A[l,l] = en_e
k = A.LLL()
for i in k:
    if set(list(i)) == {0,1,-1}:
        print(i)

# refer:https://github.com/jvdsn/crypto-attacks/blob/master/attacks/knapsack/low_density.py

然后发现私钥w已经给了,m未知但可以通过gcd求,最后写一个背包解密函数即可还原e,然后解rsa。

from Crypto.Util.number import *
from gmpy2 import *

p = 139540788452365306201344680691061363403552933527922544113532931871057569249632300961012384092481349965600565669315386312075890938848151802133991344036696488204791984307057923179677630589032444985150800881889090713797496239571291907818169058929859395965304623825442220206712660451198754072531986630133689525911
n = 22687275367292715121023165106670108853938361902298846206862771935407158965874027802803638281495587478289987884478175402963651345721058971675312390474130344896656045501040131613951749912121302307319667377206302623735461295814304029815569792081676250351680394603150988291840152045153821466137945680377288968814340125983972875343193067740301088120701811835603840224481300390881804176310419837493233326574694092344562954466888826931087463507145512465506577802975542167456635224555763956520133324723112741833090389521889638959417580386320644108693480886579608925996338215190459826993010122431767343984393826487197759618771
c = 156879727064293983713540449709354153986555741467040286464656817265584766312996642691830194777204718013294370729900795379967954637233360644687807499775502507899321601376211142933572536311131955278039722631021587570212889988642265055045777870448827343999745781892044969377246509539272350727171791700388478710290244365826497917791913803035343900620641430005143841479362493138179077146820182826098057144121231954895739989984846588790277051812053349488382941698352320246217038444944941841831556417341663611407424355426767987304941762716818718024107781873815837487744195004393262412593608463400216124753724777502286239464
pubkey = 
en_e = 31087054322877663244023458448558

q = n//p
assert p*q == n

w = 18143710780782459577
t1 =  w * 3 ** 63 - 2837628186769067706678271320788
t2 = w * 3 ** 62 - 3960871129884299055190637944954
m = gcd(t1,t2)

s = en_e * invert(w,m) % m
print(s)
r = [3 ** i for i in range(64)]

def dec(S):
    ans = []
    for t in r[::-1]:
        if S >= t:
            ans.append(1)
            S -= t
            print(f'find {t}, S <- {S}')
        else:
            ans.append(0)
    return ans[::-1]

ans = [str(i) for i in dec(s)]
e = int(''.join(ans),2)
print(e)

phi = (p-1)*(q-1)
d = invert(e,phi)
print(long_to_bytes(pow(c,d,n)))
# refer:https://www.ruanx.net/lattice-2/

SigninCrypto

关于mt19937预测这部分,用图示说明。64比特的rand实际上也是由两个32比特的rand堆叠而成的,其中先生成的32比特放在低位,后生成的放在高位。红色部分是题目给的32比特中的低16比特。缺失的高16比特恰好可以由第一次seed(rseed)之后的输出来填补,这是因为16比特rand的生成原理是取32比特rand的高16位。

image

from randcrack import RandCrack
f=open('task.txt').read()
p16=f.split('\n')[:624]
p64=f.split('\n')[624:-1]
print(p64)
p16=[int(i[2:],16) for i in p16]
p64=[int(i[2:],16) for i in p64]
p32=[]
for i in range(0,624,2):
    temp=p64[i//2]
    print(bin(temp)[2:])
    t1=bin(temp)[2:].zfill(32)[:16]
    t2=bin(temp)[2:].zfill(32)[16:]
    print(t2)
    t1=int(bin(p16[i+1])[2:]+t1,2)
    t2=int(bin(p16[i])[2:]+t2,2)
    p32.extend([t2,t1])
rc=RandCrack()
for i in p32:
    rc.submit(i)
print(rc.predict_getrandbits(64))

恢复mt的state之后可以得到K2,K3是flag前8字节,仅有一个字节未知,直接爆破即可;K1仅8字节长,可以通过xor的高位来判断,因此 os.urandom(2)的值也知道了,那么直接异或回去得到K1;IV已知高位4个字节(大写字母),剩下四个字节,利用哈希值来爆破字母即可。最后解一个3des。

import itertools
import string
from Crypto.Cipher import DES3
from Crypto.Util.number import *
from Crypto.Util.strxor import strxor
import hashlib
from binascii import unhexlify

c = b'a6546bd93bced0a8533a5039545a54d1fee647007df106612ba643ffae850e201e711f6e193f15d2124ab23b250bd6e1'
c = unhexlify(c)

h = 0x62343937373634656339396239663236643437363738396663393438316230353665353733303939613830616662663633326463626431643139323130616333363363326631363235313661656632636265396134336361623833636165373964343533666537663934646239396462323666316236396232303539336438336234393737363465633939623966323664343736373839666339343831623035366535373330393961383061666266363332646362643164313932313061633336336332663136323531366165663263626539613433636162383363616537396434353366653766393464623939646232366631623639623230353933643833
hint2 = 22078953819177294945130027344
hint2 = long_to_bytes(hint2)
print(hint2)
iv1 = b'GWHT'
iv2 = b'GWHT'
digest1 = hashlib.sha512(iv1).digest().hex()
digest2 = hashlib.sha512(iv2).digest().hex()
digest=digest1+digest2
print(hex(bytes_to_long((digest.encode()))) == hex(h))
IV = b'GWHTGWHT'

k2 = 8623025688911679118
print(k2.bit_length())
K2 = long_to_bytes(k2)
xo = 334648638865560142973669981316964458403
# 要注意K1仅8个字节
xxo = long_to_bytes(xo)[8:]
K1 = strxor(xxo,b'\xfb\xc2\xfb\xc2\xfb\xc2\xfb\xc2')
K3_ = b'DASCTF{'
for i in (string.ascii_letters + string.digits + '@' + '*'):
    K3 = K3_ + i.encode()
    KEY = K1 + K2 + K3
    mode = DES3.MODE_CBC
    des3 = DES3.new(KEY, mode, IV)
    m = des3.decrypt(c)
    if b'DASCTF' in m:
        print(m)
        break
# b'DASCTF{8e5ee461-f4e1-4af2-8632-c9d62f4dc073}\x04\x04\x04\x04'
posted @ 2023-09-03 14:28  ZimaB1ue  阅读(392)  评论(0编辑  收藏  举报