Sanic <=0.5.0 static file任意文件读取

  • A+
所属分类:颓废's Blog
摘要

一个基于 Python3.5+ 的异步(asyncio+uvloop)web框架,与Flask有点相似,特点就是非常的快,每秒钟可处理30k请求。

Sanic是什么

一个基于 Python3.5+ 的异步(asyncio+uvloop)web框架,与Flask有点相似,特点就是非常的快,每秒钟可处理30k请求。

复现环境及漏洞影响版本
Python 3.6
Sanic <= 0.5.0
macOS 10.12.4
漏洞分析
首先看下一个sanic处理静态文件的一个示例代码,第九行对static的路由进行了注册。

#!/usr/bin/env python # -*- coding:utf-8 -*- from sanic import Sanic from sanic.response import text   app = Sanic()  app.static('/static', '/var/tmp')#这里注册了static url为/static 实际物理路径为/var/tmp  @app.route("/") async def index(request):     return text('Hello!')   if __name__ == '__main__':     app.run(host='127.0.0.1', port=8787, debug=True)

我们直接看static.py中的静态文件处理逻辑,在_handler函数下个断点接着debug把应用运行起来

Sanic <=0.5.0 static file任意文件读取

然后访问http://localhost:8787/static/vulbox程序进入这个流程,往下跟发现sanic判断了"../"是否存在于file_uri中,这样看似就能阻止我们穿越目录去访问别的文件。

Sanic <=0.5.0 static file任意文件读取

继续往下走sub('^[/]*', '', file_uri)这里对file_uri进行了处理就是将开头/的字符全部替换为'',下面一行就是造成此次漏洞的关键代码了。

unquote函数对file_path进行了 urldecode 可以将%20、%2f等字符还原成原字符。因为有这一行我们就可以为所欲为了。
Sanic <=0.5.0 static file任意文件读取

漏洞利用
分析好了代码,接着我们就利用吧 这次我们访问http://localhost:8787/static/..%2f..%2f..%2ftmp%2ftest在通过ide去debug看下整个过程。 传入的file_uri为
Sanic <=0.5.0 static file任意文件读取

在这一步因为前面没有decode所以绕过了检查限制没有抛出错误。

Sanic <=0.5.0 static file任意文件读取

通过sub和path_join函数这时的文件路径为/var/tmp/..%2f..%2f..%2ftmp%2ftest,如果此时直接交给File response handler进行处理就会抛出FileNotFound。

Sanic <=0.5.0 static file任意文件读取

机智的程序员为了防止这种情况,用了unquote函数对file_path进行了 decode,经过转换后file_path就变为了/var/tmp/../../../tmp/test。

Sanic <=0.5.0 static file任意文件读取

最后通过response.py中的file函数将文件返回。

Sanic <=0.5.0 static file任意文件读取

成功目录穿越读取到别的文件。

Sanic <=0.5.0 static file任意文件读取

如何修复
大多数修复手段都是通过对../字符进行替换,或者判断路径中是否有../字符,这样的修复手段还是存在安全隐患。

以比较彻底的修复方式是在读取文件之前对最终路径的abspath进行判断,如果传入的abspath与注册的不一致则发生了目录穿越。

修复代码示例

#!/usr/bin/python # -*- coding:utf-8 -*- import os  #root_path注册的static文件路径 root_path = file_path = '/opt/sanic/static/' #file_uri用户输入的路径 file_uri = request.args.get('file', None) if file_uri:     file_path = os.path.join(root_path, sub('^[/]*', '', file_uri)) file_path = os.path.abspath(file_path) if not file_path.startwith(root_paht):     raise FileNotFound('File Not Found') ...

作者 lazydog

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: