2026ZeroG零重力CTF-misc
题目
Misc_01.StarTrail / 星轨校验
信息

README.txt:

1 2 3 4 5 6 7 8 9 10 11
| ZeroG-CTF StarTrail
Files: - ground_control.log : launch day ground control log - telemetry.bin : encrypted telemetry frame
Message from Fen: "轨迹会告诉你顺序,队伍会告诉你钥匙。"
Flag format: flag{}
|
做题
拿到附件,先分析log日志,里面有疑似base64编码,还有顺序

按照顺序把所有编码拼接起来,解码后得到zlib压缩数据


逐个分析
“nonce”:”5a45524f472d3031”
解码得到

cipher: xorstream-sha256-ctr
加密方式:
xorstream → 流加密,按字节异或
sha256 → 用于派生密钥流
ctr → 计数器模式,每个块的密钥流由 SHA256( key + counter ) 生成
kdf: sha256(‘Pwnstars::’ + ‘-‘.join(crew) + ‘::’ + domain)
密钥派生函数:
先计算 ‘Pwnstars::N1-A-Hugo-Gnaw-Fen::www.pwnstars.online‘
再取 SHA256,得到加密用的密钥
1 2 3 4 5
| - crew : ["N1","A","Hugo","Gnaw","Fen"] - domain : www.pwnstars.online - nonce : 5a45524f472d3031 → ZEROG-01 - cipher : xorstream-sha256-ctr - KDF : sha256('Pwnstars::' + '-'.join(crew) + '::' + domain)
|
在附件的README中有提示

同时题目提示pwnstar五位成员和官网作为派生秘钥
1 2
| KDF = sha256("Pwnstars::N1-A-Hugo-Gnaw-Fen::www.pwnstars.online") = c0ffb03aedafa1ca8de33ed820f5f22d1b02e88ac3bf7b06034e3ee046e33394
|
解密telemetry.bin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import hashlib
dk = hashlib.sha256( b"Pwnstars::N1-A-Hugo-Gnaw-Fen::www.pwnstars.online" ).digest()
nonce = bytes.fromhex("5a45524f472d3031")
with open("telemetry.bin", "rb") as f: ciphertext = f.read()
plaintext = bytearray() counter = 0 while len(plaintext) < len(ciphertext): keystream = hashlib.sha256(dk + nonce + counter.to_bytes(4, 'big')).digest() for i, k in enumerate(keystream): if counter * 32 + i >= len(ciphertext): break plaintext.append(ciphertext[counter * 32 + i] ^ k) counter += 1
print(bytes(plaintext).decode(errors='replace'))
|

得到flag编码,解密
base64->rot13

1
| flag{ZeroG_startrail_from_pwnstars}
|
Misc_02.Moonlight Radio / 月光电台
信息

README.txt:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ZeroG-CTF Moonlight Radio
Recovered files:
- radio.wav - telemetry.dat
Fen's note: "The moon does not speak Morse tonight."
A's note: key = sha256("ZeroG::" + radio_password + "::www.pwnstars.online")
Gnaw's note: "Numbers may become characters again."
Flag format: flag{...} or Pwnsatrs{...}
|
做题
提示wav是电话按键音,直接DTMF2NUM播音号识别

1
| 108117110097114045049055048049
|
观察发现可以分为每三个一组
1
| 108 117 110 097 114 045 049 055 048 049
|
十进制解码得到
得到秘钥
1 2 3
| sha256:ZeroG::lunar-1701::www.pwnstars.online
658446226393e73367bfd2b9f83ff6238b1c6b6adcbbcf349c936337f2de9cd7
|
然后打开telemetry.dat发现有特殊字符

推测nonce:ZGRMOON2
跟上道题目一样xorstream-sha256-ctr解密得到flag的编码
1
| FINAL=c3ludHtNcmViVF96YmJheXZ0dWdfZW5xdmJfcWd6c30=
|
再Base64+rot13解密得到flag

1
| flag{ZeroG_moonlight_radio_dtmf}
|
Misc_03.Blackbox Telemetry / 黑匣子遥测
信息

README.txt:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ZeroG-CTF Blackbox Telemetry
Recovered files:
- blackbox.db - events.log - fragment.bin
Fen: "按正确的时间看,碎片会重新排列;用任务名开箱。"
Notes: Some timestamps inside the database are unreliable. The event stream may know which frames were accepted.
Flag format: flag{...} or Pwnsatrs{...}
|
做题
查看db文件

