GHCTF

Posted by Ma3t1n on 2025-03-14
Estimated Reading Time 12 Minutes
Words 2.3k In Total
Viewed Times

摸鱼的时候打了一下这个比赛,写个WP记录一下

baby_lattice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from Crypto.Util.number import *
from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad
from secret import flag
miku = 30
p = getPrime(512)
key = getPrime(512)
while key> p:
key= getPrime(512)
ts = []
gs = []
zs = []
for i in range(miku):
t = getPrime(512)
z = getPrime(400)
g= (t * key + z) % p
ts.append(t)
gs.append(g)
zs.append(z)
print(f'p = {p}')
print(f'ts = {ts}')
print(f'gs = {gs}')
iv= os.urandom(16)
cipher = AES.new(str(key).encode()[:16], AES.MODE_CBC,iv)
ciphertext=cipher.encrypt(pad(flag.encode(),16))
print(f'iv={iv}')
print(f'ciphertext={ciphertext}')

​ 简单的隐藏数问题

​ 已知$g= (t \times key + z) mod p $,构造如下格,其中K就是z的比特数用来配格系数的,格规约后找到对应向量求出key后解密即可得到flag

$\begin{bmatrix}k_1&k_2&…&k_n&key&-1\end{bmatrix}\times\begin{bmatrix}p&0&\cdots&0&0&0\\0&p&\cdots&0&0&0\\\vdots&\vdots&\ddots&\vdots&\vdots&\vdots\\0&0&\cdots&p&0&0\\t_1&t_2&\cdots&t_n&\frac{K}{p}&0\\g_q&g_2&\cdots&g_n&0&K\end{bmatrix}=\begin{bmatrix}z_1&z_2&\cdots&z_n&\frac{key*K}{p}&K\end{bmatrix}$

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from output import *
from sage.all import *
from Crypto.Util.number import *
from Crypto.Cipher import AES
m=zero_matrix(QQ,len(ts)+2)
K=2**400
for i in range(len(ts)):
m[i,i]=p
m[len(ts),i]=ts[i]
m[len(ts)+1,i]=gs[i]
m[-1,-1]=K
m[-2,-2]=K/p
res=m.LLL()
for i in res:
if i[-1] == K:
z0=abs(i[0])
key=(gs[0]-z0)*inverse(ts[0],p)%p
cipher = AES.new(str(key).encode()[:16], AES.MODE_CBC,iv)
flag=cipher.decrypt(ciphertext)
if b'NSSCTF' in flag:
print(flag)
#NSSCTF{F@@@un7_L4444t1c3333!!}

MIMT_RSA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from Crypto.Util.number import *
from hashlib import md5
from secret import KEY, flag


assert int(KEY).bit_length() == 36
assert not isPrime(KEY)

p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 0x10001

ck = pow(KEY, e, n)


assert flag == b'NSSCTF{' + md5(str(KEY).encode()).hexdigest().encode() + b'}'

print(f"{n = }")
print(f"{e = }")
print(f"{ck = }")

'''
n = 26563847822899403123579768059987758748518109506340688366937229057385768563897579939399589878779201509595131302887212371556759550226965583832707699167542469352676806103999861576255689028708092007726895892953065618536676788020023461249303717579266840903337614272894749021562443472322941868357046500507962652585875038973455411548683247853955371839865042918531636085668780924020410159272977805762814306445393524647460775620243065858710021030314398928537847762167177417552351157872682037902372485985979513934517709478252552309280270916202653365726591219198063597536812483568301622917160509027075508471349507817295226801011
e = 65537
ck = 8371316287078036479056771367631991220353236851470185127168826270131149168993253524332451231708758763231051593801540258044681874144589595532078353953294719353350061853623495168005196486200144643168051115479293775329183635187974365652867387949378467702492757863040766745765841802577850659614528558282832995416523310220159445712674390202765601817050315773584214422244200409445854102170875265289152628311393710624256106528871400593480435083264403949059237446948467480548680533474642869718029551240453665446328781616706968352290100705279838871524562305806920722372815812982124238074246044446213460443693473663239594932076
'''

