1. 示例代码
2. 漏洞位置
3. 漏洞原理
示例代码中在使用`os.path.join`拼接路径时,对用户输入的参数做了”..”防护,代码依然存在不安全性,可以利用`os.path.join`特性,输入`language_name=/tmp/storage/`路径,使得
src = /tmp/storage/p.tar
dst = /tmp/storage/p.tar
当src==dst 条件为true。dst 已经存在的原因,导致 shutil.copy 函数抛出未处理的异常。在结合`shutil.copy`条件竞争,通过并行的对upload、install、clean,多线程大量且快速请求,实现条件竞争的效果,使服务器验证src存在,dst不存在,从而成功执行 shutil.copy。代码继续向下运行,导致 p.tar 被通过 shutil.unpack_archive 解压缩,从而tar包中的文件,解压在服务器。
4. 漏洞利用
访问页面如下:
使用upload上传p.tar文件,Burpsuite抓包,发送intruder模块,示例代码得知上传的p.tar会被存储在/tmp/storage/目录下,完整文件路径为/tmp/storage/p.tar
使用install执行解压操作,解压提交参数为language_name=/tmp/storage/p.tar,Burpsuite抓包,发送intruder模块。
使用clean执行清除文件操作,示例代码得知,clean会删除/tmp/storage/目录下的文件,此处提交参数file=p.tar,Burpsuite抓包,发送intruder模块。
利用条件竞争,对三个请求包(upload、install、clean),同时多线程大量且快速请求,实现条件竞争的效果。
查看insatll解压请求,查看响应码为200的,说明成功执行install解压操作,上传的p.tar成功解压。
示例代码中可知,上传的p.tar文件解压在’/tmp/extract’目录下,服务器查看截图如下:
p.tar文件内容
5. 修复方案
安全的路径拼接:在install()函数中使用os.path.join()拼接路径时,应该避免直接使用用户提供的language_name。
白名单方式:创建路径白名单,以确保拼接出的路径合法且在指定目录下。
相关知识
os.path.join 函数
函数os.path.join(path,*paths)用于将多个文件路径组件连接到一个组合文件路径中。第一个参数通常包含基本路径,而每个进一步的参数都作为一个组件附加到基本路径。该函数有一个特性,。如果其中一个附加组件以/开头,则包括基本路径在内的所有先前组件都将被删除,并且该组件将被视为绝对路径。
shutil.copy
shutil.copy(src,dst)函数,实现文件拷贝功能,如果src和dst两者指向统一文件,则报出异常。
shutil.copy的条件竞争:此函数执行过程,会先验证src文件是否存在,存在,则以二进制读取模式打开src文件,将读取内容暂时保存内存中。然后验证dst文件,将内容写入dst文件中。
作者:Spider-Man
2023年9月15日晚
洞源实验室