【虚空】【ISCTF2024】ISCTF2024 Crypto 全wp
免责声明:下列wp除我本人出的题外,其他题目wp不具备官方性质,仅作为参考。然后我简单说两句啊()今年的密码,我个人感觉不算很难,都是老套路套到了新题上,很多都是常见的解题思路。同时也有一些创新,但是也没有跳出固定的框架(毕竟密码学又不是misc,谁天天脑洞(乐))总之,这次我还是尽力给大家带来全套证明了。希望大家看的愉快。
免责声明:下列wp除我本人出的题外,其他题目wp不具备官方性质,仅作为参考。
然后我简单说两句啊()
今年的密码,我个人感觉不算很难,都是老套路套到了新题上,很多都是常见的解题思路。同时也有一些创新,但是也没有跳出固定的框架(毕竟密码学又不是misc,谁天天脑洞(乐))
总之,这次我还是尽力给大家带来全套证明了。希望大家看的愉快。
蓝鲨的费马
题目:
import libnum
import gmpy2
from Crypto.Util.number import *
flag=b'ISCTF{********}'
m=bytes_to_long(flag)
p=libnum.generate_prime(1024)
q=libnum.generate_prime(1024)
n=p*q
e=0x10001
c=pow(m,e,n)
d=inverse(e,(p-1)*(q-1))
leak = (d+(pow(p,q,n)+pow(q,p,n)))%n
print("c=", c)
print("n=", n)
print("leak=", leak)
"""
c= 8989289659072309605793417141528767265266446236550650613514493589798432446586991233583435051268377555448062724563967695425657559568596372723980081067589103919296476501677424322525079257328042851349095575718347302884996529329066703597604694781627113384086536158793653551546025090807063130353950841148535682974762381044510423210397947080397718080033363000599995100765708244828566873128882878164321817156170983773105693537799111546309755235573342169431295776881832991533489235535981382958295960435126843833532716436804949502318851112378495533302256759494573250596802016112398817816155228378089079806308296705261876583997
n= 13424018200035368603483071894166480724482952594135293395398366121467209427078817227870501294732149372214083432516059795712917132804111155585926502759533393295089100965059106772393520277313184519450478832376508528256865861027444446718552169503579478134286009893965458507369983396982525906466073384013443851551139147777507283791250268462136554061959016630318688169168797939873600493494258467352326974238472394214986505312411729432927489878418792288365594455065912126527908319239444514857325441614280498882524432151918146061570116187524918358453036228204087993064505391742062288050068745930452767100091519798860487150247
leak= 9192002086528025412361053058922669469031188193149143635074798633855112230489479254740324032262690315813650428270911079121913869290893574897752990491429582640499542165616254566396564016734157323265631446079744216458719690853526969359930225042993006404843355356540487296896949431969541367144841985153231095140361069256753593550199420993461786814074270171257117410848796614931926182811404655619662690700351986753661502438299236428991412206196135090756862851230228396476709412020941670878645924203989895008014836619321109848938770269989596541278600166088022166386213646074764712810133558692545401032391239330088256431881
"""
根据费马小定理,有
q
=
p
q
(
m
o
d
n
)
q = p^q \pmod{n}
q=pq(modn)
p
=
q
p
(
m
o
d
n
)
p = q^p \pmod{n}
p=qp(modn)
然后我们化简式子。
l
e
a
k
=
(
d
+
p
+
q
)
(
m
o
d
n
)
leak = (d + p + q) \pmod{n}
leak=(d+p+q)(modn)
l
e
a
k
=
d
+
p
+
q
+
k
n
,
k
∈
Z
leak= d + p+q + kn, k \in Z
leak=d+p+q+kn,k∈Z
e
∗
l
e
a
k
=
e
d
+
e
(
p
+
q
)
+
k
e
n
,
k
∈
Z
e*leak = ed + e(p+q) + ken,k \in Z
e∗leak=ed+e(p+q)+ken,k∈Z
k
1
=
e
∗
k
,
k
1
∈
Z
k_1 = e*k,k_1 \in Z
k1=e∗k,k1∈Z
e
∗
l
e
a
k
=
e
d
+
e
(
p
+
q
)
+
k
1
n
,
k
1
∈
Z
e*leak = ed + e(p+q) + k_1n,k_1 \in Z
e∗leak=ed+e(p+q)+k1n,k1∈Z
因为
e
d
≡
1
(
m
o
d
φ
(
n
)
)
ed \equiv 1 \pmod{\varphi(n)}
ed≡1(modφ(n))
有
e
d
=
1
+
t
∗
φ
(
n
)
,
t
∈
Z
ed = 1+t*\varphi(n),t \in Z
ed=1+t∗φ(n),t∈Z
代入上式
e
∗
l
e
a
k
=
1
+
t
φ
(
n
)
+
e
(
p
+
q
)
+
k
1
n
,
k
1
∈
Z
,
t
∈
Z
e*leak = 1+t\varphi(n) + e(p+q) + k_1n,k_1 \in Z,t \in Z
e∗leak=1+tφ(n)+e(p+q)+k1n,k1∈Z,t∈Z
φ
(
n
)
=
(
p
−
1
)
(
q
−
1
)
=
p
q
−
(
p
+
q
)
+
1
=
n
−
(
p
+
q
)
+
1
\varphi(n) = (p-1)(q-1) = pq-(p+q)+1 = n-(p+q)+1
φ(n)=(p−1)(q−1)=pq−(p+q)+1=n−(p+q)+1
代入
e
∗
l
e
a
k
=
1
+
t
(
n
−
(
p
+
q
)
+
1
)
+
e
(
p
+
q
)
+
k
1
n
,
k
1
∈
Z
,
t
∈
Z
e*leak = 1+t(n-(p+q)+1) + e(p+q) + k_1n,k_1 \in Z,t \in Z
e∗leak=1+t(n−(p+q)+1)+e(p+q)+k1n,k1∈Z,t∈Z
整理化简
e
∗
l
e
a
k
=
1
+
t
n
−
t
(
p
+
q
)
+
t
+
e
(
p
+
q
)
+
k
1
n
,
k
1
∈
Z
,
t
∈
Z
e*leak = 1+tn -t(p+q)+t+ e(p+q) + k_1n,k_1 \in Z,t \in Z
e∗leak=1+tn−t(p+q)+t+e(p+q)+k1n,k1∈Z,t∈Z
e
∗
l
e
a
k
=
1
+
t
+
(
e
−
t
)
(
p
+
q
)
+
(
k
1
−
t
)
n
,
k
1
∈
Z
,
t
∈
Z
e*leak = 1+t+ (e-t)(p+q) + (k_1-t)n,k_1 \in Z,t \in Z
e∗leak=1+t+(e−t)(p+q)+(k1−t)n,k1∈Z,t∈Z
等式两边对n取模
有
e
∗
l
e
a
k
≡
(
e
−
t
)
(
p
+
q
)
+
t
+
1
(
m
o
d
n
)
,
t
∈
Z
e*leak \equiv (e-t)(p+q)+t+1 \pmod{n},t \in Z
e∗leak≡(e−t)(p+q)+t+1(modn),t∈Z
记
l
≡
e
∗
l
e
a
k
(
m
o
d
n
)
l \equiv e*leak \pmod{n}
l≡e∗leak(modn)
有
l
−
t
−
1
≡
(
e
−
t
)
(
p
+
q
)
(
m
o
d
n
)
,
t
∈
Z
l-t-1 \equiv (e-t)(p+q) \pmod{n},t \in Z
l−t−1≡(e−t)(p+q)(modn),t∈Z
分析
p+q大于0
则
l
−
t
−
1
l-t-1
l−t−1的正负号仅由
e
−
t
e-t
e−t决定
若
t
>
e
t >e
t>e,则
t
>
l
−
1
t>l-1
t>l−1
而 l 是1024位以上的数,若t>e,则t几乎不可爆破
而若可以爆破,则
t
<
e
t <e
t<e
e = 0x10001
尝试爆破
l = e*leak % n
for t in range(0x10001):
if (l - 1 - t) % (e - t) == 0:
p_plus_q = (l - 1 - t) // (e - t)
delta = p_plus_q * p_plus_q - 4 * n
if delta <= 0 :
continue
squre_delta = iroot(delta,2)
if squre_delta[1]:
p = (p_plus_q + squre_delta[0])//2
q = (p_plus_q - squre_delta[0])//2
print(f"{t=}")
break
得到p和q之后就正常求解RSA 即可。
小蓝鲨的数学题
题目:
# Base和Ciphertext
m = 5321153468370294351697008906248782883193902636120413346203705810525086437271585682015110123362488732193020749380395419994982400888011862076022065339666193
c = 7383779796712259466884236308066760158536557371789388054326630574611014773044467468610300619865230550443643660647968413988480055366698747395046400909922513
离散对数求解问题
直接求解即可。
m = 5321153468370294351697008906248782883193902636120413346203705810525086437271585682015110123362488732193020749380395419994982400888011862076022065339666193
a=7383779796712259466884236308066760158536557371789388054326630574611014773044467468610300619865230550443643660647968413988480055366698747395046400909922513
G=Zmod(2**512)
m1=G(m)
c1=G(a)
print(ZZ(discrete_log(c1,m1)))
小蓝鲨的方程
题目:
from Cryptodome.Util.number import *
from random import *
from gmpy2 import *
import uuid
flag1='ISCTF{'+str(uuid.uuid4())+'}'
m1=bytes_to_long(flag1.encode())
def get_p():
BITS = 256
bits = 777
oder = 4
a = randint(1 << bits, 1 << bits + 1)
p=getPrime(BITS)
p1 = p**oder+a
return p,p1
p,p1=get_p()
s=getPrime(1024)
q=getPrime(512)
n=p*q**4
e=65537
c1=pow(s,e,n)
c=pow(s**3+1,m1,s**5)
print("c1=",c1)
print("c =",c)
print("n =",n)
print("p1 =",p1)
'''
c1= 671390498592586008552998377599101093977542184109077889081448730480869018650843045119891777468161631085086340705902115332025675787789530562679603254577287153918966364523848382506106179394235772395029788721306186952016420794804145631124905952103136061076643266886961178241381892015555099638200222249447194504082451341122502519637821695210573997670753981061458264118355417889153180841281073262935937836447460470926729282834006229571453935760593644658459098721652426154970766417292435960463905367868753821950303919781798234432998272038029063155193184039985018137026245365188171178677898869374676546799536208952198558258306460302868688355653022725288744014143221560882404431652751343944983442109327
c = 8641190030376811670503537177719719233418166235794962118828671236836174132083208517733734760455990850156371205118391537919769888760384574011411232571257192285256730733174399297826587479261381970232162702657952399683882650083181048279650913795429823628186888540572704055008102853692060360140858142686334722286525699998854566609078547487420929457446776757558492454916447188774943818970599916514467335772992690805247630814156710861067503956707301402347944233660194395192354000788262111000900574820275786269075882923600474781645848712157460135387134196156906258218217831988828360827613420801773911833194097791649069743116686685667300622630909231822986237104627385544169938138006242341269672868611269202418482629393372933567053272565557137741441902377611003983050084491513897727856173625922194300103448148829004025229567101761111396110940066254801762424343522707712480796358754008120503317686600144600226149617189681233392693738216138797012278242152852923361635415564580582002132107424154426980566696622448291815571736676562214017436
n = 1076246859437269645898003764327104347852443049519429833372038915264009774423737482018987571807662568251485615769880354898666799006772572239466617428164721157850526408878346223839884319846641438292436373441749602341461361190584638190903978829024853974880636148520803145113551453821058269641304504880310836801494499720662704717315748614372503735165114899680682056477494953525794354656896362929510309669119173103242509398650608116835276076364248473952717811633756784397347121601006659623317417388283638159905288128181587304367489096254611610975352096229116491567502061775862811850081040850421151385474249060884479729988512713640536139010928836126719149031115182144744359297169350288886555784650111
p1 = 145356063641618996012874664536921616978986640263438210169671010403677822239343590475177543891188656103067696467174379510912427160232486984044862545338401652910975162942038201716552753723984593267892098222213049269335313670049037479410635628460505327693176152061750827570561482918795206276991967169087371403553
'''
题目给了p1和n
我们先来求出来p
有
p
1
=
p
4
+
a
p_1 = p^4+a
p1=p4+a
则
p
=
p
1
−
a
4
<
p
1
4
p = \sqrt[4]{p_1 - a} < \sqrt[4]{p_1}
p=4p1−a<4p1
写成等式,有
p
+
γ
=
p
1
4
p + \gamma = \sqrt[4]{p_1}
p+γ=4p1
那么我们爆破这个伽马就行
def crack(n, p1):
for i in trange(10000):
test = iroot(p1, 4)[0] - i
if not is_prime(test):
continue
else:
q = iroot(n // test, 4)
if q[1]:
q = q[0]
print()
print("found!")
return test,q
这样就有p和q了
然后计算
φ
(
n
)
\varphi(n)
φ(n)也很简单
直接套欧拉函数就行
φ
(
n
)
=
φ
(
p
q
4
)
=
φ
(
p
)
∗
φ
(
q
4
)
=
(
p
−
1
)
∗
(
q
4
−
q
3
)
\varphi(n) = \varphi(pq^4) = \varphi(p)*\varphi(q^4) = (p-1)*(q^4 - q^3)
φ(n)=φ(pq4)=φ(p)∗φ(q4)=(p−1)∗(q4−q3)
然后就可以正常求解RSA得到s了
有s之后,要求m1
我们已经知道
c
≡
(
s
3
+
1
)
m
1
(
m
o
d
s
5
)
c\equiv(s^3 + 1)^{m_1} \pmod{s^5}
c≡(s3+1)m1(mods5)
将式子写成等式
c
+
k
s
5
=
(
s
3
+
1
)
m
1
,
k
∈
Z
c+ ks^5 = (s^3 + 1)^{m_1},k\in Z
c+ks5=(s3+1)m1,k∈Z
我们将等式右边二项式展开
(
s
3
+
1
)
m
1
=
C
m
1
0
s
3
m
1
1
0
+
C
m
1
1
s
3
(
m
1
−
1
)
1
1
+
⋯
+
C
m
1
m
1
−
1
s
3
1
m
1
−
1
+
C
m
1
m
1
s
0
1
m
1
(s^3 + 1)^{m_1} = C_{m_1}^{0}s^{3m_1}1^0 + C_{m_1}^{1}s^{3(m_1 - 1)}1^1 + \cdots + C_{m_1}^{m_1-1}s^{3}1^{m_1-1} + C^{m_1}_{m_1}s^01^{m_1}
(s3+1)m1=Cm10s3m110+Cm11s3(m1−1)11+⋯+Cm1m1−1s31m1−1+Cm1m1s01m1
我们把1全部化简掉,并且把前面几项和后边几项的排列组合式子展开
有
(
s
3
+
1
)
m
1
=
s
3
m
1
+
m
1
s
3
(
m
1
−
1
)
+
⋯
+
m
1
s
3
+
1
(s^3 + 1)^{m_1} = s^{3m_1}+m_1s^{3(m_1-1)} + \cdots + m_1s^3 + 1
(s3+1)m1=s3m1+m1s3(m1−1)+⋯+m1s3+1
显然,m1 远大于2
因此前边所有s的指数,都大于s的5次方
因此在模s的5次方的时候,可以被约去
因此式子化简为
c
≡
m
1
s
3
+
1
(
m
o
d
s
5
)
c \equiv m_1s^3 + 1 \pmod{s^5}
c≡m1s3+1(mods5)
从上式我们可以发现
只要
m
1
<
s
2
m_1 < s^2
m1<s2,那么恒等号可以化为等号
而s是1024位数,则m只要小于2048位即可
m的信息量显然小于256字节
毕竟flag也才长几十字节。
因此
m
=
c
−
1
s
3
m = \frac{c-1}{s^3}
m=s3c−1
from libnum import n2s,s2n
from gmpy2 import *
from tqdm import trange
def crack(n, p1):
for i in trange(10000):
test = iroot(p1, 4)[0] - i
if not is_prime(test):
continue
else:
q = iroot(n // test, 4)
if q[1]:
q = q[0]
print()
print("found!")
return test,q
n = 1076246859437269645898003764327104347852443049519429833372038915264009774423737482018987571807662568251485615769880354898666799006772572239466617428164721157850526408878346223839884319846641438292436373441749602341461361190584638190903978829024853974880636148520803145113551453821058269641304504880310836801494499720662704717315748614372503735165114899680682056477494953525794354656896362929510309669119173103242509398650608116835276076364248473952717811633756784397347121601006659623317417388283638159905288128181587304367489096254611610975352096229116491567502061775862811850081040850421151385474249060884479729988512713640536139010928836126719149031115182144744359297169350288886555784650111
p1 = 145356063641618996012874664536921616978986640263438210169671010403677822239343590475177543891188656103067696467174379510912427160232486984044862545338401652910975162942038201716552753723984593267892098222213049269335313670049037479410635628460505327693176152061750827570561482918795206276991967169087371403553
c1 = 671390498592586008552998377599101093977542184109077889081448730480869018650843045119891777468161631085086340705902115332025675787789530562679603254577287153918966364523848382506106179394235772395029788721306186952016420794804145631124905952103136061076643266886961178241381892015555099638200222249447194504082451341122502519637821695210573997670753981061458264118355417889153180841281073262935937836447460470926729282834006229571453935760593644658459098721652426154970766417292435960463905367868753821950303919781798234432998272038029063155193184039985018137026245365188171178677898869374676546799536208952198558258306460302868688355653022725288744014143221560882404431652751343944983442109327
c = 8641190030376811670503537177719719233418166235794962118828671236836174132083208517733734760455990850156371205118391537919769888760384574011411232571257192285256730733174399297826587479261381970232162702657952399683882650083181048279650913795429823628186888540572704055008102853692060360140858142686334722286525699998854566609078547487420929457446776757558492454916447188774943818970599916514467335772992690805247630814156710861067503956707301402347944233660194395192354000788262111000900574820275786269075882923600474781645848712157460135387134196156906258218217831988828360827613420801773911833194097791649069743116686685667300622630909231822986237104627385544169938138006242341269672868611269202418482629393372933567053272565557137741441902377611003983050084491513897727856173625922194300103448148829004025229567101761111396110940066254801762424343522707712480796358754008120503317686600144600226149617189681233392693738216138797012278242152852923361635415564580582002132107424154426980566696622448291815571736676562214017436
e = 0x10001
p,q = crack(n, p1)
print(p, q)
phi = (p-1)*(q**4 - q**3)
d = invert(e, phi)
s = pow(c1, d, n)
m = (c - 1) // (s**3)
print(m)
print(n2s(int(m)))
蓝鲨的RSA
这题的考点在去年亦有所记载(ISCTF2023 密码 1zRSA,baby group)
题目:
from secret import flag
import gmpy2
import decimal
from Crypto.Util.number import *
def gethint(h,p):
decimal.getcontext().prec = 1024
H = decimal.Decimal(int(h))
P = decimal.Decimal(int(p))
leak = decimal.Decimal((8*H*P - 1) / (16*P*P))
return leak
p = getPrime(512)
q = getPrime(512)
f = getPrime(512)
g = getPrime(128)
h = gmpy2.invert(f, p) * g % p
n = f*q
e = 65537
m = bytes_to_long(flag)
c = pow(m,e,n)
print('c =', c)
print('hint =', gethint(h,p))
print('n =',n)
#c = 587245179027322480379581200283415189810421958968516831191660631552695197401940961725169763339428980298128692606951200581483431566182271569207988054537414289564013883171160614196522169980339024564884190765084419167938640701193928669
#hint = 0.2427542737153618793334900104191212626446625872340179613972610728976081994921862517310186626304527115125924716035632505287111236596234811779375148657365336957626454491865164520834975233144235103885081268955448330597818844340656652982593545877449810282619387305007246499089258519062093814083383071737897364213169497762760797899310673216754376885295598952272100016962368762532805864796748393317534908268379601445004775495237901072144236328105526403608646831124542336002540011176406194984370372589752234640498423911217119220030242197564695880261480071310815379681250975672935544404797155655708441222387631967447088319826137200280810029390387418159394276760100487636516708987579464183208860911063948902432948269805493252899815187044807603000344378890835564906163242023600624338694473573763088471321731611077227112205396909637906507673367598721218000123789690455125909411309668615810240938664264212370815385282488986625554704015828254539339719586211726300858711328516487805251366293457402531199532556110786048074755505680210260049
#n = 839799159583571337450826982895478997157381520448790705455708438948150905361244823725400304016136863419723271227616684280477524669207590477657886623628732394537008838314015048569652202355464477680540884654473950183135276735347866051
首先先看gethint
给入p和h,给出一个leak
l
e
a
k
=
8
h
p
−
1
16
p
2
leak = \frac{8hp-1}{16p^2}
leak=16p28hp−1
化简一下,有
l
e
a
k
=
h
2
p
−
1
16
p
2
leak = \frac{h}{2p} - \frac{1}{16p^2}
leak=2ph−16p21
等式两边同乘以8
8
l
e
a
k
=
4
h
p
−
1
2
p
2
8leak = \frac{4h}{p} - \frac{1}{2p^2}
8leak=p4h−2p21
移项变化一下
4
h
p
−
8
l
e
a
k
=
1
2
p
2
\frac{4h}{p} - 8leak = \frac{1}{2p^2}
p4h−8leak=2p21
加一个绝对值
∣
4
(
2
l
e
a
k
−
h
p
)
∣
=
1
2
p
2
|4(2leak - \frac{h}{p})| = \frac{1}{2p^2}
∣4(2leak−ph)∣=2p21
放缩一下
∣
2
l
e
a
k
−
h
p
∣
<
∣
4
(
2
l
e
a
k
−
h
p
)
∣
=
1
2
p
2
|2leak - \frac{h}{p}| < |4(2leak - \frac{h}{p})| = \frac{1}{2p^2}
∣2leak−ph∣<∣4(2leak−ph)∣=2p21
即
∣
2
l
e
a
k
−
h
p
∣
<
1
2
p
2
|2leak - \frac{h}{p}| < \frac{1}{2p^2}
∣2leak−ph∣<2p21
我们有引理:
legendre Continued fraction
若有
∣
ξ
−
a
b
∣
<
1
2
b
2
\left | \xi - \frac{a}{b} \right | < \frac{1}{2b^2}
ξ−ba
<2b21
那么
a
b
\frac{a}{b}
ba是
ξ
\xi
ξ的连分数展开的某一项渐进分数
具体不证,可以去这儿仔细研究推导
https://math.stackexchange.com/questions/531736/legendres-proof-continued-fractions-from-hardys-book
关于连分数相关内容,鉴于去年已经详细说明,如果有不会的可以去翻翻我关于ISCTF2023 1zRSA 相关内容。
总之,我们可以在渐进分数中找到h
我们去年用的原生python来计算连分数,我们今年也可以通过sagemath进行求解。
这里解释几个下列代码出现的sagemath函数
continued_fraction用来计算a的连分数,后边的参数是如果a是个无穷连分数,则展开1024项。
c = continued_fraction(a, 1024)
同时,你可以对他计算渐进分数,如我们上边把结果给到了c,那么我们可以通过c.convergents()方法计算c的渐进分数。给出的结果是个形式表示,即用分子和分母进行表示,而非给出分子和分母或者计算出具体的数。因此我们可以通过字符串处理得到其每一项渐进分数的分子和分母。
同时,我们又知道p是个512位长的质数
因此我们直接求2*hint 的渐近展开就行。
这里我们唯一需要注意的就是,由于python的精度问题,直接给进去貌似是不太可行的,或许有sagemath函数可以保留精度,但是我们这里选择用小数点后的数除以10的1024次方来表示。
当然,别忘了乘上2。
def crack():
hint = 2*2427542737153618793334900104191212626446625872340179613972610728976081994921862517310186626304527115125924716035632505287111236596234811779375148657365336957626454491865164520834975233144235103885081268955448330597818844340656652982593545877449810282619387305007246499089258519062093814083383071737897364213169497762760797899310673216754376885295598952272100016962368762532805864796748393317534908268379601445004775495237901072144236328105526403608646831124542336002540011176406194984370372589752234640498423911217119220030242197564695880261480071310815379681250975672935544404797155655708441222387631967447088319826137200280810029390387418159394276760100487636516708987579464183208860911063948902432948269805493252899815187044807603000344378890835564906163242023600624338694473573763088471321731611077227112205396909637906507673367598721218000123789690455125909411309668615810240938664264212370815385282488986625554704015828254539339719586211726300858711328516487805251366293457402531199532556110786048074755505680210260049
l = hint/10**1024
c=continued_fraction(l, 1024)
result = [tuple([int(j) for j in str(i).split("/")]) for i in c.convergents()]
for i in result[2:]:
h,p = i
if len(bin(p)[2:]) == 512 and is_prime(p):
print(h,p)
return h, p
这样我们就得到h和p了
然后后边的问题是一个格密码学问题
简单写一下
我们已经知道两个值,一个h一个p
并且有
h = gmpy2.invert(f, p) * g % p
h
≡
f
−
1
g
(
m
o
d
p
)
h \equiv f^{-1}g \pmod p
h≡f−1g(modp)
我们的目的是求出来f
我们先对式子进行恒等变换,首先左右各乘上一个
f
f
f
f
h
≡
g
(
m
o
d
p
)
(1)
fh \equiv g \pmod p \tag{1}
fh≡g(modp)(1)
考虑1式,有
k
∈
Z
k \in Z
k∈Z满足
f
h
=
g
+
k
p
fh = g + kp
fh=g+kp
我们不妨构造两个向量
a
1
=
(
1
,
h
)
,
a
2
=
(
0
,
p
)
a_1=(1,h),a_2=(0,p)
a1=(1,h),a2=(0,p)
那么考虑向量
(
f
,
g
)
(f,g)
(f,g)可以表示为
(
f
,
g
)
=
f
a
1
−
k
a
2
(f,g) = fa_1 - ka_2
(f,g)=fa1−ka2
我们已知h和p
若我们能求出来f和g,那么我们显然可以直接进行解密
那么上述都是数学变换,怎么进行求解呢?
我们这里不进行具体证明,有兴趣可以自行验证
二维格上的高斯启发式:
v
m
i
n
=
d
e
t
L
π
e
v_{min = }\sqrt{\frac{detL}{\pi e}}
vmin=πedetL
其中v为二维格上的最小向量(注:上式为估计值)
那么我们不妨将a1,a2设为一个二阶格系统,
则detL为由a1,a2组成的矩阵的行列式
∣
a
1
,
a
2
∣
=
1
∗
p
−
0
∗
h
=
p
|a_1,a_2| = 1*p - 0*h = p
∣a1,a2∣=1∗p−0∗h=p
即最小向量在p附近
同时由题目可得,
∣
(
f
,
g
)
∣
≤
p
|(f,g)| \leq p
∣(f,g)∣≤p
不加证明的猜测(f,g)为最小向量
因此,我们只需要求以a1,a2为基向量的格上最短向量。
这个工作本应可以用二维的高斯格基规约来求得。但是我不清楚为什么这里没有求出来。我们直接用LLL算法了。
def crack():
hint = 2*2427542737153618793334900104191212626446625872340179613972610728976081994921862517310186626304527115125924716035632505287111236596234811779375148657365336957626454491865164520834975233144235103885081268955448330597818844340656652982593545877449810282619387305007246499089258519062093814083383071737897364213169497762760797899310673216754376885295598952272100016962368762532805864796748393317534908268379601445004775495237901072144236328105526403608646831124542336002540011176406194984370372589752234640498423911217119220030242197564695880261480071310815379681250975672935544404797155655708441222387631967447088319826137200280810029390387418159394276760100487636516708987579464183208860911063948902432948269805493252899815187044807603000344378890835564906163242023600624338694473573763088471321731611077227112205396909637906507673367598721218000123789690455125909411309668615810240938664264212370815385282488986625554704015828254539339719586211726300858711328516487805251366293457402531199532556110786048074755505680210260049
l = hint/10**1024
c=continued_fraction(l, 1024)
result = [tuple([int(j) for j in str(i).split("/")]) for i in c.convergents()]
for i in result[2:]:
h,p = i
if len(bin(p)[2:]) == 512 and is_prime(p):
print(h,p)
return h, p
h,p = crack()
t = matrix([[1,h],[0,p]])
data = t.LLL()
f, g = data[0][0], data[0][1]
print(f, g)
这样我们就求出来f和g了
有f了自然就可以按照正常思路去求解RSA了
因此这里不过多赘述了。
直接给出全部代码
from gmpy2 import *
from libnum import n2s,s2n
def crack():
hint = 2*2427542737153618793334900104191212626446625872340179613972610728976081994921862517310186626304527115125924716035632505287111236596234811779375148657365336957626454491865164520834975233144235103885081268955448330597818844340656652982593545877449810282619387305007246499089258519062093814083383071737897364213169497762760797899310673216754376885295598952272100016962368762532805864796748393317534908268379601445004775495237901072144236328105526403608646831124542336002540011176406194984370372589752234640498423911217119220030242197564695880261480071310815379681250975672935544404797155655708441222387631967447088319826137200280810029390387418159394276760100487636516708987579464183208860911063948902432948269805493252899815187044807603000344378890835564906163242023600624338694473573763088471321731611077227112205396909637906507673367598721218000123789690455125909411309668615810240938664264212370815385282488986625554704015828254539339719586211726300858711328516487805251366293457402531199532556110786048074755505680210260049
l = hint/10**1024
c=continued_fraction(l, 1024)
result = [tuple([int(j) for j in str(i).split("/")]) for i in c.convergents()]
for i in result[2:]:
h,p = i
if len(bin(p)[2:]) == 512 and is_prime(p):
print(h,p)
return h, p
h,p = crack()
t = matrix([[1,h],[0,p]])
data = t.LLL()
f, g = data[0][0], data[0][1]
n = 839799159583571337450826982895478997157381520448790705455708438948150905361244823725400304016136863419723271227616684280477524669207590477657886623628732394537008838314015048569652202355464477680540884654473950183135276735347866051
e = 0x10001
c = 587245179027322480379581200283415189810421958968516831191660631552695197401940961725169763339428980298128692606951200581483431566182271569207988054537414289564013883171160614196522169980339024564884190765084419167938640701193928669
q = n // f
assert f*q == n
phi = (f-1)*(q-1)
d = invert(e,phi)
m = pow(c,d,n)
print(n2s(int(m)))
我和小蓝鲨的秘密
GPT牛逼(
n分解一下,yafu或者factordb都能分解
除了我自己分解了一下p和q
然后算了算n
剩下的都是GPT干的
from gmpy2 import *
from PIL import Image
from libnum import n2s,s2n
import numpy as np
p = 160216064374859
q = 186431677583461
e = 0x10001
n = p*q
phi = (p-1)*(q-1)
d = invert(e,phi)
# 加载加密的图像数据
encrypted_array = np.load("encrypted_image.npy", allow_pickle = True)
h, w, _ = encrypted_array.shape
# 初始化解密后的图像数组
decrypted_array = np.zeros((h, w, 3), dtype=np.uint8)
for i in range(h):
for j in range(w):
r_enc, g_enc, b_enc = encrypted_array[i, j]
# RSA 解密
r = pow(r_enc, d, n)
g = pow(g_enc, d, n)
b = pow(b_enc, d, n)
decrypted_array[i, j] = [r, g, b]
decrypted_img = Image.fromarray(decrypted_array, "RGB")
decrypted_img.save("decrypted_flag.jpg")
ISCTF{success!_bluesharkinfo_gogogo!}
ChaCha20-Poly1305
题目:
from Crypto.Cipher import ChaCha20_Poly1305
import os
key = os.urandom(32)
nonce = os.urandom(12)
with open('flag.txt', 'rb') as f:
plaintext = f.read()
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
ct, tag = cipher.encrypt_and_digest(plaintext)
print(f"Encrypted Flag: {ct.hex()}")
print(f"Tag: {tag.hex()}")
print(f"Nonce: {nonce.hex()}")
with open('key.txt', 'w') as key_file:
key_file.write(key.hex())
key = '3=t#sMX3?9GHSPdi4i^gk!3*(cH8S8XT2y&?Tv4!?AGG=R]ZDy/PVVa+DqiXAH*}DS&Nn*a+@<H,=!L'
'''
Encrypted Flag: 20408b9fc498063ad53a4abb53633a6a15df0ddaf173012d620fa33001794dbb8c038920273464e13170e26d08923aeb
Tag: 70ffcc508bf4519e7616f602123c307b
Nonce: d8ebeedec812a6d71240cc50
'''
这题也很简单,唯一的难点就是看出来key是个base92
剩下的就交给ChatGPT写就行了
这个key用basecrack跑一下就行
然后交给GPT或者自己写都OK
from libnum import n2s,s2n
from Crypto.Cipher import ChaCha20_Poly1305
key = n2s(0x173974535637a5ef30a116b03d00bd2fe751951ca3eaa62daec2b8f5ca5b6135)
tag = n2s(0x70ffcc508bf4519e7616f602123c307b)
nonce = n2s(0xd8ebeedec812a6d71240cc50)
ciphertext = n2s(0x20408b9fc498063ad53a4abb53633a6a15df0ddaf173012d620fa33001794dbb8c038920273464e13170e26d08923aeb)
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
print(plaintext.decode())
小蓝鲨的密码
好吧我承认这题是我想复杂了(乐
跟今年ISCC一样,也有个路在脚下(是今年吧?)
实际上就是文件名
rar压缩包的密码是bluesharkinfo
得到密码本
想爆破也行,这个其实能看出来
因为里边有个blueshark和isctf2024
试一下发现是isctf2024
免去写脚本的烦恼了
用在线网站解密即可
https://tool.oschina.net/encrypt/
ezmath
修复代码
这两个函数一个是求和,一个是求极限,用到了非常简单的装饰器的用法。
先传入进去参数,然后在传入进去极限的值,也就是start之类的,也就是前n-1个在计算时是常数,最后一个是变量。对这个变量积分或者求极限。这个求极限的pos就是求左极限还是右极限,当然对于默认的无穷来说,左极限显然等于右极限。
def sumFunc(func):
def wapper(*args,start = 1,end=INF):
sums = 0
for i in trange(start, 0xffff * end):
sums += function(*args,j/0xf) * (1/0xf)
return sums
return wapper
def limitFunc(func):
def wapper(*args, approch = bigINF, pos = "+"):
o = 1/bigINF
return function(args, eval(f"{approch} {pos} {o}"))
return wapper
首先是第一个的错误
1、for循环应该是range而不是trange(请先忘掉tqdm)(line 4)
2、循环变量应该是i而不是j(line 5)
然后是第二个的错误
1、传入进来的是func,而不是function(line 12)
2、args应该解引用(line 12)
修复后的代码
def sumFunc(func):
def wapper(*args,start = 1,end=INF):
sums = 0
for i in range(start, 0xffff * end):
sums += func(*args,i/0xf) * (1/0xf)
return sums
return wapper
def limitFunc(func):
def wapper(*args, approch = bigINF, pos = "+"):
o = 1/bigINF
return func(*args, eval(f"{approch} {pos} {o}"))
return wapper
其实修不修影响并不大,毕竟如果修了程序直接跑出flag岂不是成签到了)
修只是为了能跑)
接下来其实就是高数)
首先这个magicNumber其实就是自然常数e
而limitFunc的默认是趋向于正无穷的,也就是
lim t → + ∞ ( 1 + 1 t ) t = e \lim_{t \to +\infty}(1+\frac{1}{t})^t = e t→+∞lim(1+t1)t=e
就是个重要极限
第二个可能比较难以看出来,我们先写出来
f ( x ) = ∫ 0 + ∞ t x − 1 e − t d t f(x) = \int^{+\infty}_{0}t^{x-1}e^{-t} dt f(x)=∫0+∞tx−1e−tdt
如果你注意力惊人的话,是可以注意到这玩意儿其实就是阶乘函数的解析延拓,什么是解析延拓,简单来说就是将定义域拓宽,本身的阶乘只能是整数,现在我们可以让他取实数了。(注意力不惊人的话,可以去找attention is all you need的,那个注意力比较惊人(人话:GPT))
因此,这个函数有以下性质
f ( x + 1 ) = x f ( x ) f(x+1) = xf(x) f(x+1)=xf(x)
f ( 1 − x ) f ( x ) = π s i n π x , x ∈ ( 0 , 1 ) f(1-x)f(x)=\frac{\pi}{sin\pi x},x \in (0,1) f(1−x)f(x)=sinπxπ,x∈(0,1)
因此,可以让x=0.5
f
(
1
2
)
=
π
f(\frac{1}{2}) = \sqrt{\pi}
f(21)=π
那我们题中的(这个符号就是大写的gamma(伽马),用来替代了上面的f罢了)
Γ
(
5
2
)
=
3
2
Γ
(
3
2
)
=
3
4
Γ
(
1
2
)
=
3
4
π
\Gamma(\frac{5}{2}) = \frac{3}{2}\Gamma(\frac{3}{2}) = \frac{3}{4}\Gamma(\frac{1}{2})=\frac{3}{4}\sqrt{\pi}
Γ(25)=23Γ(23)=43Γ(21)=43π
Γ
(
3
2
)
=
1
2
Γ
(
1
2
)
=
π
2
\Gamma(\frac{3}{2})=\frac{1}{2}\Gamma(\frac{1}{2}) = \frac{\sqrt{\pi}}{2}
Γ(23)=21Γ(21)=2π
然后就是common了
这个其实也很常见,写出来就知道了
∫
0
+
∞
e
−
t
2
d
t
\int_{0}^{+\infty}e^{-t^2}dt
∫0+∞e−t2dt
包是高斯积分的。
这个证法真的太多太多了,总之,我们有他的反常积分
∫ − ∞ + ∞ e t 2 d t = π \int_{-\infty}^{+\infty}e^{t^2}dt = \sqrt{\pi} ∫−∞+∞et2dt=π
被积函数是个偶函数,因此有
∫ 0 + ∞ e − t 2 d t = π 2 \int_{0}^{+\infty}e^{-t^2}dt = \frac{\sqrt{\pi}}{2} ∫0+∞e−t2dt=2π
正好是gamma(3/2)的值
验证了我们几个函数的正确性。
那么只要计算gamma(5/2)就行了,前边我们已经计算过他的值了。
from math import sqrt,pi
from hashlib import md5
import random
key = int(str(3*sqrt(pi)/4)[2:])
print(key)
random.seed(key)
print(md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest())
逃跑的HIM
先放出来本章灵感来源,事实上也就是完全照着这个就能复现出来。。。
https://github.com/spawnmason/randar-explanation
简单说一下思路,不用代码审计了就(其实ai审计也能找到答案,乐)
我自己实现了一套java的那堆随机数(包括命名也是java的那一套,Random随机数则是我直接翻java的源代码照抄的,乐)
主要的main里边就是HIM会设置两个在-23440 到23440的随机数,分别为X,Z
我们的目标就是恢复这两个随机数
恢复5次即可。
主要是考察了代码审计,格基规约,线性同余发生器破解相关的内容。(当然硬说来点pwntools也确实有)
本来想套层二维码给X,Z,然后还要读二维码,一方面时间不足,另一方面也没必要硬套了感觉,就没上。
其实大部分文章都有讲,这里就主要是代码审计之后找到漏洞然后恢复就行了。逻辑还是比较明显的,就看会不会破解了(乐
如果复现有问题联系我(建议先仔细读读上边那个文章)
大致就这样。。。
需要给sagemath装上pwntools然后就能跑了
from pwn import *
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
rl = lambda : p.recvline()
it = lambda : p.interactive()
ip = ''
port = 0
p = remote(ip , port)
# p = remote("127.0.0.1", 8888)
sla("请输入用户名: ",b"guoql")
sla("=>请选择:",b"start")
for _ in range(5):
sla("输入yes投币开始:",b"yes")
sla("请选择:","2")
sla("请输入想要挖掘的方块坐标(例: (1,1,1) ):",b"(1,1,1)")
data = rl().decode('utf-8')
blockX,blockY,blockZ = tuple([float("0." + i.split(".")[1]) for i in data.split("Block drop in ")[1][:-1][1:-1].split(", ")])
print(data,blockX,blockY,blockZ)
randX = round(2 * 2**24 * (blockX - 0.25))
randY = round(2 * 2**24 * (blockY - 0.25))
randZ = round(2 * 2**24 * (blockZ - 0.25))
# 获取低24位数据
print(f"{randX},{randY},{randZ}")
# 格基规约得到原始seed
def crack(a0,b0,c0):
a=25214903917
b=11
c=281474976710656
a1 = a0 * 2**24 + 2**23
b1 = b0 * 2**24 + 2**23
c1 = c0 * 2**24 + 2**23
v = vector([a1,b1 - b,c1 - b - a*b])
matex = matrix.zero(3)
matex[0] = [1,a,a*a]
matex[1] = [0,c,0]
matex[2] = [0,0,c]
lattice = matex.LLL()
base = lattice.transpose().inverse()
result = base*v
final = vector([round(RDF(i)) for i in result])
solved = lattice.transpose() * final
return solved
seed = int(crack(randX, randY, randZ)[0])
# LCG线性同余求解出来上一个seed
old_seed = ((seed - 11 ) * 246154705703781) % 2**48
# sagemath的异或是^^而不是^
HIM_seed = old_seed ^^ 0x5DEECE66D
print(old_seed,HIM_seed)
world_seed = 14617218721232814696
# input()
# 寻找x,z需要满足在-23440 到 23400 的范围内
for x in range(-23440, 23441):
z = ((HIM_seed - world_seed - 10387319 - x * 341873128712 ) * 211541297333629 ) % 2**48
if z >= 2**48 + -23440 :
z = z - 2**48
print(f"x,z found:{x},{z}")
break
if z <= 23440:
print(f"x,z found:{x},{z}")
break
sla("请选择:", b"1")
sla("请输入HIM所在区块的坐标(例: (10,-25) ):", f"({x},{z})".encode('utf-8'))
print(rl().decode('utf-8'))
print(rl().decode('utf-8'))
更多推荐
所有评论(0)