​ RSA加密,加密的明文比特只有36个比特,除了明文比特数较小外其他都很常规,所以攻击点只有可能在攻击明文,但是直接遍历的话,36个比特还是比较大的,所以假设明文可以分解为两个数的乘积,就可以使用mitm的方法进行遍历

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import tqdm 
from Crypto.Util.number import *
from hashlib import md5
table={}
n = 26563847822899403123579768059987758748518109506340688366937229057385768563897579939399589878779201509595131302887212371556759550226965583832707699167542469352676806103999861576255689028708092007726895892953065618536676788020023461249303717579266840903337614272894749021562443472322941868357046500507962652585875038973455411548683247853955371839865042918531636085668780924020410159272977805762814306445393524647460775620243065858710021030314398928537847762167177417552351157872682037902372485985979513934517709478252552309280270916202653365726591219198063597536812483568301622917160509027075508471349507817295226801011
e = 65537
ck = 8371316287078036479056771367631991220353236851470185127168826270131149168993253524332451231708758763231051593801540258044681874144589595532078353953294719353350061853623495168005196486200144643168051115479293775329183635187974365652867387949378467702492757863040766745765841802577850659614528558282832995416523310220159445712674390202765601817050315773584214422244200409445854102170875265289152628311393710624256106528871400593480435083264403949059237446948467480548680533474642869718029551240453665446328781616706968352290100705279838871524562305806920722372815812982124238074246044446213460443693473663239594932076

for i in tqdm.tqdm(range(2**15,2**21)):
table[pow(i,e,n)]=i
for i in trange(2**15,2**18):
c=(ck*inverse(pow(i,e,n),n))%n
try:
KEY=table[c]*i
break
except:
continue
KEY=62495925932
print(b'NSSCTF{' + md5(str(KEY).encode()).hexdigest().encode() + b'}')

EZ_Fermat_bag_PRO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import getPrime, bytes_to_long
from random import *
from secret import f, flag

assert len(flag) == 88
assert flag.startswith(b'NSSCTF{')
assert flag.endswith(b'}')

p = getPrime(512)
q = getPrime(512)
n = p*q
FLAG=b'NSCTF{'+b'1'*81+b'}'

w = pow(2,f(p,q),n)
assert all(chr(i) in ''.join(list(set(str(p)))) for i in flag[7:-1:])
c = bytes_to_long(flag) % p



print(f'{n = }\n')
print(f'{f = }\n')
print(f'{w = }\n')
print(f'{c = }\n')

​ 观察题目发现$w=2^{f(p,q)}modn$,f是一个关于p,q的多项式,对于这个式子可以调整一下,$w_2=2^{f(p,q)}modp$,将模n变成模p后,指数就是模$p-1$,所以$w=w_2+kp$,所以求出来$w_2$后$gcd(w-w_2,n)$即可分解p,q

1
2
3
4
5
6
7
8
9
10
from Crypto.Util.number import *
n = 95656952327201449381426394713246214670537600365883923624876350719801926817916514429721785287844335184715049179879891389941974481490433975689601829920289485889138252888029716516069912637121531561601839948367426922036690701168975937162280451323099126372019216020898338909808577022618554997063496690156977790629
P.<x,y> = ZZ[]
f =
w = 12796020294902567574981427270787776254781813995526831579805652479456168245098217943847166109912113827479436654134179666391771173421469188197935460525521295192736123648410762964187396897298542198935971755852754544978564521188423737649175136194386664628304164316905741781089536713701674793641345344818309314224
c = 10266913434526071998707605266130137733134248608585146234981245806763995653822203763396430876254213500327272952979577138542487120755771047170064775346450942
t = f%(x-1)
w2 = pow(2,t(y=n),n)
p=gcd(w-w2,n)
q=n//n

​ 分解n后还原flag,由于flag长度是88个字节,去掉前后缀后也还有80个字节,是640个比特,大于p的的512比特,所以分解后也无法直接还原flag。可以将$flag(modp)=c$看成一个方程:

$$
flag[0]\times 256^0+flag[1]\times 256^1+…+flag[79]\times 256^{79}(modp)=c
$$

​ 对于这个方程可以使用格规约的方法规约出flag

​ 观察题目

