2025WMCTF部分题目

QAQ

质量不是一般的高,虽然爆零了但是感觉打得很爽。

SplitMaster

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
from Crypto.Util.number import *
import socketserver
import socket

def split_master(B_decimal, segment_bits):
if len(segment_bits) < 3:
raise ValueError("no")

if sum(segment_bits) != 512:
raise ValueError("no")

n = len(segment_bits)
found_combination = None
for k in range(n,1,-1):
from itertools import combinations
for indices in combinations(range(n), k):
if sum(segment_bits[i] for i in indices) > 30:
continue

valid = True
for i in range(len(indices)):
for j in range(i+1, len(indices)):
if abs(indices[i] - indices[j]) <= 1:
valid = False
break
if not valid:
break
if not valid:
continue

if 0 in indices and (n-1) in indices:
continue
if any(segment_bits[i]>=25 for i in indices):
continue
found_combination = indices
break

if found_combination is not None:
break

if found_combination is None:
raise ValueError("no")

binary_str = bin(B_decimal)[2:].zfill(512)
if len(binary_str) > 512:
raise ValueError("no")

segments_binary = []
start = 0
for bits in segment_bits:
end = start + bits
segments_binary.append(binary_str[start:end])
start = end

segments_decimal = [int(segment, 2) for segment in segments_binary]

return [segments_decimal[i] for i in found_combination]

class Task(socketserver.BaseRequestHandler):
def _recvall(self):
BUFF_SIZE = 2048
data = b''
while True:
part = self.request.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
break
return data.strip()

def send(self, msg, newline=True):
try:
if newline:
msg += b'\n'
self.request.sendall(msg)
except:
pass

def recv(self, prompt=b'[-] '):
self.send(prompt, newline=False)
return self._recvall()

def handle(self):
# 设置socket超时而不是使用signal.alarm
self.request.settimeout(90) # 90秒超时

try:
flag = b'WMCTF{test}'
self.send(b"Welcome to WMCTF2025")
key = getPrime(512)
print(key)
q = getPrime(512)
self.send(b"q:"+str(q).encode())
for i in range(20):
a = getPrime(512)
b = a * key % q
gift = split_master(b, list(map(int, self.recv(b"> ").split())))
self.send(b"a:"+str(a).encode())
self.send(b"gift:"+str(gift).encode())
x = self.recv(b"the key to the flag is: ").decode()
if x == str(key):
self.send(flag)
except socket.timeout:
self.send(b"Time's up!")
finally:
self.request.close() # 确保连接被关闭


class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass

if __name__ == "__main__":

HOST, PORT = '0.0.0.0', 10003

print("HOST:POST " + HOST + ":" + str(PORT))
server = ThreadedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()

先来分析一下这个看起来很长的split函数,它会根据用户输入的segment_bits来对$b_i$进行部分比特的泄露。大概的泄露规则如下

1.至少分成3段

2.segment_bits总和需为512

3.泄露的部分每一部分不超过25bit且泄露总和不超过30bit

4.不能同时泄露头尾

这里我最开始的思路是输入[480.20,2,10]经过本地测试发现输出的是20和10的部分,也就是泄露了30bit,我想的是通过爆破中间的未知两位来实现已知低位打HNP。搓好脚本之后发现4$^{20}$爆破空间在我电脑上要跑6w个小时,直接给我看笑了。然后就一直尬住也没想到好的解决方法。

后来赛后请教了Dexterjie师傅了解到其实不一定非要使用20对32bits,前十次取30bits并爆破1bit,后十次取24bits,也可以拿到足够的数据打HNP,这样的话爆破只需要2$^{10}$,肯定可以在规定时间爆完。

这样我们就可以造以下格。

62d1b94c04d4059d28681385df3d44f2.png

最后规约出key,但是题目并未给出key<q,所以我们需要验证key=key+kq,然后验证位数是否为512和是否为素数。

发现只有这一个约束条件不够,所以我们把求得的key代入split函数,检验得到的gift一不一样。

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/env python3
from pwn import remote
import itertools
from sage.all import Matrix, QQ
from tqdm import tqdm
from Crypto.Util.number import isPrime

HOST = "62.234.144.69"
PORT = 31080

