今年早些时候,有研究人员发现并公布了 Oracle WebLogic中的一个反序列化漏洞 。这个漏洞比较严重,因此Oracle打破正常按季度发布补丁的惯例,专门发布紧急 更新 。不幸的是,研究人员很快就意识到攻击者有可能绕过这个补丁。
官方补丁有时候不能完全解决某个安全问题,这似乎是现在的一个 趋势 ,Oracle也不例外。本文介绍了一个目录遍历漏洞,官方通过多次补丁才修复该漏洞。Oracle最早在2019年4月份通过 CVE-2019-2618 修复这个漏洞,但随后又在7月份发布了修正补丁。
Oracle WebLogic是用来构建和部署Java Enterprise Edition(EE)应用的应用服务器。在默认安装下,WebLogic服务器会包含各种应用,以便维持和配置相关域名及应用。比如其中有个 bea_wls_deployment_internal.war
应用,该应用有个上传文件功能。通过身份认证后,用户可以向 /bea_wls_deployment_internal/DeploymentService
发送请求来上传文件。
如果请求头部中的 wl_request_type
值为 app_upload
或者 plan_upload
,那么应用就会调用 handlePlanOrApplicationUpload()
。 handlePlanOrApplicationUpload()
方法会验证 wl_upload_application_name
字段值,并且会检查两种目录遍历字符: ../
以及 /..
:
图1. 检查目录遍历字符(这里我们添加了一些注释)
<ORACLE_HOME>/user_projects/domains/[DOMAIN NAME]/servers/AdminServer/upload/
路径存储在 uploadingDirName
变量中,服务器将 wl_upload_application_name
字段值作为该路径的子目录来使用。如图1代码中所示,服务端会将用户可控的 wl_upload_application_name
值附加到 uploadingDirName
中,然后以 saveDirectory
参数形式传递给 doUploadFile()
。 doUploadFile()
函数会使用请求中的文件名参数在该位置创建一个文件。
图2. doUploadFile()
函数
wl_upload_application_name
以及 filename
字段都存在目录遍历漏洞。2019年4月,Oracle尝试通过CVE-2019-2618修复这个目录遍历问题。在CVE-2019-2618补丁中,官方加了一些处理逻辑,在 wl_upload_application_name
字段中又检查了两种目录遍历字符: ..
以及 ..
:
对于 filename
字段,CVE-2019-2618补丁中对 doUploadFile()
新增了一处检查,确保保存文件的最终路径包含 saveDir
所指定的正确目录。 saveDir
的值为 <ORACLE_HOME>/user_projects/domains/[DOMAIN NAME]/servers/AdminServer/upload/[UPLOAD_APP]
,其中 [UPLOAD_APP]
的值位于 wl_upload_application_name
中。如果 filename
变量包含目录遍历字符,并且不包含 saveDir
指示的字符串,那么新的 doUploadFile()
方法就会抛出异常:
图4. 针对 saveDir
的异常错误
针对 fileName
字段的检查基本已经比较完善,然而这里我们想提一下,如果官方不使用 contains
,而是使用 startsWith
来进行判断,那么修复的效果应该会更好。根据这个补丁的处理逻辑,从理论上讲,如果最终路径中任何一部分与合法的保存路径相似,那么就能绕过这个补丁。然而经过我们的分析,这里并没有直接可以利用的代码路径。如果 saveTo
指定的路径不存在,那么 doUploadFile()
函数不会自动创建对应的目录结构。因此,如果想绕过这个补丁,攻击者需要使用其他足够强大的技术,在服务器上的敏感位置创建任意目录结构,但本身这里无法提供文件上传功能。因此总的说来,这个补丁不大可能被绕过。
然而,对于 wl_upload_application_name
头部字段,CVE-2019-2618补丁就没有做好,攻击者可以将 wl_upload_application_name
头部字段值设置为 ..
(两个点)来绕过该补丁。通过这种方式,攻击者可以将文件上传到 <ORACLE_HOME>/user_projects/domains/[DOMAIN NAME]/servers/AdminServer
目录的任意子目录(要注意的是这里缺少一个最终路径组件: upload
)。因此,攻击者可以在 <ORACLE_HOME>/user_projects/domains/[DOMAIN NAME]/servers/AdminServer/tmp/
目录中写入一个JSP文件,实现代码执行。例如,攻击者可以通过如下POST请求,将 poc.jsp
文件写入 <ORACLE_HOME>/user_projects/domains/[DOMAIN NAME]/servers/AdminServer/tmp
目录中:
图5. 目录遍历漏洞演示
攻击者无需通过身份认证,就可以访问已写到 tmp
目录下 _WL_internalbea_wls_internal
子目录中的文件。对于这个案例,攻击者可以向 /bea_wls_internal/pos.jsp
这个URI发送请求,就能执行JSP代码。
官方在7月份发布了CVE-2019-2827补丁,成功修复了该漏洞。补丁会验证 wl_upload_application_name
头部字段值中是否包含 ..
目录遍历字符,如下所示:
图6. CVE-2019-2827补丁改动的代码
目录遍历漏洞的各种变种已经出现有一段时间,但依然会影响多种类型的软件。开发者应该确保自己在执行文件操作之前已经正确过滤了用户输入数据。多年以来,攻击者一直在使用各种编码技巧来绕过针对目录遍历的防御机制。比如,攻击者可以使用URI编码,将 %2e%2e%2f
转换为 ../
来绕过某些过滤器,我们永远不要低估这些人的创造力。
虽然本文介绍的是来自Oracle的一个不成功的补丁,但其他厂商也存在类似问题。对补丁的分析是检测开发者是否存在纰漏的一个好方法,检查被修复的组件同样是寻找相关漏洞的绝佳切入点。