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的私人仓库里

QiangwangMimicQuals2025-1

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

QiangwangMimicQuals2025-2

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

QiangwangMimicQuals2025-3

md5(Ciallo_Encrypt0r) = f42e16b836b22e83fd3818b603c75dc6这点不必多说

账号的获取方式是非预期解法:

首先在公告看到账号是qq邮箱:

QiangwangMimicQuals2025-4

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

巧的是我还没删除当时的内存镜像,因此打开当时题目的镜像搜索 @qq.com

QiangwangMimicQuals2025-5

这里找到的 3517508570@qq.com 就是登录邮箱

预期解法是在 想请教师傅点问题 · Issue #1 · Yu2ul0ver/Ciallo_Encrypt0r 找到 QQ 号 3517508570

登录后台在最早的一条记录找到密文

QiangwangMimicQuals2025-6

sql
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

在前面找到的仓库中发现加密函数是这样导入的:

python
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 师傅提供的脚本),考虑到耗时过长,我们平均分成四份进行爆破,最终在最后一份里爆破得到结果,以下是当时的爆破代码:

python
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.")

QiangwangMimicQuals2025-7

爆破得到的结果: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:

python
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

根据加密函数编写解密脚本即可:

python
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

text
flag{9f08699d-1b6c-471e-9ed0-86dbf3ee8074}

低空经济网络安全

Challenge

我们截获了一个名为“MAV1”的恐怖组织发出的无人机遥测下行数据。capture.pcap中的数据流量看起来很正常,是吗?
We’ve intercepted a drone’s telemetry downlink from a terrorist group called “MAV1”. The traffic in capture.pcap appears to be normal, or is it?

Solution

根据流量特征,结合题目描述 “MAV1” 不难发现该流量为 MAVLink v1 协议通信

MAVLink v1 消息结构是:

[FE (Start)] [Len] [Seq] [SysID] [CompID] [MsgID] [Payload...] [Checksum (2 bytes)]

直接搜索字符串 “flag”

QiangwangMimicQuals2025-8

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

QiangwangMimicQuals2025-9

发现 {dr0,符合前面的猜想,因此以此类推继续搜索后面的序列号,也就是继续搜索十六进制 fe0402fe0403fe0404

最后全部连起来就能得到 flag 了

FLAG

text
flag{dr0n3_fl1ght_c0ntr0ll3r_h4ck3d}