D u know Pyc?官方Write Up

发布于 2025-12-07 1363 次阅读


这题的灵感来自C3ngH师傅的“你知道pyc的文件头可以推出来吗”,于是就有了这题

前后的部分估计不是很难,那就主要来讲讲构建pyc打明文攻击这一步

关于PYC

https://ctf-wiki.org/misc/other/pyc/

可以参考这个文档

ok回头看我给的压缩包,给了信息

python 3.12.12
Bit Field为00 00 00 00
hash 字段非置空

Magic Number

那已知python版本号就可以推出前四字节

conda create -n ctf-test python=3.12.12
conda activate ctf-test
python
>>>import importlib.util
>>>importlib.util.MAGIC_NUMBER
b'xcbrrn'importlib.util.MAGIC_NUMBER.hex()
'cb0d0d0a'

可以得到前四字节"cb0d0d0a"

Bit Field

在python 3.12.12中,Bit Field只有两种状态

00 00 00 00 ------->后面跟着的是Timestamp和Source Size,然后16位后hash

值置空(就是全0,会被非预期,所以我手动将其改掉了,但是不会影响pyc的正常功能,对此感兴趣师傅可以自己去构建一下)

01 00 00 00------->后面跟着的是hash值

至于Bit Field为00 00 00 00,决定了8-12位是TIMESTAMP or HASH

Timestamp和Source Size

有一个点在ctf-wiki里没写清楚,他说的是.pyc产生的时间,事实上是py文件最后修改的时间,大小也是指py文件的大小,我在压缩包里给了flag.py,可以看到大小,不过只能看到 ZIP entry 的 mtime,而不能看到文件产生的时间。

所以只能获取Source Size

flag.py原始大小是1411字节,转为4字节的16进制表示为:83050000

明文攻击

综上已知CB 0D 0D 0A 00 00 00 00 ?? ?? ?? ?? 83 05 00 00

8+4字节,且偏移量为12。可以进行明文攻击

后面分析flag.py就可以了

exp

from base64 import b64decode
def rc4_bytes(key: bytes, data: bytes) -> bytes:
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) & 0xFF
        S[i], S[j] = S[j], S[i]
    i = j = 0
    out = bytearray()
    for byte in data:
        i = (i + 1) & 0xFF
        j = (j + S[i]) & 0xFF
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) & 0xFF]
        out.append(byte ^ K)
    return bytes(out)
CIPHER_B64 = "adJts2ab4DvAn9Zu60bNyGrOAWj72B1/kg==" 
def step2_unxor(data: bytes, k: int = 0x37) -> bytes:
    return bytes(b ^ k for b in data)
def step1_unreverse(s: str) -> str:
    return s[::-1]
def solve():
    cipher = b64decode(CIPHER_B64)
    with open("S3ct3t", "rb") as f:
        key = f.read().strip()
    inter = rc4_bytes(key, cipher)
    b_plain_reversed = step2_unxor(inter, 0x37)
    s_plain_reversed = b_plain_reversed.decode("utf-8", errors="ignore")
    flag = step1_unreverse(s_plain_reversed)
    print("flag:", flag)
if __name__ == "__main__":
    solve()

S3ct3t文件在图片尾部的压缩包中,解压获得

 

S3ct3t:your-secret-key-here

随后获得flag:VNCTF{W3lc0me_T0_V&N2025}

如果本题有哪里有问题,还请师傅们多多包涵,欢迎找我反馈

  • reward_image1
Being Better
最后更新于 2025-12-07