跳转到主内容

CTF:最好的语言解密过程

题目描述就四个字——"最好的语言",一开始猜测PHP,打开文件才发现是Python。本文将完整记录解密过程。

来源 Bugku

下载题目后得到一个文本文件re没有后缀,用文本打开一看,是一个类似XML格式的文本


magic 03f30d0a
moddate 6d4a695b (Tue Aug  7 15:29:49 2018)
<code>
   <argcount> 0 </argcount>
   <nlocals> 0</nlocals>
   <stacksize> 5</stacksize>
   <flags> 0040</flags>
   <code>
      6400006401006c00005a00006400006402006c01006d02005a0200016400
      006401006c03005a03006400006401006c04005a04006403005a05006404
      ...
   </code>
   <names> ('base64', 'hashlib', 'md5', 'random', 'string', 'f', '_', '____', 'e', 'b64encode')</names>
   <consts>
      -1
      None
      ('md5',)
      'flag{*******}'
      ...
      'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
   </consts>
</code>
.......

看到头部有magic字段,这个应该是pyc文件生成;
pyc文件的结构:有magic number常量表变量名表字节码等。但缺少关键的<dis>反汇编字段,应该被出题人删掉了!这就没法直接用工具反编译。

恢复内容

虽然没有反汇编,但文件里保留了完整的常量表和变量名。解题思路是:写一个简单的Python脚本;

有请AI上场,用同样的方式编译解析,然后反复对比、猜测、验证,逐步还原原始代码。

经过一番折腾,还原出的核心代码如下:


import base64
from hashlib import md5
import random
import string

f = 'flag{*******}'

def _(b):
    __ = ''.join(random.sample(string.digits, 4))  # 随机4位数字密钥
    ___ = ''
    for i in range(len(b)):
        ___ += chr(ord(b[i]) ^ ord(__[i % 4]))     # 逐字符XOR加密
    return ___

def ____(a):
    ___ = md5()
    ___.update(a)
    return ___.digest()                             # 返回16字节MD5

# 核心:flag被切成三段,分别处理后拼接
e = _(f[:12]) + ____(f[12:19]) + _(f[19:])
print base64.b64encode(e)

输出结果:

U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU=

解密过程

第一步:Base64解码,分割三段


import base64
e = 'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
target = base64.b64decode(e)

before = target[:12]      # 前12字节:_(f[:12])
md5_raw = target[12:28]   # 中间16字节:____(f[12:19])
after = target[28:]       # 后10字节:_(f[19:])

# 38字节,正好12+16+10

第二步:破解前12字节

由于flag开头必定是 flag{,我们可以用这个已知明文来反推密钥。XOR加密有个特点:如果知道明文和密文,就能直接算出密钥。

但这里我们采用更直接的方法——暴力枚举所有可能的4位数字密钥(0000-9999的不重复排列,共5040种),用每个密钥去解密前12字节,看哪个结果以 flag{ 开头:


from itertools import permutations

all_keys = [''.join(p) for p in permutations('0123456789', 4)]

def xor_crypt(data, key):
    result = ''
    for i in range(len(data)):
        result += chr(data[i] ^ ord(key[i % 4]))
    return result

for key in all_keys:
    decrypted = xor_crypt(before, key)
    if decrypted.startswith('flag{'):
        print(f"密钥: {key}")
        print(f"结果: {decrypted}")
        break

运行结果:


密钥: 5914
结果: flag{PyC_1s_

这里就拿到了前12字符:flag{PyC_1s_

第三步:查询MD5

中间16字节是MD5的二进制结果,转成十六进制:


print(md5_raw.hex())
# 输出: 42eec54bb567824d4e4d0e7caaa8215e

拿到在线MD5破解网站一查,返回结果:613u21i

第四步:破解后10字节

后10字节的解密没有已知明文可以利用,但我们知道 flag 最后一位肯定是 }。同样暴力枚举所有密钥:


import string

for key in all_keys:
    decrypted = xor_crypt(after, key)
    if decrypted.endswith('}'):
        valid = set(string.ascii_letters + string.digits + '_}')
        if all(c in valid for c in decrypted):
            print(f"密钥={key}: {decrypted}")

输出多个候选,挨个测试,最终发现是:


密钥=4813: _N0t_Hard}

第五步:拼接完整flag


前段: flag{PyC_1s_
中段: 613u21i
后段: _N0t_Hard}

完整: flag{PyC_1s_613u21i_N0t_Hard}

加密逻辑分析


flag:  flag{PyC_1s_613u21i_N0t_Hard}
       |________| |_____| |_________|
         前12字符  中间7字符   剩余部分
            |        |          |
         _( )     ____( )     _( )
         XOR加密   MD5哈希   XOR加密
            |        |          |
         12字节   16字节     10字节
            \       |         /
             \      |        /
              拼接后Base64编码
                  |
              38字节密文

完整flag
flag{PyC_1s_613u21i_N0t_Hard}

modified: 2026-05-19 14:01
    1. 瑟的头像
      IP: 中国上海电信 #1
      思路清晰,学习了
    2. 路的头像
      IP: 中国北京联通 #2
      排版有序
支持 Markdown 语法