发表主题 回复主题
  • 1974阅读
  • 1回复

[代码审计]Sanic <=0.5.0 static file任意文件读取

一个为了解决读取文件异常,对文件路径使用unquote函数decode所引发的血案。 嗯,一个夜黑风高的晚上发现的这个漏洞,漏洞利用过程没有什么奇巧淫技,最后也是给官方提交了PR辅助了修复工作。


0x00 Sanic是什么

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

                 ▄▄▄▄▄
        ▀▀▀██████▄▄▄       _______________
      ▄▄▄▄▄  █████████▄  /                 \
     ▀▀▀▀█████▌ ▀▐▄ ▀▐█ |   Gotta go fast!  |
   ▀▀█████▄▄ ▀██████▄██ | _________________/
   ▀▄▄▄▄▄  ▀▀█▄▀█════█▀ |/
        ▀▀▀▄  ▀▀███ ▀       ▄▄
     ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
   ██▀▄▄▄██▀▄███▀ ▀▀████      ▄██
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███     ▌▄▄▀
▌    ▐▀████▐███▒▒▒▒▒▐██▌
▀▄▄▄▄▀   ▀▀████▒▒▒▒▄██▀
          ▀▀█████████▀
        ▄▄██▀██████▀█
      ▄██▀     ▀▀▀  █
     ▄█             ▐▌
 ▄▄▄▄█▌              ▀█▄▄▄▄▀▀▄
▌     ▐                ▀▀▄▄▄▀
 ▀▀▄▄▀

0x01 复现环境及漏洞影响版本

Python 3.6
Sanic <= 0.5.0
macOS 10.12.4

0x02 漏洞分析

首先看下一个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把应用运行起来point

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

logic

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

unquote函数对file_path进行了 urldecode 可以将%20%2f等字符还原成原字符。因为有这一行我们就可以为所欲为了。

unquote_example

0x03 漏洞利用

分析好了代码,接着我们就利用吧 这次我们访问http://localhost:8787/static/..%2f..%2f..%2ftmp%2ftest在通过ide去debug看下整个过程。 传入的file_urialt

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

alt

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

alt

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

alt

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

alt

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

alt

0x04 如何修复

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

以比较彻底的修复方式是在读取文件之前对最终路径的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')
...
本帖最近评分记录: 1 条评分 人民币 +20
hades 人民币 +20 感谢支持ing 欢迎常来 哈哈 06-12
级别: Master
只看该作者 沙发  发表于: 06-12
感谢支持ing 欢迎常来 哈哈
发表主题 回复主题
« 返回列表上一主题下一主题