# 与题目服务的 split_master 逻辑相同,用于本地验证
def split_master(B_decimal, segment_bits):
if len(segment_bits) < 3:
raise ValueError("no")
if sum(segment_bits) != 512:
raise ValueError("no")
n = len(segment_bits)
found_combination = None
from itertools import combinations
for k in range(n,1,-1):
for indices in combinations(range(n), k):
if sum(segment_bits[i] for i in indices) > 30:
continue
valid = True
for i in range(len(indices)):
for j in range(i+1, len(indices)):
if abs(indices[i] - indices[j]) <= 1:
valid = False
break
if not valid:
break
if not valid:
continue
if 0 in indices and (n-1) in indices:
continue
if any(segment_bits[i]>=25 for i in indices):
continue
found_combination = indices
break
if found_combination is not None:
break
if found_combination is None:
raise ValueError("no")
binary_str = bin(B_decimal)[2:].zfill(512)
if len(binary_str) > 512:
raise ValueError("no")
segments_binary = []
start = 0
for bits in segment_bits:
end = start + bits
segments_binary.append(binary_str[start:end])
start = end
segments_decimal = [int(segment, 2) for segment in segments_binary]
return [segments_decimal[i] for i in found_combination]

# 与服务交互,收集 20 组数据(默认)
def collect(seg_input=b"481 6 1 24", rounds=20):
io = remote(HOST, PORT)
_ = io.recvline() # welcome
q_line = io.recvline().decode().strip()
qq = int(q_line.split(":",1)[1].strip())
A = []
gifts = []
for _ in range(rounds):
io.recvuntil(b"> ")
io.sendline(seg_input)
a_line = io.recvline().decode().strip()
gift_line = io.recvline().decode().strip()
a = int(a_line.split(":",1)[1].strip())
gift = eval(gift_line.split(":",1)[1].strip()) # expect [seg6, seg24]
A.append(a)
gifts.append(gift)
return io, qq, A, gifts

# 你提供的 recover_B(原样,改 n=20 时可用)
def recover_B(N, A, B, n=20):
try:
inv1 = pow(2**31, -1, N)
inv2 = pow(2**24, -1, N)
except Exception as e:
return None

scale = 2**7 # 前十列的放大因子
M1 = Matrix(QQ, n+2, n+2)

for i in range(10):
# 对角线
M1[i, i] = N * scale # 乘上2^7
M1[i+10, i+10] = N # 后10列保持不变

# 最后两行
M1[-2, i] = A[i] * inv1 * scale
M1[-1, i] = B[i] * inv1 * scale
M1[-2, i+10] = A[i+10] * inv2
M1[-1, i+10] = B[i+10] * inv2

