web安全-SSTI模板注入漏洞

一.初识SSTI

1.什么是SSTI注入?

SSTI模板注入(Server-Side Template Injection),通过与服务端模板的输入输出交互,在过滤不严格的情况下,构造恶意输入数据,从而达到读取文件或者getshell的目的。

2.SSTI漏洞成因

​漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。

3. 利用条件

网站由数据与模板框架处理输出页面,我们的数据在数据库不会改变,但是画面的模板可以转换多样,当模板存在可控的参数变量或模板代码内有模板的调试功能,可能会导致SSTI模板注入,对大多数脚本类型均存在该注入。

二. 含注入风险的模板框架

1. python中的SSTI

python常见的模板有:Jinja2,tornado

Jinja2

python模板注入漏洞的产生在于Flask应用框架中render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,而且Flask模板中使用了Jinja2作为模板渲染引擎,{ {}}在Jinja2中作为变量包裹标识符,在渲染的时候将{ {}}包裹的内容作为变量解析替换,比如{ {1+1}}会被解析成2。

以一道ctf例题展示模板框架及利用方法:

攻防世界shrine wp

源码:

import flask
import os

app = flask.Flask(__name__)
#用当前模块的路径初始化app,__name__是系统变量即程序主模块或者包的名字,该变量指的是本py文件的文件名。

app.config['FLAG'] = os.environ.pop('FLAG')
#设置一个配置:app.config[‘FLAG’]就是当前app下一个变量名为’FLAG’的配置,
#它的值等于os.environ.pop(‘FLAG’)移除环境变量中的键名为’FLAG’的值。

#访问http://ip/,则执行index()函数打开当前文件,读取文件内容,返回文件源码
@app.route('/')
def index():
    return open(__file__).read()

#访问http://ip/shrine/,则调用flask.render_template_string函数
#返回渲染模板字符串safe_jinja(shrine)
@app.route('/shrine/<path:shrine>')
def shrine(shrine):

    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')    #先去掉s字符串变量中的“(”和“)”左右括号
        blacklist = ['config', 'self']             #过滤掉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)

shrine路径下,构造Python模板注入,发现存在模板注入

之后查看config、self配置:

过滤了括号和关键字,所以带括号的魔法函数都不能使用,可以利用其他Python内置函数

Python中常用于SSTI的魔术方法

__class__:返回类型所属的对象
__mro__:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__:返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的
__subclasses__:每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__:类的初始化方法
__globals__:对包含函数全局变量的字典的引用
__builtins__:builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块。
app.config['FLAG'] = os.environ.pop('FLAG')

这道题中的目的是读取配置文件中变量名为’FLAG’的值,也就是环境变量中的键名为’FLAG’的值,但是config、self参数的值设为None,无法直接查看,可利用如下两种函数:

url_for()函数查看flag

用url_for函数来查看当前包中所有的静态文件,其中包括了配置文件。

先查看url_for函数的全局变量的字典的引用:

其中’current_app’: <Flask ‘app’>键值对,current_app意思是当前app,那么我们直接查看app.config[‘FLAG’]

get_flashed_messages()函数查看flag 

flash()用于闪现(可以理解为发送)一个消息。在模板中,使用 get_flashed_messages() 来获取消息(闪现信息只能取出一次,取出后闪现信息会被清空)。

flash()函数有三种形式缓存数据:
(1)缓存字符串内容。
设置闪现内容:flash(‘恭喜您登录成功’)
模板取出闪现内容:{% with messages = get_flashed_messages() %}
(2)缓存默认键值对。当闪现一个消息时,是可以提供一个分类的。未指定分类时默认的分类为 ‘message’ 。
设置闪现内容:flash(‘恭喜您登录成功’,“status”)
模板取出闪现内容:{% with messages = get_flashed_messages(with_categories=true) %}
(3)缓存自定义键值对。
设置闪现内容:flash(‘您的账户名为admin’,“username”)
模板取出闪现内容:{% with messages = get_flashed_messages(category_filter=[“username”])

所以我们可以通过get_flashed_messages()来获取所有缓存的闪现内容:

http://61.147.171.105:54585/shrine/{
  {get_flashed_messages.__globals__}}

  • 0
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值