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把应用运行起来
然后访问http://localhost:8787/static/vulbox程序进入这个流程,往下跟发现sanic判断了"../"是否存在于file_uri中,这样看似就能阻止我们穿越目录去访问别的文件。
继续往下走sub('^[/]*', '', file_uri)这里对file_uri进行了处理就是将开头/的字符全部替换为'',下面一行就是造成此次漏洞的关键代码了。
unquote函数对file_path进行了 urldecode 可以将%20、%2f等字符还原成原字符。因为有这一行我们就可以为所欲为了。
漏洞利用
分析好了代码,接着我们就利用吧 这次我们访问http://localhost:8787/static/..%2f..%2f..%2ftmp%2ftest在通过ide去debug看下整个过程。 传入的file_uri为
在这一步因为前面没有decode所以绕过了检查限制没有抛出错误。
通过sub和path_join函数这时的文件路径为/var/tmp/..%2f..%2f..%2ftmp%2ftest,如果此时直接交给File response handler进行处理就会抛出FileNotFound。
机智的程序员为了防止这种情况,用了unquote函数对file_path进行了 decode,经过转换后file_path就变为了/var/tmp/../../../tmp/test。
最后通过response.py中的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
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论