aiohttp目录穿越漏洞(CVE-2024-23334)分析

发布时间 2024-04-01

一、漏洞概述


aiohtp 是构建于 Python l/0 框架 AsyncI0 之上的开源库,用于处理无需基于传统线程网络的大量并发HTTP请求。aiohttp支持HTTP客户端、HTTP服务端、WebSocket客户端、WebSocket服务端、服务端中间件等。aiohttp被广泛使用,在网络中有大量基于该框架开发的在线系统。

和记ADLab研究员在漏洞情报跟踪中发现了aiohttp目录遍历漏洞(CVE-2024-23334),并对其进行了深入分析和验证。


二、影响版本


受影响版本:<3.9.2,请相关用户尽快升级到3.9.2及以上版本。


三、漏洞分析


该漏洞的关键信息如下[1]:

aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. When using aiohttp as a web server and configuring static routes, it is necessary to specify the root path for static files. Additionally, the option 'follow_symlinks' can be used to determine whether to follow symbolic links outside the static root directory. When 'follow_symlinks' is set to True, there is no validation to check if reading a file is within the root directory. This can lead to directory traversal vulnerabilities, resulting in unauthorized access to arbitrary files on the system, even when symlinks are not present. Disabling follow_symlinks and using a reverse proxy are encouraged mitigations. Version 3.9.2 fixes this issue.


根据关键信息,定位到开发文档的说明[2]:



根据文档描述,follow_symlinks是一个设计上用于非生产环境的功能,并且已在文档中明确提示启用该功能是一个安全风险。


编写如下的示例代码,测试follow_symlinks的功能:



在static目录下创建符号链接d,指向其它目录d:\test(该目录下存在测试文件123.txt)。



以follow_symlink=fasle的模式启动测试webserver,访问static/d/123.txt的结果如下所示(提示找不到文件):



以follow_symlink=true的模式启动测试webserver,访问static/d/123.txt的结果如下所示(成功读取文件内容):



显然,根据文档的描述,如果follow_symlink=true且存在符号链接,结果就是程序的正常预期功能。那么,该功能是如何被认定成漏洞?


漏洞的关键信息[1]中有一句重要描述:漏洞不依赖于符号链接的存在性。但是,在请求路径中使用不存在的符号链接,底层又如何能访问到目标文件。


经过分析,发现底层处理请求的关键函数如下:



首先,获取请求文件名(filename)。以 GET /static/d/123.txt为例,filename就是d/123.txt。


然后,把filename转换为Path对象,并检测该对象是否存在anchor属性。如果存在,则拒绝访问。在windows平台上,anchor对象就是盘符(比如d:\)。因此,此处存在安全检测,即不允许跨盘符访问。比如GET /static/d:\test\123.txt,会被视作非法请求而拒绝:



再然后,把static目录的Path对象和filename的Path对象做拼接,形成新文件路径filepath。最后,读取filepath的数据,并返回给客户端。


显然,如果filename包含不存在的符号链接,但能访问到目标文件,那说明对象拼接产生了非预期的结果。由于Path对象是标准的python类,直接单独进行测试。


(1)设定directory=c:\test , filename = d:\test\123.txt,拼接代码和结果如下:



拼接结果指向目标文件d:\test\123.txt,但是fname包含anchor,无法通过前面的filename.anchor检查。


(2)设定directory=c:\test , filename = ..\d:\test\123.txt,拼接代码和运行结果如下:



拼接结果读取到目标文件,且fname不包含anchor,能通过filename.anchor检查。


(3)设定directory=c:\test , filename = ..\..\d:\test\123.txt,拼接代码和运行结果如下:



拼接结果也读取到了目标文件,且fname不包含anchor,能filename.anchor检查。


因此,只要在filename之前加上..\就能直接引入盘符,违背原始功能设计中需要符号链接存在的限制,从而形成了可以读取磁盘上任意文件的安全漏洞。


四、补丁分析


补丁关键代码如下:



补丁的核心机制是:路径拼接成unresolved_path后,首先调用normpath处理unresolved_path来形成normalized_path,然后判定normalized_path是否位于静态目录_directory之下。


单独测试补丁机制的代码和结果如下:



显然,由于拼接后的路径不是static目录的子目录,所以触发了异常,导致后续代码不再读取拼接后路径下的文件,从而导致了该漏洞不再存在。


补丁不影响aiohttp的符号连接支持,因为符号链接文件必须存在于static目录之下。使用符号链接文件来处理static资源本身就是一个潜在的安全风险,开发者仍需要引起足够的重视,谨慎使用该功能。