1
assert all(chr(i) in ''.join(list(set(str(p)))) for i in flag[7:-1:])

​ 告诉了flag中间的部分全都是数字,所以我们规约的目标向量是在$48-57$了,所以可以先将c减去bytes_to_long(b’0’*80)后目标向量就在$0-9$了,目标向量值在$0-9$平均值为5,配好系数后进行规约后判断一下即可得到目标向量

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
prefix = b"NSSCTF{"
suffix = b"}"
length = 88 - len(prefix) - len(suffix)

c -= 256^(len(suffix) + length) * bytes_to_long(prefix)
c -= bytes_to_long(suffix)
c = c * inverse(256,p) % p
c -= bytes_to_long(b'0'*length)
L = Matrix(ZZ,length+2,length+2)
for i in range(length):
L[i,i] = 1
L[i,-1] = 256^i
L[-2,-2] = 5
L[-2,-1] = -c
L[-1,-1] = p
L[:,-1:] *= p
res = L.BKZ()
flag = ""
for i in res[:-1]:
if(abs(i[-2]) == 5 and all(abs(j) < 10 for j in i[:-2])):
for j in i[:-2][::-1]:
flag += chr(48 + abs(j))
print(flag)

RSA_and_DSA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from random import getrandbits, randint
from secrets import randbelow
from Crypto.Util.number import*
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import hashlib
import random
import gmpy2
ink=getPrime(20)
p1= getPrime(512)
q1= getPrime(512)
N = p1 * q1
phi = (p1-1) * (q1-1)
while True:
d1= getRandomNBitInteger(200)
if GCD(d1, phi) == 1:
e = inverse(d1, phi)
break
c_ink = pow(ink, e, N)
print(f'c_ink=',c_ink)
print(f'e=',e)
print(f'N=',N)

k= getPrime(64)
q = getPrime(160)
def sign(msg, pub, pri, k):
(p,q,g,y) = pub
x = pri
r = int(pow(g, k, p) % q)
h = int(hashlib.sha256(msg).digest().hex(),16)
s = int((h + x * r) * gmpy2.invert(k, q) % q)
return (r, s)

