NKCTF--Crypto复现

  1. baby_RSA
  2. ezRSA
  3. ezMath
  4. ez_polynomial
  5. eZ_Bl⊕ck
  6. easy_high
  7. complex_matrix
  8. ez_LargeCG

还是一如既往的菜,没啥长进啊。题目肯定是不算难的,但就是不会,还是太懒了,抓紧学起来!

baby_RSA

from Crypto.Util.number import *
nbit = 512
flag='****************************'

p=getPrime(nbit)
q=getPrime(nbit)
e=65537
n=p*q
m= bytes_to_long(bytes(flag.encode()))
P = pow(m,p,n)
Q = pow(m,q,n)
N=P*Q
phi_N=(P-1)*(Q-1)
d=inverse(e,phi_N)
dP=d%(P-1)
print('n = ',n)
print('N = ',N)
print('dP = ',dP)


n =  114101396033690088275999670914803472451228154227614098210572767821433470213124900655723605426526569384342101959232900145334500170690603208327913698128445002527020347955300595384752458477749198178791196660625870659540794807018881780680683388008090434114437818447523471527878292741702348454486217652394664664641
N =  1159977299277711167607914893426674454199208605107323826176606074354449015203832606569051328721360397610665453513201486235549374869954501563523028914285006850687275382822302821825953121223999268058107278346499657597050468069712686559045712946025472616754027552629008516489090871415609098178522863027127254404804829735621706042266140637592206366042515190385496909533329383212542170504864473944657824502882014292528444918055958758310544435120502872883857209880723535754528096143707324179005292445100655695427777453144657819474805882956064292780031599790769618615908501966912635232746588639924772530057835864082951499028
dP =  33967356791272818610254738927769774016289590226681637441101504040121743937150259930712897925893431093938385216227201268238374281750681609796883676743311872905933219290266120756315613501614208779063819499785817502677885240656957036398336462000771885589364702443157120609506628895933862241269347200444629283263

看到出现了dp,套个dp的爆破脚本解出P、Q来:

for i in range(1,e):
    if (dP*e-1)%i == 0:
        if (N%((dP*e-1)//i+1)) == 0:
            P = (dP*e-1)//i+1
            Q = N // P
            print(P)
            print(Q)
#P = 37269067352457630263351774206178494913957088859822110344334922741582406663357663275001777826744534556652993452577088773275825539553907027527722045884489297259984687894496505265384077983882580247333972954704644517469999916574893996324149548980338301147983367163067556434434982470623587914250041142380667816331
#Q = 31124398373258967647259273958463995255448332282733968391096762136494537693158180734634142626374694999391960402681114664140356232930100613919064790135723294710984789809295862966052914533841176463683069225072916170624016363164528353386087305805869313783193076760778433672011918504199905552973276891160558444988

然后根据题目得到下面两个关系式:

$P \equiv m^p \space mod \space n$

$Q \equiv m^q \space mod \space n$

各种推导,但我这个数学思维就是不行,没推出来。

$把n拆开,并利用费马小定理,m^{p-1} \equiv 1mod p(根本没想到,还是不熟悉),分别得到:$

$P \equiv m^p \space mod \space p
\equiv mm^{p-1} \space mod \space p \equiv m \space mod \space p\Rightarrow P = m + k_1 p $

$同理,Q \equiv m^q \space mod \space q \equiv m \space mod \space q \Rightarrow Q = m + k_2q $
$两式子相乘$

$ \Rightarrow PQ = m^2 + (k_1p+k_2q)m + kn $
$\Rightarrow PQ \equiv -m^2 + (P+Q)m \space mod \space n $

$我想知道为什么m^2前面带了负号。\
这里(k_1p+k_2q) \approx (P+Q)$

然后构造多项式直接求解m:

P = ...
Q = ...
n = ...

R.<m> = Zmod(n)[]
f = -m**2+(P+Q)*m-P*Q#这里的加减号不能错了
m = f.monic().small_roots()
'''
#或者这样构造
PR.<m> = PolynomialRing(Zmod(n))
'''

print(m)
#m = 152099310694956022622926857538598513541723670773227126074246760446874272165452073476477
#显然p、q是远大于m的,这就是为什么上面的推导可以近似等于
#print(long_to_bytes(m))
#b'NKCTF{Th1S_a_babyRSA_y0u_are_tql!!!}'

ezRSA

from Crypto.Util.number import *
from secret import flag

m1 = bytes_to_long(flag[:len(flag)//3])
m2 = bytes_to_long(flag[len(flag)//3:])

def gen():
    prime_list  = []
    for i in range(4):
        prime_list.append(getPrime(512))
    return sorted(prime_list)

prime_list = gen()
p,q,r,t = prime_list[0],prime_list[3],prime_list[1],prime_list[2]
e = 65537
n = p*q*r*t
phi = (p-1)*(q-1)*(r-1)*(t-1)
c1 = pow(m1,e,p*q)
p1 = getPrime(512)
q1 = getPrime(512)
N = p1*q1
c2 = pow(m2,p1,N)
c3 = pow(m2,q1,N)
print(f'n = {n}')
print(f'phi = {phi}')
print(f'c1 = {c1}')
print(f'N = {N}')
print(f'c2 = {c2}')
print(f'c3 = {c3}')

'''
n = 8836130216343708623415307573630337110573363595188748983290313549413242332143945452914800845282478216810685733227137911630239808895196748125078747600505626165666334675100147790578546682128517668100858766784733351894480181877144793496927464058323582165412552970999921215333509253052644024478417393146000490808639363681195799826541558906527985336104761974023394438549055804234997654701266967731137282297623426318212701157416397999108259257077847307874122736921265599854976855949680133804464839768470200425669609996841568545945133611190979810786943246285103031363790663362165522662820344917056587244701635831061853354597
phi = 8836130216343708623415307573630337110573363595188748983290313549413242332143945452914800845282478216810685733227137911630239808895196748125078747600505622503351461565956106005118029537938273153581675065762015952483687057805462728186901563990429998916382820576211887477098611684072561849314986341226981300596338314989867731725668312057134075244816223120038573374383949718714549930261073576391501671722900294331289082826058292599838631513746370889828026039555245672195833927609280773258978856664434349221972568651378808050580665443131001632395175205804045958846124475183825589672204752895252723130454951830966138888560
c1 = 78327207863361017953496121356221173288422862370301396867341957979087627011991738176024643637029313969241151622985226595093079857523487726626882109114134910056673489916408854152274726721451884257677533593174371742411008169082367666168983943358876017521749198218529804830864940274185360506199116451280975188409
N = 157202814866563156513184271957553223260772141845129283711146204376449001653397810781717934720804041916333174673656579086498762693983380365527400604554663873045166444369504886603233275868192688995284322277504050322927511160583280269073338415758019142878016084536129741435221345599028001581385308324407324725353
c2 = 63355788175487221030596314921407476078592001060627033831694843409637965350474955727383434406640075122932939559532216639739294413008164038257338675094324172634789610307227365830016457714456293397466445820352804725466971828172010276387616894829328491068298742711984800900411277550023220538443014162710037992032
c3 = 9266334096866207047544089419994475379619964393206968260875878305040712629590906330073542575719856965053269812924808810766674072615270535207284077081944428011398767330973702305174973148018082513467080087706443512285098600431136743009829009567065760786940706627087366702015319792328141978938111501345426931078
'''

n由四个大素数组成,加密只使用p和q,给了n、e、phi的值,在佬的提示下找了个know_phi的脚本,这里不贴了github上有,解出四个因数来,注意选择最小的和最大的作为p、q:

p = 8420341711111386139826589531912136886697864041156239432418101990860951057225344962981814097328105109563572968937589482777359728076364540186966659710593563
q = 10834231423967940002221004300639472879422266804796466209639481015717927053785699849888626682293072249949761634253205413288361321778947376683555990557548843

进一步解出m1来:

b'NKCTF{it_i5_e45y_th4t_Kn0wn'

接下来由题可以得到两个式子:

$c2 \equiv m_2^{p_1} \space mod \space N $
$c3 \equiv m_2^{q_1} \space mod \space N $
$非常之熟悉吧,和上面的baby一模一样,同理推导 $
$c2 = m_2 + k_1p_1 $
$c3 = m_2 + k_2q_1 $
$\Rightarrow c2c3 = -m_2^2+(c2+c3)m \space mod \space N$

就是因为这部分,导致两道题都没能做出来😥

R.<m> = Zmod(N)[]
#或者PR.<m> = PolynomialRing(Zmod(N))
f = -m**2+(c2+c3)*m-c2*c3
m = f.monic().small_roots(X=2^432,beta = 0.5,epsilon=0.01)
#X=2^432,前面求出m1,求得位数为215,所以m2的位数430,但为什么设为432
print(m)

#m = 4134704395865052635910135198384991632904826868184178486449854532897025132606910716694129086727413635889097150181468673789907726717
#print(long_to_bytes(m))
#b'_phi_4nd_N_dec0mp0ses_N_w1th_th3_s4m3_c0mm0n_n_but_pq}'

综上,flag:

NKCTF{it_i5_e45y_th4t_Kn0wn_phi_4nd_N_dec0mp0ses_N_w1th_th3_s4m3_c0mm0n_n_but_pq}

解释:epsilon=0.01,表示小根搜索算法中搜索到的所有小根的误差上界为 $\epsilon$。也就是说,如果 $r$ 是 $m(x)$ 在模 $X$ 意义下的一个小根,那么 $|r| < X^{1/2} \cdot \epsilon$。

ezMath

from Crypto.Util.number import getPrime, bytes_to_long
from secret import BITS, hints, flag

p = getPrime(BITS)
q = getPrime(BITS)
n = p * q
print(f'n = {n}')

e = 0x10001
c = pow(bytes_to_long(flag), e, n)
print(f'c = {c}')

print('Give you some boring pows:')
for i in range(len(hints)):
    print(hints[i])
'''
n = 369520637995317866367336688225182965061898803879373674073832046072914710171302486913303917853881549637806426191970292829598855375370563396182543413674021955181862907847280705741114636854238746612618069619482248639049407507041667720977392421249242597197448360531895206645794505182208390084734779667749657408715621
c = 324131338592233305486487416176106472248153652884280898177125443926549710357763331715045582842045967830200123100144721322509500306940560917086108978796500145618443920020112366546853892387011738997522207752873944151628204886591075864677988865335625452099668804529484866900390927644093597772065285222172136374562043
Give you some boring pows:
...
'''

给了n、c、e以及一大堆的等式,不知道怎么利用。

看了佬们的wp后,发现其实还是跟推导有关😔

从给的数据里选择两组,两组满足底数相同,结果为平方关系,如下面这组:

pow(6,141997416965295486849546892322458652502850390670128808480582247784728456230996812361056958004801816363393016360646922983916999235770803618904474553309200419301820603229504955218189709387942156848904968053547462302189568831762401075340100029630332409419313772378068180267756675141584884876543484516408660699471038, n) = 9
pow(6,163378867981477210016607618217525067516899896304907822758749135410592905658324027908854458465871295591148114728316034699358213461728042658497873130073105697195541220650688132150216266657024774867846925219967805863946774978900772828496605795631400777090954141000078238226487076065753781167791598816872139973922682, n) = 3

进行推导:

$a^{e_1} \equiv a^{2e_2} \space mod \space n \Rightarrow a^{2e_2-e_1} \equiv 1 \space mod \space n $
$由欧拉定理,a^{\phi(n)} \equiv 1 \space mod \space n
\Rightarrow 2e_2-e_1 = k \phi(n) $
$这里带上k(有点疑惑)$

多找几组,对$2*e_2-e_1$部分求gcd得到$\phi(n)$。

pow(5,159624441075769368394142197503800917105605266793330527400563601282696932962732683048452274321445695181246055818189076528484173940458256745774766217433996778905066039826859919029954611118842967545793750205189398662362981005947945407568116603784658538931110792205205160154125544664182868277733116044735541284338342, n) = 9
pow(5,172192380036714150788905270808196199818277334366508682218739812159577144024191963252552116624193235000074634457087111471641800814071769221933018962135503876997163938949365614056098717522475180216291316295788774044033481065993544994610614082708563841846852650913646728169671510827052772868386414581035580266356334, n) = 3

pow(7,156359509651684605051402965560382969488421316701585527115005130492947292379802933549188085059602557600903593831240316597311439285149968787780538126741092612405335349622445040578126369183536683733294143156965518222696624206221060030916594302284630706642066420353822195108928341123726471513256217857861184609387726, n) = 9
pow(7,170559914324671769117535654836487226009685359320636182075960576764702323732727088502920021993271666209903403463612731506055433486417625242935904916789051793747298593847158174830184596554822038310041512771676833824200302666130102306284852931958549925702330464987955245647072909056824574486147965487598401928881026, n) = 3
e = 0x10001
n = ...
c = ...

e1 = [..., ..., ...]
e2 = [..., ..., ...]

res = []
for i in range(3):
    res.append(2*e2[i]-e1[i])
phi1 = gcd(res[0],res[1])
phi2 = gcd(phi1,res[2])

d = inverse(e,phi2)
m = pow(c,d,n)
print(long_to_bytes(m))

我验证了一下,如果只用两组e1和e2,也能得到flag,多用几组或许能更准确?

ez_polynomial

#sage
from Crypto.Util.number import *
flag = list(bytearray(''))
p = getPrime(16)
R.<y> = PolynomialRing(GF(p))
while True:
    P1 = R.random_element(degree=(ZZ.random_element(len(flag), 2*len(flag))))
    Q1 = R.random_element(degree=(ZZ.random_element(len(flag), 2*len(flag))))
    if P1.is_irreducible() and Q1.is_irreducible():
        P = P1
        Q = Q1
        break
e = 65537
N = P*Q
S.<x> = R.quotient(N)
c = S(flag) ^ e
print("P:" + str(p) + "\n")
print("N:" + str(N) + "\n")
print("C:" + str(c))

#P:40031
#N:24096*y^93 + 38785*y^92 + 17489*y^91 + 9067*y^90 + 1034*y^89 + 6534*y^88 + 35818*y^87 + 22046*y^86 + 12887*y^85 + 445*y^84 + 26322*y^83 + 37045*y^82 + 4486*y^81 + 3503*y^80 + 1184*y^79 + 38471*y^78 + 8012*y^77 + 36561*y^76 + 19429*y^75 + 35227*y^74 + 10813*y^73 + 26341*y^72 + 29474*y^71 + 2059*y^70 + 16068*y^69 + 31597*y^68 + 14685*y^67 + 9266*y^66 + 31019*y^65 + 6171*y^64 + 385*y^63 + 28986*y^62 + 9912*y^61 + 10632*y^60 + 33741*y^59 + 12634*y^58 + 21179*y^57 + 35548*y^56 + 17894*y^55 + 7152*y^54 + 9440*y^53 + 4004*y^52 + 2600*y^51 + 12281*y^50 + 22*y^49 + 17314*y^48 + 32694*y^47 + 7693*y^46 + 6567*y^45 + 19897*y^44 + 27329*y^43 + 8799*y^42 + 36348*y^41 + 33963*y^40 + 23730*y^39 + 27685*y^38 + 29037*y^37 + 14622*y^36 + 29608*y^35 + 39588*y^34 + 23294*y^33 + 757*y^32 + 20140*y^31 + 19511*y^30 + 1469*y^29 + 3898*y^28 + 6630*y^27 + 19610*y^26 + 11631*y^25 + 7188*y^24 + 11683*y^23 + 35611*y^22 + 37286*y^21 + 32139*y^20 + 20296*y^19 + 36426*y^18 + 25340*y^17 + 36204*y^16 + 37787*y^15 + 31256*y^14 + 505*y^13 + 27508*y^12 + 20885*y^11 + 32037*y^10 + 31236*y^9 + 7929*y^8 + 27195*y^7 + 28980*y^6 + 11863*y^5 + 16025*y^4 + 16389*y^3 + 570*y^2 + 36547*y + 10451
#C:3552*x^92 + 6082*x^91 + 25295*x^90 + 35988*x^89 + 26052*x^88 + 16987*x^87 + 12854*x^86 + 25117*x^85 + 25800*x^84 + 30297*x^83 + 5589*x^82 + 23233*x^81 + 14449*x^80 + 4712*x^79 + 35719*x^78 + 1696*x^77 + 35653*x^76 + 13995*x^75 + 13715*x^74 + 4578*x^73 + 37366*x^72 + 25260*x^71 + 28865*x^70 + 36120*x^69 + 7047*x^68 + 10497*x^67 + 19160*x^66 + 17939*x^65 + 14850*x^64 + 6705*x^63 + 17805*x^62 + 30083*x^61 + 2400*x^60 + 10685*x^59 + 15272*x^58 + 2225*x^57 + 13194*x^56 + 14251*x^55 + 31016*x^54 + 10189*x^53 + 35040*x^52 + 7042*x^51 + 29206*x^50 + 39363*x^49 + 32608*x^48 + 38614*x^47 + 5528*x^46 + 20119*x^45 + 13439*x^44 + 25468*x^43 + 30056*x^42 + 19720*x^41 + 21808*x^40 + 3712*x^39 + 25243*x^38 + 10606*x^37 + 16247*x^36 + 36106*x^35 + 17287*x^34 + 36276*x^33 + 1407*x^32 + 28839*x^31 + 8459*x^30 + 38863*x^29 + 435*x^28 + 913*x^27 + 36619*x^26 + 15572*x^25 + 9363*x^24 + 36837*x^23 + 17925*x^22 + 38567*x^21 + 38709*x^20 + 13582*x^19 + 35038*x^18 + 31121*x^17 + 8933*x^16 + 1666*x^15 + 21940*x^14 + 25585*x^13 + 840*x^12 + 21938*x^11 + 20143*x^10 + 28507*x^9 + 5947*x^8 + 20289*x^7 + 32196*x^6 + 924*x^5 + 370*x^4 + 14849*x^3 + 10780*x^2 + 14035*x + 15327

唯一写出来的题,还是因为之前写过这种题,不然爆0了。。。

在之前的文章里写过,相关知识不说了。

sage上对N进行多项式分解得到P、Q两个多项式:

R.<y> = PolynomialRing(GF(40031))
N = R("...")
print(N.factor())

然后就是常规思路求d求flag:

e = 65537
phi_n = (40031^40-1)*(40031^53-1)
d = inverse_mod(e, phi_n)
flag = pow(C,d,N)
print(flag)
#输出:
#125*y^32 + 83*y^31 + 109*y^30 + 97*y^29 + 51*y^28 + 114*y^27 + 100*y^26 + 95*y^25 + 116*y^24 + 117*y^23 + 66*y^22 + 95*y^21 + 103*y^20 + 110*y^19 + 49*y^18 + 104*y^17 + 116*y^16 + 48*y^15 + 110*y^14 + 95*y^13 + 51*y^12 + 86*y^11 + 97*y^10 + 72*y^9 + 95*y^8 + 101*y^7 + 87*y^6 + 123*y^5 + 70*y^4 + 84*y^3 + 67*y^2 + 75*y + 78

提取系数,转ASCII码,发现是逆序的,再倒一下就行:

flag = "..."
flag = flag.split('+')
res = ''
for i in flag:
    res += chr(int(i.split('*')[0]))
print(res)
print(res[::-1])

#}Sma3rd_tuB_gn1ht0n_3VaH_eW{FTCKN
#NKCTF{We_HaV3_n0th1ng_But_dr3amS}

eZ_Bl⊕ck

from Crypto.Util.strxor import strxor as xor
import os
from secret import flag

def round(s, k):
    l, r = s[:16], s[16:]
    l_, r_ = xor(xor(r, k), l), l
    return l_ + r_

def encode(s, k):
    t = s
    for i in range(8):
        t = round(t, k[i])
    return t

r = os.urandom(32)
print(r)

key = [os.urandom(16) for _ in range(8)]

print(encode(r, key))
m = flag.strip(b'NKCTF{').strip(b'}').replace(b'-',b'')
print(encode(m, key))

#b"t\xf7\xaa\xac\x9d\x88\xa4\x8b\x1f+pA\x84\xacHg'\x07{\xcc\x06\xc4i\xdd)\xda\xc9\xad\xa9\xe8\x1fi"
#b"'{<z}\x91\xda\xc5\xd5S\x8b\xfa\x9f~]J\x0f\xf4\x9a\x1e\xe0\xef\x129N\xe7a\x928+\xe0\xee"
#b'8\x1f"\x83B4\x86)\xce\xebq3\x06\xa0w\x16U\x04M/w\xa1\x8f;)M\xdd~\x11:\xe3\xb3'

不知道怎么推,块加密类的题练的少,要加强练习了。

分组密码的Feistel结构,没学过。

加密过程简单描述就是,将明文分组左右等长的两部分,两部分数据会经过n轮迭代,每轮迭代的规则一样,即使用相同的轮函数。且每轮迭代会使用密钥$K_i$,同样,每轮迭代使用的$K_i$也是不同的。这只是大致的过程,还有不少细节部分。

Feistel结构的迭代典型次数为16,可以看到题目中是8次。

通过分析可以知道,先要通过r和r与key进行加密的结果来得到key。

隔了几天,回顾这道题,终于搞懂了:

$首先把r的左半部分记作l_0,右半部分记作r_0,把r与key加密的结果记作enc$
$按照题目的推理规则可以逐步得到下面的等式:$

$l_1 = l_0 \bigoplus r_0 \bigoplus k_0$
$ r_1 = l_0 $
$按顺序推导八轮,最终可以得到如下关系:$
$enc_l = r_0 \bigoplus k_0 \bigoplus k_1 \bigoplus k_3 \bigoplus k_4 \bigoplus k_6 \bigoplus k_7 $
$enc_r = r_0 \bigoplus l_0 \bigoplus k_0 \bigoplus k_2 \bigoplus k_3 \bigoplus k_5 \bigoplus k_6$
$推导过程我是在纸上写的,有点丑,不贴了。。。$
$将enc_l中的key整体记作k_l,enc_r中的key整体记作k_r,$
$我对于此处的理解是,并不是把k_l与题目的key的左半部分等同起来(实际也不相等)$
$这里的k_l只是作一个定义,因为在后面m与key进行轮加密的过程中,最终也会得到$
$与上面enc两行相同结构的结构(这里把m和key加密的结果记作c):$
$c_l = m_r \bigoplus k_l$
$c_r = m_r \bigoplus m_l \bigoplus k_r$

如此一来便能解了:

from Crypto.Util.strxor import strxor as xor

r = b"t\xf7\xaa\xac\x9d\x88\xa4\x8b\x1f+pA\x84\xacHg'\x07{\xcc\x06\xc4i\xdd)\xda\xc9\xad\xa9\xe8\x1fi"
enc = b"'{<z}\x91\xda\xc5\xd5S\x8b\xfa\x9f~]J\x0f\xf4\x9a\x1e\xe0\xef\x129N\xe7a\x928+\xe0\xee"
c = b'8\x1f"\x83B4\x86)\xce\xebq3\x06\xa0w\x16U\x04M/w\xa1\x8f;)M\xdd~\x11:\xe3\xb3'

r_l = r[:16]
r_r = r[16:]
enc_l = enc[:16]
enc_r = enc[16:]

key_l = xor(enc_l,r_r)
key_r = xor(enc_r,xor(r_l,r_r))

c_l = c[:16]
c_r = c[16:]
m_r = xor(c_l,key_l)
m_l = xor(xor(key_r,c_r),m_r)

m = m_l+m_r
print(m)
#b'1ccd5ceec96d4caf8ce59a512b3d0655'

import uuid
flag = '1ccd5ceec96d4caf8ce59a512b3d0655'
print(uuid.UUID(flag))
#1ccd5cee-c96d-4caf-8ce5-9a512b3d0655

这里uuid搞不懂。

easy_high

from Crypto.Util.number import *
flag = ''

p, q = getPrime(1024), getPrime(1024)
N = p * q
p0 = p ^ (bytes_to_long(flag)<<444)
m = bytes_to_long(flag)
c = pow(m, 65537, N)
print('c=',c)
print('N=',N)
print('p0=',p0)

#c= 4881545863615247924697512170011400857004555681758106351259776881249360423774694437921554056529064037535796844084045263140567168171628832384672612945806728465127954937293787045302307135365408938448006548465000663247116917564500525499976139556325841597810084111303039525833367199565266613007333465332710833102978756654324956219855687611590278570749890543277201538208370370097424105751568285050703167350889953331829275262932104042040526209179357770495596739361176548337593674366015027648541293309465113202672923556991818236011769228078267484362980348613669012975963468592763463397575879215173972436831753615524193609612
#N= 17192509201635459965397076685948071839556595198733884616568925970608227408244870123644193452116734188924766414178232653941867668088060274364830452998991993756231372252367134508712447410029668020439498980619263308413952840568602285764163331028384281840387206878673090608323292785024372223569438874557728414737773416206032540038861064700108597448191546413236875600906013508022023794395360001242071569785940215873854748631691555516626235191098174739613181230094797844414203694879874212340812119576042962565179579136753839946922829803044355134086779223242080575811804564731938746051591474236147749401914216734714709281349
#p0= 149263925308155304734002881595820602641174737629551638146384199378753884153459661375931646716325020758837194837271581361322079811468970876532640273110966545339040194118880506352109559900553776706613338890047890747811129988585025948270181264314668772556874718178868209009192010129918138140332707080927643141811

基本上是分析这一行:

p0 = p ^ (bytes_to_long(flag)<<444)

可以知道flag左移444位后(低位补0)与$p$异或得到$p_0$,那么$p$的低444位与$p_0$是相同的。等于是知道了p的低444位,未知位数达到580位,在之前的文章里提到,当p、q位数是1024bit时,未知位数不能超过454bit过多。所以至少还得知道p的126位才能使用copper。

在p、q相近的情况下对n直接开方取高位作p的高位,但这里不成立,故行不通。

另寻他路:以flag的格式,最长也就那么长,转整数后也不会很大,可以试一下:

flag = b"NKCTF{ajdnaosdhiasdsfisdjdihadiasdhoadhsiodaj}"
print(bytes_to_long(flag))
res = 183876783844719951164006751402861139015367850881248806466142942786265188461121827081223474428956250940320606845
print(res.bit_length())

#367

flag是我随便打的,看到也才367位,实际肯定比这短,所以未知位数是小于454位的,故$p_0$的高位部分与p也相同,到这就可以解了。

p_low = p0 - ((p0>>444)<<444)#低444位
p_high = p0>>898 #高126位
#未知位数恰好454位

#sage
PR.<x> = PolynomialRing(Zmod(N))
f = (p_high<<898) + x*(2**444) + p_low
res = f.monic().small_roots(X=2^454,beta=0.4)
print(res)

#x = 8996514515788824631645217678983870972689906544773849929777173576310649928527740139864048823998116829780535762155750526515388194925404594
p = (p_high<<898) + x*(2**444) + p_low
q = N // p
d = inverse(e,(p-1)*(q-1))
m = pow(c,d,N)

print(long_to_bytes(m))
#b'NKCTF{F10wrs_hVe_r3strDay}'

我看佬的脚本是使未知位数为400,我试了下最大未知位数,也能解。还有一个地方,small_roots()函数里的参数,beta=等于0.2、0.3、0.5都不能解出来。还有如果加上epsilon=0.02,也能解,但会比不加慢一两秒。

佬的脚本里,beta等于0.2、0.3、0.4、0.5都能解,加不加epsilon也都能解。

这个参数的确定我还不太理解,迟早得彻底弄清楚。

complex_matrix

from Crypto.Util.number import *
import gmpy2 as gy
flag = ''


k = 400
p, q = getPrime(741), getPrime(741)
N = p * q
phi = (p-1) * (q-1)
_flag = bytes_to_long(flag)
p, q = getPrime(1024), getPrime(1024)
d_array = [getPrime(k) for _ in range(4)] 
e_array = [inverse(i, phi) for i in d_array]
c = pow(_flag, 65537, N)
print('N:',N)
print('e:',e_array)
print('c:',c)

#N: 71841248095369087024928175623295380241516644434969868335504061065977014103487197287619667598363486210886674500469383623511906399909335989202774281795855975972913438448899231650449810696539722877903606541112937729384851506921675290984316325565141178015123381439392534417225128922398194700511937668809140024838070124095703585627058463137549632965723304713166804084673075651182998654091113119667582720831809458721072371364839503563819080226784026253
#e: [65128799196671634905309494529154568614228788035735808211836905142007976099865571126946706559109393187772126407982007858423859147772762638898854472065889939549916077695303157760259717113616428849798058080633047516455513870697383339784816006154279428812359241282979297285283850338964993773227397528608557211742425548651971558377656644211835094019462699301650412862894391885325969143805924684662849869947172175608502179438901337558870349697233790535, 58756559706647121529575085912021603170286163639572075337348109911506627489265537716060463072086480156516641723700802217411122982693536541892986623158818442274840863016647800896033363360822503445344748132842451806511693779600370832206455202293028402486647422212959763287987847280322100701242139127654031151565924132562837893975505159702015125483479126108892709063135006366792197127007229210558758401679638300464111782814561428899998471531067163715, 34828685390969672139784723764579499920301439564705391196519314224159563070870933754477650614819514127121146216049444888554338415587165719098661141454627820126445291802801256297252654045398330613075575527685542980264993711077876535643646746742646371967302159565887123638001580042027272379341650995728849759541960087953160211696369079708787543303742132161742979856720539914370868829868891655221361545648778590685232034703220732697083024449894197969, 26717968456600556973167180286909817773394160817933525240720067057464671317174201540556176814203780603153696663101158205367554829261808020426363683474848952397963507069306452835776851274959389849223566030857588019845781623271395012194869024566879791449466064832273531795430185178486425688475688634844530106740480643866537205900809400383304665727460014210405339697947582657505028211149470787536144302545259243549176816653560626044921521516818788487]
#c: 39297018404565022956251803918747154798377576057123078716166221329195959669756819453426741569480551313085435037629493881038383709458043802420338889323233368852331387845200216275712388921820794980987541224782392553528127093154957890356084331463340193478391679540506421250562554424770350351514435220782124981277580072039637811543914983033300225131364246910828188727043248991987332274929827173923543187017105236008487756190002204169623313222748976369

拓展winner攻击:

from Crypto.Util.number import *
debug = True
 
isdigit = lambda x: ord('0') <= ord(x) <= ord('9')
 
def my_permutations(g, n):
    sub = []
    res = []
    def dfs(s, prev):
        if len(s) == n:
            res.append(s[::])
        for i in g:
            if i in s or i < prev:
                continue
            s.append(i)
            dfs(s, max(prev, i))
            s.remove(i)
    dfs(sub, 0)
    return res
 
class X3NNY(object):
    def __init__(self, exp1, exp2):
        self.exp1 = exp1
        self.exp2 = exp2
    
    def __mul__(self, b):
        return X3NNY(self.exp1 * b.exp1, self.exp2 * b.exp2)
 
    def __repr__(self):
        return '%s = %s' % (self.exp1.expand().collect_common_factors(), self.exp2)
 
class X_Complex(object):
    def __init__(self, exp):
        i = 0
        s = '%s' % exp
        while i < len(s):
            if isdigit(s[i]):
                num = 0
                while i < len(s) and isdigit(s[i]):
                    num = num*10 + int(s[i])
                    i += 1
                if i >= len(s):
                    self.b = num
                elif s[i] == '*':
                    self.a = num
                    i += 2
                elif s[i] == '/':
                    i += 1
                    r = 0
                    while i < len(s) and isdigit(s[i]):
                        r = r*10 + int(s[i])
                        i += 1
                    self.b = num/r
            else:
                i += 1
        if not hasattr(self, 'a'):
            self.a = 1
        if not hasattr(self, 'b'):
            self.b = 0
 
def WW(e, d, k, g, N, s):
    return X3NNY(e*d*g-k*N, g+k*s)
def GG(e1, e2, d1, d2, k1, k2):
    return X3NNY(e1*d1*k2- e2*d2*k1, k2 - k1)
 
def W(i):
    e = eval("e%d" % i)
    d = eval("d%d" % i)
    k = eval("k%d" % i)
    return WW(e, d, k, g, N, s)
 
def G(i, j):
    e1 = eval("e%d" % i)
    d1 = eval("d%d" % i)
    k1 = eval("k%d" % i)
    
    e2 = eval("e%d" % j)
    d2 = eval("d%d" % j)
    k2 = eval("k%d" % j)
    
    return GG(e1, e2, d1, d2, k1, k2)
 
def R(e, sn): # min u max v
    ret = X3NNY(1, 1)
    n = max(e)
    nn = len(e)
    l = set(i for i in range(1, n+1))
    d = ''
    u, v = 0, 0
    for i in e:
        if i == 1:
            ret *= W(1)
            d += 'W(%d)' % i
            nn -= 1
            l.remove(1)
            u += 1
        elif i > min(l) and len(l) >= 2*nn:
            ret *= G(min(l), i)
            nn -= 1
            d += 'G(%d, %d)' % (min(l), i)
            l.remove(min(l))
            l.remove(i)
            v += 1
        else:
            ret *= W(i)
            l.remove(i)
            d += 'W(%d)' % i
            nn -= 1
            u += 1
    if debug:
        print(d)
    return ret, u/2 + (sn - v) * a
 
def H(n):
    if n == 0:
        return [0]
    if n == 2:
        return [(), (1,), (2,), (1, 2)]
    ret = []
    for i in range(3, n+1):
        ret.append((i,))
        for j in range(1, i):
            for k in my_permutations(range(1, i), j):
                ret.append(tuple(k + [i]))
    return H(2) + ret
    
def CC(exp, n):
    cols = [0 for i in range(1<<n)]
    
    # split exp
    texps = ('%s' % exp.exp1.expand()).strip().split(' - ')
    ops = []
    exps = []
    for i in range(len(texps)):
        if texps[i].find(' + ') != -1:
            tmp = texps[i].split(' + ')
            ops.append(0)
            exps.append(tmp[0])
            for i in range(1, len(tmp)):
                ops.append(1)
                exps.append(tmp[i])
        else:
            ops.append(0)
            exps.append(texps[i])
    if exps[0][0] == '-':
        for i in range(len(exps)):
            ops[i] = 1-ops[i]
        exps[0] = exps[0][1:]
    else:
        ops[0] = 1
    # find e and N
    l = []
    for i in range(len(exps)):
        tmp = 1 if ops[i] else -1
        en = []
        j = 0
        while j < len(exps[i]):
            if exps[i][j] == 'e':
                num = 0
                j += 1
                while isdigit(exps[i][j]):
                    num = num*10 + int(exps[i][j])
                    j += 1
                tmp *= eval('e%d' % num)
                en.append(num)
            elif exps[i][j] == 'N':
                j += 1
                num = 0
                if exps[i][j] == '^':
                    j += 1
                    while isdigit(exps[i][j]):
                        num = num*10 + int(exps[i][j])
                        j += 1
                if num == 0:
                    num = 1
                tmp *= eval('N**%d' % num)
            else:
                j += 1
        if tmp == 1 or tmp == -1:
            l.append((0, ()))
        else:
            l.append((tmp, tuple(sorted(en))))
    
    # construct h
    mp = H(n)
    for val, en in l:
        cols[mp.index(en)] = val
    # print(cols)
    return cols
 
def stirling(k):
    return factorial(k)/(factorial(k//2)^2)
def calcAlpha(n):
    if n % 2 == 1:
        fz = (2*n + 1) * 2^n - 4*n*stirling(n-1)
        fm = (2*n - 2) * 2^n + 8*n*stirling(n-1)
    else:
        fz = (2*n + 1) * 2^n - (2*n + 1)*stirling(n)
        fm = (2*n - 2) * 2^n + (4*n + 2)*stirling(n)
    return fz/fm
 
 
def EWA(n, elist, NN, alpha):
    mp = H(n)
    var('a')
    S = [X_Complex(n*a)]
    cols = [[1 if i == 0 else 0 for i in range(2^n)]]
    for i in mp[1:]:
        eL, s = R(i, n)
        cols.append(CC(eL, n))
        S.append(X_Complex(s))
    
    alphaA,alphaB = 0, 0
    for i in S:
        alphaA = max(i.a, alphaA)
        alphaB = max(i.b, alphaB)
    D = []
    for i in range(len(S)):
        D.append(
            int(NN^((alphaA-S[i].a)*alpha + (alphaB - S[i].b)))
        )
    kw = {'N': NN}
    for i in range(len(elist)):
        kw['e%d' % (i+1)] = elist[i]
    if debug:
        print("The lattice: ")
        print(Matrix(cols).T)
        
        print("The matrix D: ")
        print([N^((alphaA-S[i].a)*alpha + (alphaB - S[i].b)) for i in range(len(S))])
 
    B = Matrix(ZZ, Matrix(cols).T(**kw)) * diagonal_matrix(ZZ, D)
    L = B.LLL(0.5)
    v = Matrix(ZZ, L[0])
    x = v * B**(-1)
    phi = int(x[0,1]/x[0,0]*elist[0])
    return phi
 
def attack(NN, elist, alpha):
    for i in range(1, len(elist)+1):
        var("e%d" % i)
        var("d%d" % i)
        var("k%d" % i)
    g, N, s = var('g'), var('N'), var('s')
    phi = EWA(len(elist), elist, NN, alpha)
    return phi
 
def example(N,e):
    from Crypto.Util.number import long_to_bytes, getPrime, bytes_to_long
    import uuid
    def rsa(e, n):
        m = uuid.uuid4().hex.encode()
        c = pow(bytes_to_long(m), e, n)
        return m.decode(), c
#     p = getPrime(1024)
#     q = getPrime(1024)
    
    # The modulus in RSA
    NN = N
    
    # The exponent in RSA
#     e = 0x10001
#     m, c = rsa(e, NN)
#     print("The plaintext is:", m)
    
    # Theoretical upper bound in paper, but it is much smaller when actual test 
    alpha = calcAlpha(3)
    print("Alpha: ",alpha)
    elist = e
    phi = attack(NN, elist, alpha)
    
    if phi != 0:
        print("Found Phi: ", phi)
        return phi
 
N=...
n=N
e=[...]
c=...
phi=example(N,e)
d=inverse(65537,ZZ(phi))
long_to_bytes(ZZ(power_mod(c, d, n)))

ez_LargeCG

from gmpy2 import next_prime
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
import random
from secret import flag

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

def genMyPrimeB(bits):
    while True:
        g = 2
        while g < 2 ** bits:
            g *= random.choice(primes)
        g -= 1
        if isPrime(g):
            return g

def gen(st, n, a, b, c, d):
    A = [st + 2023, st + 2024, st + 2025]
    for i in range(6**666):
        A.append((a * A[-3] + b * A[-2] + c * A[-1] + d) % n)
    return A

primes = init()
p1 = getPrime(256)
print(p1)
q1 = 1
while p1 > q1:
    q1 = genMyPrimeA(256)
print(q1)
p2 = getPrime(256)
q2 = 1
while p2 > q2:
    q2 = genMyPrimeB(256)
n1 = p1 * q1
n2 = p2 * q2
print(f'n1 = {n1}')
print(f'n2 = {n2}')

r = getPrime(512)
print(f'r = {r}')

A = gen(bytes_to_long(flag), r, p1, q1, p2, q2)
print(f'A[-3] = {A[-3]}')
print(f'A[-2] = {A[-2]}')
print(f'A[-1] = {A[-1]}')


# n1 = 39755206609675677517559022219519767646524455449142889144073217274247893104711318356648198334858966762944109142752432641040037415587397244438634301062818169
# n2 = 30725253491966558227957591684441310073288683324213439179377278006583428660031769862224980605664642101191616868994066039054762100886678504154619135365646221
# r = 7948275435515074902473978567170931671982245044864706132834233483354166398627204583162848756424199888842910697874390403881343013872330344844971750121043493
# A[-3] = 6085327340671394838391386566774092636784105046872311226269065664501131836034666722102264842236327898770287752026397099940098916322051606027565395747098434
# A[-2] = 1385551782355619987198268805270109182589006873371541520953112424858566073422289235930944613836387546298080386848159955053303343649615385527645536504580787
# A[-1] = 2529291156468264643335767070801583140819639532551726975314270127875306069067016825677707064451364791677536138503947465612206191051563106705150921639560469

涉及到光滑数、矩阵快速幂。

对于n1,通过分析q的生成过程,可以知道是p-1光滑;对于n2,可以知道是p+1光滑,可以分别分解得到因数:

from gmpy2 import *

def Pollard(n):
    a=2
    while True:
        for i in range(2,80000):
            a=pow(a,i,n)
        for j in range(80000,104729+1):
            a=pow(a,j,n)
            if j % 15 ==0:
                d = gcd(a-1,n)
                if(1<d<n):
                  return(d)
        a+=1

n1 = 39755206609675677517559022219519767646524455449142889144073217274247893104711318356648198334858966762944109142752432641040037415587397244438634301062818169
q1 = Pollard(n1)
p1 = n1 // q1
print(p1,q1)
#p1 = 92946439027877993602295703905130336736159270745389239059083263513478865293549 
#q1 = 427721675251610827084310512123962488210068003845592404231631542730839819224381

#p2=288551157776490110472645044398395422160196115791981535735903775378294599329633
#q2=106481130516780473105954611077340189174861459077145246394800660809527032990637
def gen(st, n, a, b, c, d):
    A = [st + 2023, st + 2024, st + 2025]
    for i in range(6**666):
        A.append((a * A[-3] + b * A[-2] + c * A[-1] + d) % n)
    return A

接着通过分析gen函数,四阶LCG问题,构建矩阵:

$A =
\left[
\begin{matrix}
0 & 1 & 0 & 0 \
0 & 0 & 1 & 0 \
a & b & c & d \
0 & 0 & 0 & 1
\end{matrix}
\right]
\Rightarrow A*
\left(
\begin{matrix}
s_1 \
s_2 \
s_3 \
1
\end{matrix}
\right) =
\left(
\begin{matrix}
s_2 \
s_3 \
s_4 \
1
\end{matrix}
\right)$

a = q1
b = p1
c = q2
d = p2

r = 7948275435515074902473978567170931671982245044864706132834233483354166398627204583162848756424199888842910697874390403881343013872330344844971750121043493
s1 = 6085327340671394838391386566774092636784105046872311226269065664501131836034666722102264842236327898770287752026397099940098916322051606027565395747098434
s2 = 1385551782355619987198268805270109182589006873371541520953112424858566073422289235930944613836387546298080386848159955053303343649615385527645536504580787
s3 = 2529291156468264643335767070801583140819639532551726975314270127875306069067016825677707064451364791677536138503947465612206191051563106705150921639560469
S = Vector(GF(r),[s1,s2,s3,1])
A = matrix(GF(r),[[0,1,0,0],[0,0,1,0],[a,b,c,d],[0,0,0,1]])

flag = A^(-6^666)*S

print(long_to_bytes(ZZ(flag[0]-2023)))
#NKCTF{y0u_kN0w_r5A_&_LCg_&_Ma7r1X_s0_w3ll!!!}

参考:2023 nkctf crypto wp - App1e_Tree - 博客园 (cnblogs.com)

(1条消息) NKCTF2023 Crypto 部分wp_无趣的浅的博客-CSDN博客


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1666739907@qq.com
github