t = QQ(2**488 / N)
K = 2**488
M1[-2, -2] = t
M1[-1, -1] = K
try:
L = M1.LLL()
except Exception:
return None
for row in L:
try:
if abs(row[-1]) == K:
x2 = int(abs(row[-2]) // t)
return x2
except Exception:
continue
return None

if __name__ == "__main__":
# collect data from remote
io, qq, A, gifts = collect()
# verify we collected 20 groups
if len(A) < 20 or len(gifts) < 20:
print("[!] collected less than 20 groups")
io.close()
raise SystemExit(1)


seg6_list = [g[0] for g in gifts]
seg24_list = [g[1] for g in gifts]


def make_candidate31(seg6, seg24, midbit):
return (seg6 << 25) | (midbit << 24) | seg24

N_groups = 20

total = 1 << 10
print(f"[*] Trying {total} combinations for the 10 unknown middle bits (2^10 = {total})")
found_key = None

for mask in tqdm(range(total), desc="Brute forcing 10 mid-bits", ncols=80):
B_candidates = []
for i in range(10):
midbit = (mask >> i) & 1
val = make_candidate31(seg6_list[i], seg24_list[i], midbit)
B_candidates.append(val)
# last 10 groups: only seg24 (24-bit)
for i in range(10, 20):
B_candidates.append(seg24_list[i])

# call recover_B with n=20
x2 = recover_B(qq, A[:20], B_candidates, n=20)
if x2 is None:
continue

# try x2 + k*q to get 512-bit prime key
# try a few small k values (0..7) — 调整上限如有需要
for k in range(8):
candidate_key = x2 + k * qq
if candidate_key.bit_length() != 512:
continue
if not isPrime(candidate_key):
continue

# final verification: compute b = a*key % q and run split_master to get returned segments
ok = True
for idx in range(20):
a_val = A[idx]
b_calc = a_val * candidate_key % qq
try:
# split into returned segments using same segment_bits
segs = split_master(b_calc, [481,6,1,24]) # returns [seg6, seg24]
except Exception:
ok = False
break

seg6_calc, seg24_calc = segs[0], segs[1]

# for first 10 groups, both seg6 & seg24 must match
if idx < 10:
if seg6_calc != seg6_list[idx] or seg24_calc != seg24_list[idx]:
ok = False
break
else:
# last 10 groups only require seg24 match (we only used seg24)
if seg24_calc != seg24_list[idx]:
ok = False
break

if ok:
found_key = candidate_key
print("[*] Found key:", found_key)
break

if found_key is not None:
break

if found_key is not None:
# submit key and get flag
try:
io.recvuntil(b"the key to the flag is: ")
except Exception:
pass
io.sendline(str(found_key).encode())
try:
flag = io.recvall(timeout=10)
print("[*] Flag:", flag.decode(errors='ignore'))
except Exception as e:
print("[!] Failed to receive flag:", e)
else:
print("[!] No key found after trying all 2^10 combos")

io.close()

Ishowsplit

看起来是上一题的升级版。

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
from Crypto.Util.number import *

from hashlib import md5

def split_master(B_decimal, segment_bits):
if len(segment_bits) < 3:
raise ValueError("no")

if sum(segment_bits) != 512:
raise ValueError("no")

n = len(segment_bits)
found_combination = None
for k in range(n,1,-1):
from itertools import combinations
for indices in combinations(range(n), k):
if sum(segment_bits[i] for i in indices) > 30:
continue

valid = True
for i in range(len(indices)):
for j in range(i+1, len(indices)):
if abs(indices[i] - indices[j]) <= 1:
valid = False
break
if not valid:
break
if not valid:
continue

if 0 in indices and (n-1) in indices:
continue

if any(segment_bits[i]>=25 for i in indices):
continue

found_combination = indices
break

if found_combination is not None:
break

if found_combination is None:
raise ValueError("no")

binary_str = bin(B_decimal)[2:].zfill(512)
if len(binary_str) > 512:
raise ValueError("no")

segments_binary = []
start = 0
for bits in segment_bits:
end = start + bits
segments_binary.append(binary_str[start:end])
start = end

segments_decimal = [int(segment, 2) for segment in segments_binary]

return [segments_decimal[i] for i in found_combination]

p=getPrime(512)
key=randrange(1,p)
gift=split_master(key,[182,20,200,10,100])

flag="WMCTF{"+md5(str(key).encode()).hexdigest()+"}"

A=[]
B=[]
R=[]
M=[64, 128, 256]
for i in range(10):
a=randrange(1,p)
A.append(a)
s=0
rr=[]
for j in range(3):
r=randrange(1,p)
k=randrange(1,2^M[j])
s+=r*k%p
rr.append(r)
R.append(rr)
B.append((a*key+s)%p)

print(f"p={p}")
print(f"A={A}")
print(f"B={B}")
print(f"R={R}")
print(f"gift={gift}")

# p=7159749741429322755131240146118071759513715820993285825839372472474407666017557572129271731613358007058734527689330441569348431807180112353088919340436347
# A=[6099484397780065687822398499925956660167123649401003086450429553387635127108172239381711192830201627868739985455478039493705929385423995630816089813826652, 89722133899180146464100891510163842168405798298452823483063318133678868709451123173510945333104825653032959450315649783796310151123989041792721415722484, 6852373640840439902263069245352036561377612959731529442657261290464193432386684112062098796260781796040393491512167644773780010990517598995340849012156752, 905572447043025859658716967933649489546055535993585428025481014254600350130412634867260097168151503405254514553766823286383565893018173373564554336056693, 4619203611445979770240267215110891760841947947738244443460262136009336416179092898214434705455167036725713675890810610700106198084790551650093160195275702, 3069238570521651083095142261917310941562970802732278543454193591033820858125892411024150728211080137659037098102856608536404922668196774692048992081648551, 2072290141156875572599510174743549619222963372822414855220494223803795956351998912692844952044029330311821434784805742487737048376693843981146845532811418, 4118630348835299974900731058046317343585580076694977535798202938226187304960635726712385771299418425062431822315135332845240129948946520330840063372710812, 5494897767679702083905262716715474988716617413297387287978901109122932238365802120197755794026314539468927208231771384572456104728695543349076171476106692, 5242904898120293050194404817363504806316489104114848367489798169501727630568797399722291273601688449269113866312614390323808528035310223034076071688527093]
# B=[2225881589521184173065274225912017053067069649965432632423136697355986574624766674385247816885344883664964124555071710934403200036986056742665338710501199, 6648320309177586281256682217887823816445310746588184022611740437856487828841524257412678979289757558617628247133981523476180196883192489542467687175493866, 1368643343865975119970439821808519233195197253476459608302319415282308653927922072355125801570423137124789823080768464141261145798676583005521122600184021, 4757972435898893943050634851470164873875968802674701938325764517462502213399385660925464222079964582272246709905040569303916540860368398046797311634207119, 3491493600654315810383896038657359020801782746573223354229968950004336723011925225140197224620617196713846979463196337854602425927920594332253840267066287, 5812910599586334226756848369194045700041602130860303332014243049211324518168755764068940745746358136899818045819591702608213306673108319488577075164061902, 5286480909173195769260613883921498519696474112632193807758953252056528658290642833837928174403014629586637399671995087032742056865191425515352773317139947, 5990084243427240363832897819696345106790895509165460228212982648304258173654753003702718799678678397988011691031933597893892134441380835867546094903910961, 4657207884013678545225122398830559190905549085156416548267023047411835416409008088567237198440378272503925106822333599825940007094045484876222921444207859, 4337342732185682149995046884384392099143035562597116814453255157188836395606408153375003404584342070812600880379801960599561396105172991519802949392096959]
# R=[[5437342344417526961447037102420381576182242917002824736531383988448576795401348337669838142255293777506485879415403692176729738237621696959342977281065396, 118207420592466754679295752653401966487038067694232692642557897325071960359432451312279399266748403346434433562012104799520263992802423378954266828804131, 7096197247368703535624516822532525663824053041765748785834200261711792994365801659587654582314309510718479268191354720373426303224114439156530646421389487], [2048337393654342724878996996835462937898615364747266814141518152181142819685131064444737246705773125581643549980403948577060721913752092536246991248488798, 4032915799560554709639638304258383923016302450145031749214368211030759499850002173909365184297695024196808815382820437454460747458506629192382539028901160, 4232871593307892145191764881832084004817150942430800176840205014538991625057835110518374643680800074286367340775128361231313202429848296012489919924979266], [2553386960937591100090865827289064489951927968995578370310341693487902315425241611375327831218812187817696136311487587016400127802098102736127873401818913, 5823670592022005462447151598758745521401539301567430301030393638437006554138239970651467866637510447017480176716140423825242961163243963505222273874838613, 5774084025457416187346689800934856256827564486310334723289121828349816917057440353481035763960569668502226684658505097070668779989218703360744064625684871], [7046572309712543369992700561219588655579833588142366002445356785295649361641707579997338719645976088701747288198545152391667165237528203051491380206476100, 3086411743880543388057778894558929465483005347509988210044202022882123069984407733408405454699097438747656268706316626496116194702187510362704226701362680, 3824920704333716278162115284149190730865231866245771161577917822721893196275454552093227654236325852790896150665403865418453038676291626669950621607864235], [6760618245898750953819376194151632666239738960636983938459565687242233704064651489179662554150497876748082273757875418325340597777457003363001123655534527, 4343397856990866281857795612386217816895468511981051731094731266904611655944226707430233814074001565540730803579725372227013575628572646210241319814714476, 7126011694277861782468567565693664008866580617261568379233477615778205440306291716911745585348838954153812439519950532595352182974188212855768277246923419], [2953923187831173071776186551032070259914505167253362070557203382982396081492190509064486405525934004929672640497574202986456906310807830423216504796476927, 1345669062441589204474792668690191778211085836386374497142860395037294956095491918997725669029150439803057766748915079527876461488078624845798404978671362, 6975771805724338057550497476188442963101655123133481610941634405619501180059972427132933469246923537067484663454770321483398210207386857564548399452193784], [613595431074082703746546668753146025816666490902587228756275516028887275638378432031652661608234989583525385999489844534573227312653028029927921272453512, 4123105910746992373978630252040464408125059102592223292674058753777019714209155710896007043366563874898777820917476610249503568925138753611543704471760303, 989555937865263901715230959831678853496396994080634488540356871394920097958240471950255879798889334377502384605373832255522749450694078070958912976709214], [5535837862012327812765107969360670846116191751375838521859633639946929019321243840486291205554534270213183060274542457089991919161794065714297577071253492, 2217022003356883793255174224244083193207752640022692463419248286782028965975927441224003452590436406144452759467937867082143500151445862797223973537424326, 4169012965258555766659454909927125486691566973583123755296718808398491419295598806865374588756721952326039465076613735671092746736038930739233766233317746], [2460973952017922432127354291027776825014962112677929880673566789249322210046943034298740329681812324994822480424953136978723367413424675867422535483400994, 6525560390886316331291464628669910149125483607726248566635546904534294626062586662528213108239725365594595677881216065984903337839878980240035564934046552, 5267455846476800778837810675968652912499902190539148131941469838044707979168868352552242860860469415418625601143261920057498327632704722410629571902757207], [2836101201606404086963674515196242099981794676200180367684860793033271859982764426617335526949325589855895823167197136257361757221153866636871210172909499, 2905746791428321890819637325300810834453495282574466482434863215508033662223795925912808993026408194967015679126821486382496994000126841698935066307107806, 6179734411062461009407000122687973511105948755170244204181450729410647901986426188970783453277592832386486628788023466433324078852891442496360969724173650]]
# gift=[841309, 840]

再研究研究