Misc
Ciallo_Encrypt
Challenge
我的第一个项目终于上线了,上线前我加密了一个小秘密,应该不会被发现吧……
My first project has finally gone live. Before going live, I encrypted a little secret that shouldn’t have been discovered, right
Hint:
admin账号邮箱为qq邮箱。(数字@qq.com)例子:12345@qq.com The email for the admin account is a QQ email (<number>@qq.com) Example: 12345@qq.com
Solution
首先在日志(/logs)发现部分代码在远程仓库,5qC45b+D5Luj56CB5oiR5bey5bCG5YW25pS+6L+b5LqGZm9ya+eahOengeS6uuS7k+W6k+mHjA== base64解码得到 核心代码我已将其放进了fork的私人仓库里

因此直接搜索页面底部的 Yu2ul0ver 发现项目 https://github.com/Yu2ul0ver/Ciallo_Encrypt0r

查看 commit 记录发现后台的账号密码是 email:md5(Ciallo_Encrypt0r)

md5(Ciallo_Encrypt0r) = f42e16b836b22e83fd3818b603c75dc6这点不必多说
账号的获取方式是非预期解法:
首先在公告看到账号是qq邮箱:

接着在之前找到的 commit 记录可以发现是 Uh5ih2 提交的,这个id很眼熟,在两周前刚结束的 2025年“羊城杯”网络安全大赛 的题目 你也是旮旯给木大师? 中见到过,这题是一道内存取证题,且出题人当时开着 QQ
巧的是我还没删除当时的内存镜像,因此打开当时题目的镜像搜索 @qq.com

这里找到的 3517508570@qq.com 就是登录邮箱
预期解法是在 想请教师傅点问题 · Issue #1 · Yu2ul0ver/Ciallo_Encrypt0r 找到 QQ 号 3517508570
登录后台在最早的一条记录找到密文

