Iran Tech Olympics 2025 quals
Fight as a member@結束バンド
打到总第11,中国第1,似乎是进决赛了,然而不打算去。
Note
去伊朗直接被乱枪打死
后续是进了决赛且路费全包,但是还是没去,那个时候有XCTF决赛线下。
Done
Spy PLC
依旧大模型先吃。
T_Thr_Min = 200
T_Thr_Max = 1200
T_mid = (T_Thr_Min + T_Thr_Max) // 2
H_Thr_Min = 200
H_Thr_Max = 900
H_mid = (H_Thr_Min + H_Thr_Max) // 2
P_Thr_Min = 500
P_Thr_Max = 3000
P_mid = (P_Thr_Min + P_Thr_Max) // 2
G_Thr_Min = 100
G_Thr_Max = 1000
G_mid = (G_Thr_Min + G_Thr_Max) // 2
TEMP_CASES = [180, 180, 1300, 1300, 1300, 180, 900, 1300, 1300, 180, 180, 400, 1300, 180, 400, 400, 900, 180, 1300, 180, 900, 1300, 400, 900, 180, 400, 1300, 180, 900, 400, 1300, 400]
HUM_CASES = [150, 1000, 150, 150, 1000, 400, 150, 150, 1000, 400, 800, 400, 1000, 800, 400, 150, 150, 400, 1000, 150, 400, 1000, 150, 1000, 400, 400, 150, 400, 150, 800, 150, 400]
PRESS_CASES = [1000, 400, 400, 3500, 1000, 3500, 3500, 3500, 1000, 3500, 400, 2000, 1000, 2000, 2000, 2000, 1000, 1000, 1000, 3500, 2000, 1000, 3500, 2000, 2000, 1000, 3500, 3500, 3500, 2000, 3500, 3500]
GAS_CASES = [300, 300, 300, 300, 300, 50, 300, 50, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 50, 300, 300, 50, 300, 300, 300, 300, 300, 300, 300, 50, 50]
def get_zone(raw, thr_min, mid, thr_max):
if raw <= thr_min:
return 0
elif raw <= mid:
return 1
elif raw < thr_max:
return 2
else:
return 3
message = []
for i in range(32):
tr = TEMP_CASES[i]
hr = HUM_CASES[i]
pr = PRESS_CASES[i]
gr = GAS_CASES[i]
Tz = get_zone(tr, T_Thr_Min, T_mid, T_Thr_Max)
Hz = get_zone(hr, H_Thr_Min, H_mid, H_Thr_Max)
Pz = get_zone(pr, P_Thr_Min, P_mid, P_Thr_Max)
Gz = get_zone(gr, G_Thr_Min, G_mid, G_Thr_Max)
StrongHeat = (Tz == 1) or (Tz == 3)
ColdHumid = (Tz >= 2)
HighPress = (Hz == 1) or (Hz == 3)
LowPress = (Hz >= 2)
HighGas = (Pz == 1) or (Pz == 3)
LowGas = (Pz >= 2)
HeatAndPressure = (Gz == 1) or (Gz == 3)
DryAndGas = (Gz >= 2)
# bits: .0 to .7: StrongHeat, ColdHumid, HighPress, LowPress, HighGas, LowGas, HeatAndPressure, DryAndGas
# Assuming .0 LSB, .7 MSB
byte = 0
byte |= (1 if DryAndGas else 0) << 7
byte |= (1 if HeatAndPressure else 0) << 6
byte |= (1 if LowGas else 0) << 5
byte |= (1 if HighGas else 0) << 4
byte |= (1 if LowPress else 0) << 3
byte |= (1 if HighPress else 0) << 2
byte |= (1 if ColdHumid else 0) << 1
byte |= (1 if StrongHeat else 0) << 0
message.append(byte)
# Now,convert to string
try:
msg_str = ''.join(chr(b) for b in message)
except:
msg_str = ''.join([chr(b) if 32 <= b <= 126 else '.' for b in message])
print(msg_str)
print([hex(b) for b in message]) # for debugging
Lost Logic
666还有强起。
很笨的方法,直接硬看,不知道有没有其他比较简单的方法。
y24 = x8 && (!x24)
y26 = x9 || (!x25)
y19 = x3 || (!x19)
y10 = (!((x8 ^ x9) && !(x10 ^ x11))) || (! ((x25 ^ x24) && !(x26 ^ x27)))
y4 = (x8 ^ x9) || !( ((x0 && x1)^(x2 || x3)) && ((x16 && x17) ^ (x18 || x19)) )
y14 = (!((x8 ^ x9) && !(x10 ^ x11))) && ((x16 && x17) ^ (x18 || x19))
y25 = !( (x2 || x3)^ !((x8 ^ x9) && !(x10 ^ x11)) )
y29 = !(x13 ^ !x29 )
y2 = !((x0 && x1)^ (!((x8 ^ x9) && !(x10 ^ x11))))
y28 = !((x2 || x3)^ ((!x14 || x15 )|| (x12 && !x13)))
y7 = !((x0 && x1)^(! ((x25 ^ x24) && !(x26 ^ x27)))
y17 = !((x0 && x1)^((x16 && x17) ^ (x18 || x19)))
y20 = !((x0 && x1)^!(((!x14 || x15 )|| (x12 && !x13))^!(((!x14 || x15 )|| (x12 && !x13))^((x28&&!x29)||(!x30||x31)))))
y16 = !((x0 && x1) ^ !( ((x0 && x1)^(x2 || x3)) && ((x16 && x17) ^ (x18 || x19)) ))
y30 = ! ((x0 && x1) && !((x8 ^ x9) && !(x10 ^ x11))) ^((!x14 || x15 )|| (x12 && !x13)) )
y6 = (!( (x25 ^ x24)&& !(x27 ^ x26)) ^(!((x8 ^ x9) && !(x10 ^ x11))) )^(x12 && !x13)
y8 = !( (x0 && x1)^ ((x28&&!x29)||(!x30||x31))))
y12 = !((x0 && x1) ^ !(((x16 && x17) ^ (x18 || x19))^!(!(x20 && x21) || !(x22 || x23))) )
y31 = x15 || !x31
y5 = !( (!(!(x4 && x5) || !(x7 || x6)) || !(!(x20 && x21) || !(x22 || x23)))&& !(x10 ^ x11))
y3 = !((x0 && x1) ^ ((!x14 || x15 )|| (x12 && !x13)))
y1 = !((x0 && x1) ^ ((x0 && x1)^(x2 || x3)))
y15 = !(((!x14 || x15 ) || (x12 && !x13)) || !(!(x20 && x21) || !(x22 || x23)))
y0 = !y21
y21 = !((((x0 && x1) ^ (x2 || x3)) && !(!(x4 && x5) || !(x6 || x7)))&& (x0 && x1))
y13 = !(!(!(x4 && x5) || !(x6 || x7))) ^ ((x28&&!x29)||(!x30||x31)))
y18 = !((x0 && x1) ^ !(!(x20 && x21) || !(x22 || x23)))
y27 = !((x0 && x1) ^ !(!(x4 && x5) || !(x6 || x7)))&& (x0 && x1))
y23 = !(x7 ^ !x23)
y11 = !((x0 && x1) ^ !(!(x4 && x5) || !(x6 || x7))))
y9 = !(!(x4 && x5) || !(x6 || x7))) && !(!(x20 && x21) || !(x22 || x23))
y22 = !x22 ^ x6
这样解出来的结果是ASIS{L0v!},猜是love,最后是ASIS{L0v3}。
做出来的时候直接战吼了,细细的线给我眼睛都要看坏了。
new tag
十六进制读出来打印,看到有PNG和IEND,应该是字节按序处理。
把前面共同的东西删掉,用后几位排序,重建图片,是一个二维码。
chunks = [
"a0240800a24b310843d16804448",
"a0240800a265915947dcc6c25a8",
"a0240800a25459440d71417c368",
"a0240800a24f28d856f433013a8",
"60240800a24600a8bfabf72e218",
"60240800a257a585284c39662b8",
"a0240800a25c32dad32914fd428",
"60240800a262762ff37890f90f8",
"60240800a25f3bbf1c637781478",
"a0240800a256db3b91059031668",
"a0240800a2495478da7da8c4148",
"a0240800a2410000000da4ae788",
"60240800a25b6bedc52cfa7d1b8",
"60240800a26aa5a1a36b1f93698",
"60240800a26c00000000a4697d8",
"60240800a25365b3e5c78e60638",
"a0240800a26b87c39914295e3c8",
"60240800a263ddaa55e3d8747b8",
"60240800a26d49454e44a96a0f8",
"a0240800a25ec1d4a71add3d128",
"a0240800a24ad2410e038207368",
"60240800a24c77ff4bff146f3f8",
"a0240800a2440000001b47fd7e8",
"a0240800a259924a63123a454e8",
"a0240800a26eae426082d2021e8",
"a0240800a2503f044895ad1c6c8",
"a0240800a2520311120d8d5b3a8",
"a0240800a255bcd37d0f99c24e8",
"a0240800a26880d19f672531188",
"a0240800a251d4100da4569d228",
"60240800a24789000000d49a5f8",
"a0240800a23f89504e4729bc5c8",
"a0240800a2692ffffa034a075c8",
"60240800a2400d0a1a0a4b41178",
"60240800a2608b8a279f3c8e6f8",
"60240800a2584769fad471593f8",
"60240800a267f744b07b6f9c178",
"60240800a261be8182ea167b398",
"a0240800a25a5051c81619fb608",
"60240800a24e74580032a460698",
"a0240800a2488749444189b66c8",
"60240800a2450800000089bd1d8",
"a0240800a24dae5a2bd108fe5c8",
"60240800a242494844527c17598",
"a0240800a2666a631a1c6338728",
"a0240800a2430000001b9bcd3c8",
"a0240800a2642722c1f6be1c368",
"a0240800a25d55dc4e8cceb5288"
]
sorted_chunks = sorted(chunks, key=lambda c: int(c[10:12], 16))
big_hex = ''.join(c[12:20] for c in sorted_chunks)
data = bytes.fromhex(big_hex)
with open('reconstructed.png', 'wb') as f:
f.write(data)
# ASIS{MFU_nTAGs___4UtH!!}
冷知识,做出这道题的时候结束乐队来到了顶峰:

Rider
别逗我笑。
就是把nc一下导进一个wav文件然后看频谱图。
import librosa
import librosa.display
import matplotlib.pyplot as plt
import numpy as np
# 加载音频文件
y, sr = librosa.load('audio.wav') # 如果文件是WAV格式,可以直接这样加载
# 生成频谱图
D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)
# 绘制频谱图
plt.figure(figsize=(10, 4))
librosa.display.specshow(D, sr=sr, x_axis='time', y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Spectrogram')
plt.tight_layout()
plt.savefig('spectrogram.png')
plt.show()

Roman Wire
绝唱,后面就没咋打了。
sal文件,logic2打开。
完了我logic2呢?找不到了随便吧。
然后就是四条线,时钟周期一个,enable一个,剩下两个一个当MISO一个当MISI。
一共有三次数据流,前两次是在说什么roman speak之类的,后面解出来是TLBL{...},提示说roman,再结合ASIS,大概率就是rot13了,拿去cyberchef就行,flag我记得是ASIS{MISO_MISI_H4LF},大小写记不太清了。
Review
DGA
做是做出来了,然而是队友的功劳,看writeup好像挺简单的。
0, xclc.uylrslogllhlijlfnl.com
1, xcic.uysrseogalhhijsfne.com
2, xele.udlrblowllolixlfql.com
3, xeie.udsrbeowalohixsfqe.com
4, xglg.uhlrjlonllvlimlftl.com
5, xgig.uhsrjeonalvhimsfte.com
6, xili.ullrrloelldliblfwl.com
7, xiii.ulsrreoealdhibsfwe.com
8, xklk.uplraloullkliplfal.com
9, xkik.upsraeoualkhipsfae.com
10, xmlm.utlrilolllrlielfdl.com
11, xmim.utsrieolalrhiesfde.com
12, xolo.uxlrqlocllylislfgl.com
13, xoio.uxsrqeocalyhissfge.com
14, xqlq.uclrylosllglihlfjl.com
15, xqiq.ucsryeosalghihsfje.com
16, xsls.uglrhlojllnlivlfml.com
17, xsis.ugsrheojalnhivsfme.com
18, xulu.uklrploalluliklfpl.com
19, xuiu.uksrpeoaaluhiksfpe.com
20, xwlw.uolrxloqllcliylfsl.com
21, xwiw.uosrxeoqalchiysfse.com
...
56, aolo.wxltqlqclnylkslhgl.com
57, aoio.wxstqeqcanyhksshge.com
58, aqlq.wcltylqslnglkhlhjl.com
59, aqiq.wcstyeqsanghkhshje.com
别样的,找规律大赛。
Flag:ASIS{wgstheqjannhkvshme}
Safe Card
没做,也没咋看,浅复盘一下。
后面有空再看吧,最近感觉有点忙,课内还没复习)。
Reference
https://jia.je/ctf-writeups/2025-09-26-iran-tech-olympics-ctf-2025/
GGWP,这位全栈佬真的厉害。