欢迎来到 无奈人生 安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

一次安全测试引发的对Django框架文件上传安全机制的初步分析

来源: 作者: 时间:2019-02-24 21:00 点击: 我要投稿
广告位API接口通信错误,查看德得广告获取帮助

我司的堡垒机是基于jumpserver 0.3版本进行二次开发,进行了大量的重构和新功能的添加,基本满足了公司安全运维的需求。在对文件上传接口进行安全审计的时候发现,其对上传文件名没有过滤处理,然后直接写入磁盘(部分代码如下)

隐隐觉得可以搞搞任意文件写入漏洞(jumpserver web 控制面板都是以root权限运行,如果可以任意文件写入,危害呵呵,你懂得)。启动burpsuite ,拦截请求,修改文件名(含有目录穿越字符),但是结果没成功,调试发现upload_file.name 已经是../../等目录穿越字符过滤后的结果,有点奇怪,莫非是框架自动帮我过滤掉了,好奇心驱使我必须弄明白其中的原理,于是有了本文。
0×01. 分析过程
切入点就是request.FILES 对象的由来,整个流程涉及到5个模块,如下:
django.core.handlers.wsgi
django.http.request
django.http.multipartparser
django.core.files.uploadhandler
django.core.files.uploadedfile
request.FILES 是一个类似于dict的对象,上传文件输入框name属性的值为键名,键值指向处理后的文件对象(框架会调用指定的文件处理器处理),这个文件对象就是django.core.files.uploadedfile 模块中UploadedFile类的实例。详细分析如下:
访问request.FILES 就是访问 django.core.handlers.wsgi 模块中WSGIRequest(继承至django.http.request模块的HttpRequest类)类的FILES属性

也即访问WSGIRequest._get_files,这个方法会先判断是否已经解析过上传的文件(也即判断是否有_files属性,其实FILES 就是_files,MultiValueDict 类的实例),跟进_load_post_and_files 方法(这是继承至其父类django.http.request模块的HttpRequest类中的方法),如下:

跟进parse_file_upload方法(这里的data为WSGIRequest 的实例),如下:

这里要先说下upload_handlers 成员,如下:

初始化upload_handlers的时候会调用django.core.files.uploadhandler模块的load_handler加载系统默认的文件处理器,如下:

settings.FILE_UPLOAD_HANDLERS
默认就是指的红框中的两个文件处理器,大于2.5M的就用TemporaryFileUploadHandler 处理器,否则用MemoryFileUploadHandler。
初始化文件上传处理器之后,就开始调用django.http.multipartparser 模块的MultiPartParser 类的parse 方法对上传文件进行解析处理,在解析处理过程中,会调用 handle_file_complete 对上传后的文件进行再次处理(处理完成后就返回一个django.core.files.uploadedfile.UploadedFile类的实例, 这个实例对象会被添加到_files 对象中,然后由parse 方法返回此对象, 这个过程就包含文件名被过滤掉的过程),如下:

图中的old_filed_name 即为上一个解析完毕的文件,跟进handle_file_complete ,如下:

跟进文件处理器的file_complete方法, 这个方法返回的就是处理后的文件对象,也就是0×00 图中upload_file 变量指向的文件对象,这里我们以MemoryFileUploadHandler 文件处理器为例进行说明:

也就是说0×00 中的upload_file 也即InMemoryUploadedFile 类的实例,所以调用upload_file.name 即调用InMemoryUploadedFile 的name属性,如下:

调用InMemoryUploadedFile 的name属性,即调用_get_name方法,在InMemoryUploadedFile  实例话的过程中有name的赋值操作(在其父类__init__方法中)如下:

赋值操作就会触发_set_name方法的执行:

[1] [2]  下一页

我司的堡垒机是基于jumpserver 0.3版本进行二次开发,进行了大量的重构和新功能的添加,基本满足了公司安全运维的需求。在对文件上传接口进行安全审计的时候发现,其对上传文件名没有过滤处理,然后直接写入磁盘(部分代码如下)

隐隐觉得可以搞搞任意文件写入漏洞(jumpserver web 控制面板都是以root权限运行,如果可以任意文件写入,危害呵呵,你懂得)。启动burpsuite ,拦截请求,修改文件名(含有目录穿越字符),但是结果没成功,调试发现upload_file.name 已经是../../等目录穿越字符过滤后的结果,有点奇怪,莫非是框架自动帮我过滤掉了,好奇心驱使我必须弄明白其中的原理,于是有了本文。
0×01. 分析过程
切入点就是request.FILES 对象的由来,整个流程涉及到5个模块,如下:
django.core.handlers.wsgi
django.http.request
django.http.multipartparser
django.core.files.uploadhandler

www.wnhack.com


django.core.files.uploadedfile
request.FILES 是一个类似于dict的对象,上传文件输入框name属性的值为键名,键值指向处理后的文件对象(框架会调用指定的文件处理器处理),这个文件对象就是django.core.files.uploadedfile 模块中UploadedFile类的实例。详细分析如下:
访问request.FILES 就是访问 django.core.handlers.wsgi 模块中WSGIRequest(继承至django.http.request模块的HttpRequest类)类的FILES属性

也即访问WSGIRequest._get_files,这个方法会先判断是否已经解析过上传的文件(也即判断是否有_files属性,其实FILES 就是_files,MultiValueDict 类的实例),跟进_load_post_and_files 方法(这是继承至其父类django.http.request模块的HttpRequest类中的方法),如下:

跟进parse_file_upload方法(这里的data为WSGIRequest 的实例),如下:
本文来自无奈人生安全网


这里要先说下upload_handlers 成员,如下:

初始化upload_handlers的时候会调用django.core.files.uploadhandler模块的load_handler加载系统默认的文件处理器,如下:

settings.FILE_UPLOAD_HANDLERS
默认就是指的红框中的两个文件处理器,大于2.5M的就用TemporaryFileUploadHandler 处理器,否则用MemoryFileUploadHandler。
初始化文件上传处理器之后,就开始调用django.http.multipartparser 模块的MultiPartParser 类的parse 方法对上传文件进行解析处理,在解析处理过程中,会调用 handle_file_complete 对上传后的文件进行再次处理(处理完成后就返回一个django.core.files.uploadedfile.UploadedFile类的实例, 这个实例对象会被添加到_files 对象中,然后由parse 方法返回此对象, 这个过程就包含文件名被过滤掉的过程),如下: 本文来自无奈人生安全网

图中的old_filed_name 即为上一个解析完毕的文件,跟进handle_file_complete ,如下:

跟进文件处理器的file_complete方法, 这个方法返回的就是处理后的文件对象,也就是0×00 图中upload_file 变量指向的文件对象,这里我们以MemoryFileUploadHandler 文件处理器为例进行说明:

也就是说0×00 中的upload_file 也即InMemoryUploadedFile 类的实例,所以调用upload_file.name 即调用InMemoryUploadedFile 的name属性,如下:

www.wnhack.com


调用InMemoryUploadedFile 的name属性,即调用_get_name方法,在InMemoryUploadedFile  实例话的过程中有name的赋值操作(在其父类__init__方法中)如下:

赋值操作就会触发_set_name方法的执行:
无奈人生安全网

[1] [2]  下一页 本文来自无奈人生安全网

。 (责任编辑:admin)
【声明】:无奈人生安全网(http://www.wnhack.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱472701013@qq.com,我们会在最短的时间内进行处理。