Ci@110一(2・ω<)⌒★ Cia11o~(∠°ω<)⌒★ Cial10一(2・ω<)⌒★ Cia1lo~(2・ω<)⌒★ Ciallo一(∠・w<)⌒★ Cial10~(∠・ω<)⌒★ Cial10~(∠°ω<)⌒★ Ci@110~(2・ω<)⌒★ Cia110一(∠・ω<)⌒★ Cia11o~(2・ω<)⌒★ Ciall0一(∠・w<)⌒★ Cia110~(2・ω<)⌒★ Ciallo一(2・ω<)⌒★ Cia11o~(2・ω<)⌒★ Ci@110一(∠°ω<)⌒★ Ci@1lo一(∠・w<)⌒★ Cia110一(∠°ω<)⌒★ Cia11o~(2・ω<)⌒★ Cia1lo一(∠・w<)⌒★ Cia1lo一(∠・ω<)⌒★ Ciallo~(∠°ω<)⌒★ Cial10一(∠・ω<)⌒★ Ciallo~(∠・w<)⌒★ Cial10一(2・ω<)⌒★ Cial10~(2・ω<)⌒★ Cia1lo~(2・ω<)⌒★ Ciall0~(∠・ω<)⌒★ Cial10一(∠・w<)⌒★ Ci@110~(∠・ω<)⌒★ Cia110~(2・ω<)⌒★ Cia1l0~(∠・ω<)⌒★ Ciall0~(∠・ω<)⌒★ Ciallo~(∠・w<)⌒★ Cial10一(∠・ω<)⌒★ Ciallo一(∠°ω<)⌒★ Cia1l0~(∠・w<)⌒★ Cia110~(∠°ω<)⌒★ Ci@110~(2・ω<)⌒★ Cial10一(∠・w<)⌒★ Ciall0一(∠・ω<)⌒★ Cia1l0一(∠・ω<)⌒★ Cial10~(∠°ω<)⌒★ Ci@110一(∠・ω<)⌒★ Cia1lo一(∠・ω<)⌒★ Ci@1lo~(∠・w<)⌒★ Cia11o~(∠・ω<)⌒★ Cia1lo一(∠・ω<)⌒★ Ciallo~(2・ω<)⌒★ Ciallo~(2・ω<)⌒★ Cial10~(2・ω<)⌒★ Cia1l0一(∠・ω<)⌒★ Cia1lo一(∠・w<)⌒★ Ciallo~(∠°ω<)⌒★ Cia1l0一(∠・ω<)⌒★ Cia11o~(2・ω<)⌒★ Ci@1lo~(∠・w<)⌒★ Cial1o~(∠°ω<)⌒★ Cia110~(2・ω<)⌒★ Cial10~(∠°ω<)⌒★ Ciall0一(∠°ω<)⌒★ Cial10一(∠°ω<)⌒★ Cia110一(∠・ω<)⌒★ Cia110一(∠・w<)⌒★ Cia1l0一(2・ω<)⌒★ 时间(北京时间):2025-10-17 22:27:17
在前面找到的仓库中发现加密函数是这样导入的:
from encrypt import ciallo_encrypt然而搜遍了整个仓库都没找到这个库,此时想起前面base64解码得到的 核心代码我已将其放进了fork的私人仓库里
根据 ThTsOd 师傅找到的文章 https://mp.weixin.qq.com/s?__biz=MzA3MzI4MjgzMw==&mid=2650927888&idx=3&sn=68fcbe5bc86381f18d171c3aa0fa16f1 了解到可以通过爆破 Commit Hash 访问已删除 fork 存储库的数据,编写脚本进行爆破(修改自 ThTsOd 师傅提供的脚本),考虑到耗时过长,我们平均分成四份进行爆破,最终在最后一份里爆破得到结果,以下是当时的爆破代码:
import requestsfrom multiprocessing import Poolfrom tqdm import tqdmimport timeimport random RETRY_COUNT = 0x10 def process_url(current): retry = 0 while(retry < RETRY_COUNT): try: url = "https://github.com/Yu2ul0ver/Ciallo_Encrypt0r/commit/%04x" % current result = requests.get(url) if result.status_code == 429: print(" %04x" % current, "SLOW DOWN! Rate limited.") time.sleep(random.randint(10, 30)) # 不抛出异常,让循环继续重试 elif result.status_code != 404: print(f" Found: {url} Status: {result.status_code}") # 找到非404状态码,成功,跳出重试循环 break else: # 状态码是404,说明不存在,也算成功处理,跳出重试循环 break except requests.exceptions.RequestException as e: # print("%04x"%current,"Err",e) retry += 1 time.sleep(1) # 网络错误时稍等一下再重试 if retry >= RETRY_COUNT: print(" %04x" % current, "FAILED after multiple retries.") return 0 if __name__ == '__main__': num_processes = 8 # 定义爆破的起始和结束范围 start_range = 0xc000 # 最后四分之一的起始点 end_range = 0x10000 # 结束点 (不包含) # 计算需要处理的总数,用于进度条 total_to_process = end_range - start_range print(f"Starting brute-force from {start_range:04x} to {end_range-1:04x}...") try: # 创建进程池 with Pool(processes=num_processes) as pool: # 创建tqdm进度条 with tqdm(total=total_to_process, desc="Processing Commits") as pbar: # 使用 imap_unordered 可以一边处理一边更新进度条,效率更高 # 将 range() 修改为我们定义的起始和结束范围 for _ in pool.imap_unordered(process_url, range(start_range, end_range)): pbar.update(1) except KeyboardInterrupt: print("\nUser interrupted, shutting down...") finally: print("\nProcess finished.")
爆破得到的结果:https://github.com/Yu2ul0ver/Ciallo_Encrypt0r/commit/e58e
赛后找到了这篇 blog CFOR Exploit - Recovering Deleted and Private Github Commits,进而发现了这个 exp SorceryIE/cfor_exploit: Exploit script for the CFOR vulnerability using Github’s GraphQL API
得到 encrypt.py:
from Crypto.Cipher import AESfrom Crypto.Util.Padding import padimport hashlibimport base64 def ciallo_encrypt(input,ts_str): key = hashlib.md5(ts_str.encode()).digest() cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.encrypt(pad(input.encode(), AES.block_size)) enc_b64 = base64.b64encode(ciphertext).decode() utf8_bytes = enc_b64.encode("utf-8") binary = ''.join(format(b, '08b') for b in utf8_bytes) sum = '' data = [binary[i:i + 8] for i in range(0, len(binary), 8)] # 核心变换 for i in range(0, len(data)): cia = 'Ciallo~(∠・ω<)⌒★' if data[i][0] == '0': cia = cia else: cia = cia[:1] + '1' + cia[2:] if data[i][1] == '0': cia = cia[:2] + '@' + cia[3:] else: cia = cia if data[i][2] == '0': cia = cia elif data[i][2] == '1': cia = cia[:3] + '1' + cia[4:] if data[i][3] == '0': cia = cia elif data[i][3] == '1': cia = cia[:4] + '1' + cia[5:] if data[i][4] == '0': cia = cia[:5] + '0' + cia[6:] elif data[i][4] == '1': cia = cia if data[i][5] == '0': cia = cia elif data[i][5] == '1': cia = cia[:6] + '一' + cia[7:] if data[i][6:8] == '00': cia = cia[:9] + '°' + cia[10:] elif data[i][6:8] == '01': cia = cia elif data[i][6:8] == '10': cia = cia[:8] + '2' + cia[9:] elif data[i][6:8] == '11': cia = cia[:10] + 'w' + cia[11:] sum += cia + ' ' return sum根据加密函数编写解密脚本即可:
from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpad, pad # pad is included for the original functionimport hashlibimport base64import datetime def ciallo_decrypt(encrypted_ciallo, ts_str): """ Decrypts the 'Ciallo' encoded string back to the original text. :param encrypted_ciallo: The output string from ciallo_encrypt. :param ts_str: The same timestamp string used for encryption. :return: The original decrypted string. """ # 步骤 1 & 2: 解析 "Ciallo" 字符串并逆向推导为二进制字符串 cia_parts = encrypted_ciallo.split(' ') binary_string = '' for cia in cia_parts: if not cia: continue # Skip empty strings if there are double spaces bits = [''] * 8 # 逆向推导每一位 # Bit 0 bits[0] = '1' if cia[1] == '1' else '0' # Bit 1 bits[1] = '0' if cia[2] == '@' else '1' # Bit 2 bits[2] = '1' if cia[3] == '1' else '0' # Bit 3 bits[3] = '1' if cia[4] == '1' else '0' # Bit 4 bits[4] = '0' if cia[5] == '0' else '1' # Bit 5 bits[5] = '1' if cia[6] == '一' else '0' # Bits 6, 7 (这是一个互斥的条件) if cia[9] == '°': bits[6:8] = ['0', '0'] elif cia[8] == '2': bits[6:8] = ['1', '0'] elif cia[10] == 'w': bits[6:8] = ['1', '1'] else: # 默认情况 bits[6:8] = ['0', '1'] binary_string += ''.join(bits) # 步骤 3: 二进制 -> 字节 utf8_bytes = bytes([int(binary_string[i:i + 8], 2) for i in range(0, len(binary_string), 8)]) # 步骤 4: UTF-8 解码 -> Base64 字符串 enc_b64 = utf8_bytes.decode('utf-8') # 步骤 5: Base64 解码 -> AES 密文 ciphertext = base64.b64decode(enc_b64) # 步骤 6: AES 解密 key = hashlib.md5(ts_str.encode()).digest() cipher = AES.new(key, AES.MODE_ECB) decrypted_padded_bytes = cipher.decrypt(ciphertext) # 步骤 7: 去除填充 (Unpad) try: original_bytes = unpad(decrypted_padded_bytes, AES.block_size) except ValueError as e: raise ValueError(f"Unpadding failed. The key (from ts_str) or ciphertext may be incorrect. Error: {e}") # 步骤 8: 字节 -> 最终字符串 original_text = original_bytes.decode('utf-8') return original_text if __name__ == "__main__": time_str = "2025-10-17 22:27:17" dt_object = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S") timestamp = str(int(dt_object.timestamp())) encrypted_data = "Ci@110一(2・ω<)⌒★ Cia11o~(∠°ω<)⌒★ Cial10一(2・ω<)⌒★ Cia1lo~(2・ω<)⌒★ Ciallo一(∠・w<)⌒★ Cial10~(∠・ω<)⌒★ Cial10~(∠°ω<)⌒★ Ci@110~(2・ω<)⌒★ Cia110一(∠・ω<)⌒★ Cia11o~(2・ω<)⌒★ Ciall0一(∠・w<)⌒★ Cia110~(2・ω<)⌒★ Ciallo一(2・ω<)⌒★ Cia11o~(2・ω<)⌒★ Ci@110一(∠°ω<)⌒★ Ci@1lo一(∠・w<)⌒★ Cia110一(∠°ω<)⌒★ Cia11o~(2・ω<)⌒★ Cia1lo一(∠・w<)⌒★ Cia1lo一(∠・ω<)⌒★ Ciallo~(∠°ω<)⌒★ Cial10一(∠・ω<)⌒★ Ciallo~(∠・w<)⌒★ Cial10一(2・ω<)⌒★ Cial10~(2・ω<)⌒★ Cia1lo~(2・ω<)⌒★ Ciall0~(∠・ω<)⌒★ Cial10一(∠・w<)⌒★ Ci@110~(∠・ω<)⌒★ Cia110~(2・ω<)⌒★ Cia1l0~(∠・ω<)⌒★ Ciall0~(∠・ω<)⌒★ Ciallo~(∠・w<)⌒★ Cial10一(∠・ω<)⌒★ Ciallo一(∠°ω<)⌒★ Cia1l0~(∠・w<)⌒★ Cia110~(∠°ω<)⌒★ Ci@110~(2・ω<)⌒★ Cial10一(∠・w<)⌒★ Ciall0一(∠・ω<)⌒★ Cia1l0一(∠・ω<)⌒★ Cial10~(∠°ω<)⌒★ Ci@110一(∠・ω<)⌒★ Cia1lo一(∠・ω<)⌒★ Ci@1lo~(∠・w<)⌒★ Cia11o~(∠・ω<)⌒★ Cia1lo一(∠・ω<)⌒★ Ciallo~(2・ω<)⌒★ Ciallo~(2・ω<)⌒★ Cial10~(2・ω<)⌒★ Cia1l0一(∠・ω<)⌒★ Cia1lo一(∠・w<)⌒★ Ciallo~(∠°ω<)⌒★ Cia1l0一(∠・ω<)⌒★ Cia11o~(2・ω<)⌒★ Ci@1lo~(∠・w<)⌒★ Cial1o~(∠°ω<)⌒★ Cia110~(2・ω<)⌒★ Cial10~(∠°ω<)⌒★ Ciall0一(∠°ω<)⌒★ Cial10一(∠°ω<)⌒★ Cia110一(∠・ω<)⌒★ Cia110一(∠・w<)⌒★ Cia1l0一(2・ω<)⌒★ " decrypted_data = ciallo_decrypt(encrypted_data, timestamp) print(decrypted_data)FLAG
flag{9f08699d-1b6c-471e-9ed0-86dbf3ee8074}低空经济网络安全
The Hidden Link
Challenge
我们截获了一个名为“MAV1”的恐怖组织发出的无人机遥测下行数据。
capture.pcap中的数据流量看起来很正常,是吗?
We’ve intercepted a drone’s telemetry downlink from a terrorist group called “MAV1”. The traffic incapture.pcapappears to be normal, or is it?
Solution
根据流量特征,结合题目描述 “MAV1” 不难发现该流量为 MAVLink v1 协议通信
MAVLink v1 消息结构是:
[FE (Start)] [Len] [Seq] [SysID] [CompID] [MsgID] [Payload...] [Checksum (2 bytes)]
直接搜索字符串 “flag”

不难发现flag由 [SysID] [CompID] [MsgID] [Payload]组成,发现此时的序列号 [Seq] 为 00,自然而然想看 01 是什么样的,因此搜索十六进制 fe0401(fe 是固定的起始符,04 是固定的长度,01 就是序列号):

发现 {dr0,符合前面的猜想,因此以此类推继续搜索后面的序列号,也就是继续搜索十六进制 fe0402,fe0403,fe0404…
最后全部连起来就能得到 flag 了
FLAG
flag{dr0n3_fl1ght_c0ntr0ll3r_h4ck3d}