自struts2官方纰漏S2-066漏洞已经有一段时间,期间断断续续地写,直到最近才完成。羞愧地回顾一下官方通告:2023.12.9发布,编号CVE-2023-50164,主要影响版本是 2.5.0-2.5.32 以及 6.0.0-6.3.0,描述中提到了文件上传漏洞和目录穿越漏洞。开始以为这是个组合漏洞,其实不是,这是一个漏洞,看了几篇大佬的文章,有的把它称为“文件上传目录穿越漏洞”,也有道理。准备工作就是搭建项目,用Tomcat跑,调试好断点,回顾下struts2的结构。篇幅有限,这里只贴一张struts2自身的配置文件:struts2有众多的Filter和Intercepter,它的配置逻辑是,除文件中定义的class、package以外,其余全部拦截。对于S2-066,处理一个请求要经过的几个关键类包括Dispatcher、Interceptor、HttpParameters以及UploadAction。使用下面的poc:请求首先会进入著名的Dispatcher。multi参数对应的就是body数据,包含upload、fileName、contentType三个变量,无误:request中还有一个参数,uploadFileName=../../z127.txt,这个是污染参数,文件上传的目的地,也是利用这个漏洞的目标。走到这里Dispatcher只是简单处理一下请求然后交给Interceptor,无异常。拦截器先是把request包装了一下,类型是MultiPartRequestWrapper。这里与Dispatcher一样,请求参数还是multi和request两部分,也无异常。并且遍历只有一次 ,因为真正的body只有一个,就是那个multi。在遍历过程中struts2还出现了硬编码现象,要求文件名参数必须以FileName结尾,且拼接完成的文件名前缀就是body中的{upload}名称,这就给exp带来了一定限制:来到HttpParameters查看接收的参数,还是upload、contentType、fileName三个:但是参数接收完后就不正常了,除了原本的UploadFileName(注意首字母是大写),还多了一个uploadFileName(注意首字母是小写),size也变成了4。这就是Struts2官方所解释的大小写敏感,即对大写的Upload和小写的upload分别做处理。从这里开始,S2-066才露出真正面目:通过操控上传参数,黑客能够出发目录穿越漏洞,这样一来,在某些情况下可以上传恶意文件,从而进行RCE。换种说法,S2-066是框架自身、软件工程师、Java反射机制共同作用的结果。走到这里,为了简化代码,UploadAction即是Action又是Entity。而在Entity的实例化过程中,必然是通过setXX属性来赋值。所以就有了setUploadFileName(注意首字母大写)和setuploadFileName(注意首字母小写)的需求 。而在Entity的setter与getter中,这两种需求都会被当做一种,即setUploadFileName,因此覆盖也就发生了。正常情况下实例化方法只走一遍,如contentType:参数不是filename结尾,失败:参数不符合FileName大小写要求,失败:大写覆盖小写失败:大写覆盖大写失败:小写覆盖小写失败:小写覆盖大写成功,文章开头所用。另外覆盖也可以放在body中:验证:利用条件多少有点苛刻,但杀伤力不输struts2过去那一堆,CVSS3.0评分9.8,CRITICAL。