1 2 3 4 5 6
| 6 ZG-FRAME-4B 2026-04-27T00:00:06Z base64+zlib eJxjYMADDofGri/a7fJG3ch70dubO+M27GL9YR7YsChow8XjTVP0AQjREJU= 1 ZG-FRAME-2E 2026-04-27T00:00:12Z base64+zlib eJzrkdy2819rOp+grtblGSfNlHT51K/fX+yzRsRV+wdzgDcziwgDAwMHw6lZS2KWn2p4oQPkqQMxDxCnAgCusxLy 5 ZG-FRAME-9F 2026-04-27T00:00:15Z base64+zlib eJwBPADD/5IPRUopVdNFriQMPjB8eDMvIbu7Erx82pxvaVTcB3AT8d4GOiIiQw5OF2myC02fBDl3Hw4Iqnr/AAAAOStGRk= 2 ZG-FRAME-8D 2026-04-27T00:00:18Z base64+zlib eJwBNwDI/25jX2ZsYWcuYmluAScA2P/qJX2weJAnY8ArMeNJnJCn7o93mcE0ixIbT1SmtHIUOOM/eaVmrj+f8xii 3 ZG-FRAME-1C 2026-04-27T00:00:03Z base64+zlib eJwBPADD/4GCEGLqUjkXtzklOUf2NUf+PXdqO+HF0nvW82fvWGWDLwO5vpFf/GYnM+uCnxIZYEsNlqrxKwWLpAavMk9pG20= 4 ZG-FRAME-7A 2026-04-27T00:00:09Z base64+zlib eJwL8GZmEWFgYOBgODVrSUwn+wVz4E8b0aQCAODn+Iq15JRUnqBEP/ww48reX/uVYLc8xi5Gzz0OG4CADKHBC3
|
提示说根据log文件能得到正确顺序

根据ZG-FRAME-7A~~来一一对应ZG-FRAME-7A 2026-04-27T00:00:09Z base64+zlib eJwL8GZmEWFgYOBgODVrSUwn+wVz4E8b0aQCAODn+Iq15JRUnqBEP/ww48reX/uVYLc8xi5Gzz0OG4CADKHBC3
然后依次base64+zlib inflate解码出hex形式拼接得到zip文件的16进制表示
1
| 504b0304140000000800ca9aa45c89039ba8e70000004b010000080000004e4f54452e7478746590314fc3400c8577ff0aab13089a010986482c08d181821062ea523917b739253947f63547fe3d776a3be1c5d27bd6f367ef5865832f03b9be915ffc662733eb829f1219604b0d96aaf12b058ba406af32920f454a2955d345ae240c3e307c78332f21bbbb12bc7ef36a71bda553701dc04fc77818e888890c39385da6c82d367c10e5dc7c3822a9ebfccc6d05f0ce0bb6ac7ea698336b8082d267f119ada387c7a79bf1bc6e1f6864bc0000000000000000000000000000000000000000000000000000000000000000c3555daf72bb44ec27324ba2edd9b95eb0ba05f8375180a252b0d1c782942f8c19b6b9fe85670e112d2ad398c936222d0e27d7dfa34cac14452bf803504b0304140000000800ca9aa45ca7ca80e82c000000270000000c000000656e635f666c61672e62696e012700d8ffea257db078902763c02b31e3499c90a7ee8f7799c1348b121b4f54a6b4721438e33f79a566ae3f
|
根据文件头可知有NOTE.txt和enc_flag.bin文件,但是没有源目录数据区,所以用winRAR修复
得到两个文件
NOTE.txt:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ZeroG Blackbox Recovery Note
Lab : Pwnstars Domain : www.pwnstars.online Mission : ZeroG-First-Launch
The flag was encrypted before being archived.
Key derivation:
key = sha256(mission_name + "::" + archive_password + "::Pwnstars")
archive_password was transmitted in the blackbox event stream.
Good luck, operator.
|
知道解密方式后就可以得到flag了
archive_password在log文件中

mission_name

1 2 3 4 5 6
| mission_name = "ZeroG-First-Launch" archive_password = "timeline-0427"
key_material = "ZeroG-First-Launch::timeline-0427::Pwnstars" key = sha256(key_material)
|
最后循环异或enc_flag.bin
1 2 3 4 5 6 7 8 9 10 11 12
| import hashlib
mission_name = "ZeroG-First-Launch" archive_password = "timeline-0427"
key_material = mission_name + "::" + archive_password + "::Pwnstars" key = hashlib.sha256(key_material.encode()).digest() with open('enc_flag.bin', 'rb') as f: enc_data = f.read()
flag = bytes(enc_data[i] ^ key[i % 32] for i in range(len(enc_data))) print(flag.decode())
|
得到flag

1
| flag{ZeroG_blackbox_timeline_recovered}
|