while True:
temp = q * getrandbits(864)
if isPrime(temp + 1):
p = temp + 1
break
assert p % q == 1
h = randint(1, p - 1)
g = pow(h, (p - 1) // q, p)
y = pow(g, k, p)
pub = (p,q,g,y)
pri = random.randint(1, q-1)
print(f"(r1,s1)=",sign(b'GHCTF-2025', pub, pri, k))
print(f"(r2,s2)=",sign(b'GHCTF-2025', pub, pri, k+ink))
print(f"{g= }")
print(f"{q= }")
print(f"{p= }")
print(f"{y= }")
key = hashlib.sha1(str(pri).encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_ECB)
flag="NSSCTF{xxxxxxxxx}"
ciphertext = cipher.encrypt(pad(flag.encode(), 16))
print(f"{ciphertext = }")
'''
c_ink= 84329531596553394336538987023357227935440127545924398750500007122949822951975451942488164538560925222694222413022235832336439700420379598454619959178424907616592885325169668838139433265501326382467741883799799897305247164532663683724926267222341485376684034461780316163663624769479766276645610470850267093664
e= 100797590979191597676081881632112443200677974501832055481332601002844223186483558337099380805371010917502984674789369037985572270571944684404114475915036053451756526659905789324413633016308331745100752282051937597697581233757669107763643041665187533373053952694612521031477624363476981177214961821456672635823
N= 133020919573254586736009662994351483197630110046444622015176359062686053521475990861985101412597512894313048001198942449066636145265799205815566892581351543233960812384316942438814742826123037762680960898927252792974233266551853930274479435403549161383103059746381782668941421906340168652870371226382805032027
(r1,s1)= (105538622724986198173818280402723234123231812870, 165871242333491991006684781121637801537623792920)
(r2,s2)= (895673018852361693797535983771888430717799939767, 511956887428148277909616673338517730698888202223)
g= 97444164915108666817264719918456841236668149777715575246719562319277238318814584882880249446488655758781498681349330709135670188875982069778879957837454582916193915374305422049064769688749957611500682447936476425649642359105731049262259786188565867271216015835626264543593116387612078934710741467063982007499
q= 974306102330898613562307019447798934376234044213
p= 113996945917185663452903189185812083054654586038361814576057637684218572059191009152754335053396974825607186512631652893899380922217026759410880236546966561476761050482902589270845489570126254333374605973087540746242818447451510386137109253463070487353845675998098620056687507969012229115435439218407426962991
y= 8015503667614219250943034151839311927430676423719991507127801373333532219335171760992873121586820712328636972152697436159934583810723294897449200937370031784164230148453787378834760102389031574149857480339843366568164403131143385627621208571673677878768568991050568882099039880976450795530322753270408770484
ciphertext = b'\xb0\ra\x9c\xeb9y\xd5k\xfde\xdfJ\xba\n\xce^u\xae\x81J8\xe4\x8da\xdf;H@WV5'
'''

​ 先使用维纳攻击分解p,q,阅读后面的代码发现就是一个模方程组,解方程即可。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from Crypto.Cipher import AES
import hashlib
c_ink= 75517502692097831363443739147565176367247985201143975453326891807623085586665800338505194812511215986799510259417486636115714543892322380908775898968005967267154089356401466517827082639942650711458196552847137272733225451581167527549711435805194039361007506436462393605949663173972848316802086005675594122447
e= 97127528017076464922170978933159739328499830874876612140194720448608536284451056980759925228574802703400503852897647806707933102198339936307176078592550748707182506634151382952065240918923594664309561466538540752851827183955776181255541306419282376724578231110985180090748520497985751591062886932254909959583
N= 131342286566556880877092331187418465653855813425966929864425381510875531237549624989644814104311810243468058174748867544024292263674725375273146689145421426693384862215460097683187892351130513429928063652701077721570140977719823754701988835199434602294597102748436781125528389125846980183998136743830655746063
(r1,s1)= (116607429176396769010327954525649019081679807573, 242024511677350537268048640408155954695100314686)
(r2,s2)= (282969697665204582571637561594660002955972273916, 233488943967710383661411268886726155900968304282)
g= 113752591438136097752416877421595518178059067044406008965947486693591255247711343925741016027611310257564826355221058212913879375956265361413159461801130112690842862767232535007802294944943540511148985219047761964228666223605898858379133610079745082176804146052086680551043775640630819062323009071190616231206
q= 1010682595723348988315901923086844563134854720501
p= 117574623402990360322255542120443410701206393780334500865748478770699335257408652117586356058603035930256320433146236288486738066821845146885689024168044244453298677322763816219621376364185484753693835156465778487436550070788009331605135011517651578548403565196930955818239577581944709384126001757349228062611
y= 114719310327856435690312712426667059528255758467780345974417610618467568889317865434557927492426543544900066735337367408842750916882737134575812646864528120528284507025781167314123831868039617392195979751697057669675359335622753482920688147222344801914086866640706838774098397923450797553056502975678740968481
ciphertext = b'\x10\xbcL|\xcb\xe5W\x1e0\xa3\x83\x85vr^SmU\xac\xe3L\x93"#\xb4\x81\xd0\xf0S\x05\xb7\xc7'
def factor_rsa_wiener(N, e):
N = Integer(N)
e = Integer(e)
cf = (e / N).continued_fraction().convergents()
for f in cf:
k = f.numer()
d = f.denom()
if k == 0:
continue
phi_N = ((e * d) - 1) / k
b = -(N - phi_N + 1)
dis = b ^ 2 - 4 * N
if dis.sign() == 1:
dis_sqrt = sqrt(dis)
p = (-b + dis_sqrt) / 2
q = (-b - dis_sqrt) / 2
if p.is_integer() and q.is_integer() and (p * q) % N == 0:
p = p % N
q = q % N
if p > q:
return (p, q)
else:
return (q, p)
# print(factor_rsa_wiener(N,e))
p1,q1=12225084412047579822832549104535271582076346471858362184377821243862043555425127421441678118937351561732381490357686331351719893200877259338844387581595621, 10743671138754808534710934502273854826923182323008489540604345001862855247233202999593381409435636565873506722574731689867584227989434182977521718923135203
phi=(p1-1)*(q1-1)
d=inverse(e,phi)
ink=pow(c_ink,d,N)
#(h+x*r1)/k=s1 (h+x*r2)/(k+ink)=s2
h = int(hashlib.sha256(b'GHCTF-2025').digest().hex(),16)
P.<x,k> = PolynomialRing(Zmod(q))
f1 = (h+x*r1)-s1*k
f2 = (h+x*r2)-s2*(k+ink)
G = Ideal([f1, f2]).groebner_basis()
key = hashlib.sha1(str(x).encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_ECB)
ciphertext = cipher.decrypt(ciphertext)

Sin

1
2
3
4
5
6
7
from Crypto.Util.number import bytes_to_long; 
m = bytes_to_long(b'NSSCTF{test_flag}')
print((2 * sin(m) - 2 * sin(m) * cos(2 * m)).n(1024))
'''
m的值即为flag
0.002127416739298073705574696200593072466561264659902471755875472082922378713642526659977748539883974700909790177123989603377522367935117269828845667662846262538383970611125421928502514023071134249606638896732927126986577684281168953404180429353050907281796771238578083386883803332963268109308622153680934466412
'''

​ 题目很简洁,整理一下输出发现就是$4\times sin^3(m)$,但是由于m远大于$2\pi$,所以不能直接使用$arcsin$,对于下面式子构造格攻击即可

$$
m=arcsin(\frac{t}{4}^{\frac{1}{3}})+2\times k\pi
$$

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sage.all import *
from Crypto.Util.number import *
t=0.002127416739298073705574696200593072466561264659902471755875472082922378713642526659977748539883974700909790177123989603377522367935117269828845667662846262538383970611125421928502514023071134249606638896732927126986577684281168953404180429353050907281796771238578083386883803332963268109308622153680934466412
t = t/4
t = t**(1/3)
m = Matrix(QQ,[[2*pi.n(1024)*2**1024,0,0],
[-1*2**1024,1,0],
[arcsin(t)*2**1024,0,2**512]])
res = m.LLL()
for i in res:
if abs(i[-1]) == 2**512:
print(long_to_bytes(abs(int(i[1]))))
break
#NSSCTF{just_make_a_latter_and_LLL_is_OK_padpad}

river

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import md5
from secret import flag, seed, mask


class 踩踩背:
def __init__(self, n, seed, mask, lfsr=None):
self.state = [int(b) for b in f"{seed:0{n}b}"]
self.mask_bits = [int(b) for b in f"{mask:0{n}b}"]
self.n = n
self.lfsr = lfsr

def update(self):
s = sum([self.state[i] * self.mask_bits[i] for i in range(self.n)]) & 1
self.state = self.state[1:] + [s]

def __call__(self):
if self.lfsr:
if self.lfsr():
self.update()
return self.state[-1]
else:
self.update()
return self.state[-1]


class 奶龙(踩踩背):
def __init__(self, n, seed, mask):
super().__init__(n, seed, mask, lfsr=None)


n = 64
assert seed.bit_length == mask.bit_length == n
lfsr1 = 奶龙(n, seed, mask)
lfsr2 = 踩踩背(n, seed, mask, lfsr1)
print(f"mask = {mask}")
print(f"output = {sum(lfsr2() << (n - 1 - i) for i in range(n))}")
print(f"enc = {AES.new(key=md5(str(seed).encode()).digest(), mode=AES.MODE_ECB).encrypt(pad(flag, 16))}")
# mask = 9494051593829874780
# output = 13799267741689921474
# enc = b'\x03\xd1#\xb9\xaa5\xff3y\xba\xcb\x91`\x9d4p~9r\xf6i\r\xca\x03dW\xdb\x9a\xd2\xa6\xc6\x85\xfa\x19=b\xb2)5>]\x05,\xeb\xa0\x12\xa9\x1e'

这个题目当时参与了试题就没有做了,主要思路就是剪枝爆破得到seed后一一代入求解

具体可以参考一下别人的题解

[2025 GHCTF(校赛) Writeup - suhanhan的博客](https://suhanhan-cpu.github.io/2025/03/19/2025 GHCTF Writeup/)


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !