刷b站看到有大佬在讲这个漏洞,正好打算学习一下java安全,这个就当了解一下java代码审计吧,为以后的学习找一下方向,寒假回去学一下java基础和Servlet开发,打算培养一下代码审计能力。
先来分析一下补丁
https://github.com/apache/struts/commit/162e29fee9136f4bfd9b2376da2cbf590f9ea163
新增了java.util.Iterator 接口
java.util.Iterator(迭代器)-CSDN博客
修改后的新增添迭代器的作用是通过遍历参数集合中的条目,查找与给定参数名相匹配的条目,并从集合中移除这些匹配的条目。这种方式使得参数集合的具体实现对客户端代码来说是透明的,同时也支持了对集合进行安全修改的操作。
迭代器( Iterator )模式,又叫做游标( Cursor )模式,是用于遍历集合类的标准访问方法。
这段代码的目的是在参数集合中查找并移除与给定参数名相匹配的参数。由于进行了大小写不敏感的比较,即使参数名的大小写不同,也会被正确地识别和移除。
这段代码用于检查在不区分大小写的情况下,给定的映射parameters
中是否包含与输入名称name
匹配的键。如果找到匹配的键,则返回true
,否则返回false
。
S2-066 - Apache Struts 2 Wiki - Apache Software Foundation
根据描述
1 | An attacker can manipulate file upload params to enable paths traversal and under some circumstances this can lead to uploading a malicious file which can be used to perform Remote Code Execution. |
攻击者可以操纵文件上传参数来启用路径遍历,在某些情况下,这可能导致上传可用于执行远程代码执行的恶意文件。
前置知识
Apache Struts Web 框架是一个免费的开源解决方案,用于 使用 Java 创建动态 Web 应用程序。
Web 应用程序与传统网站的不同之处在于 Web 应用程序 可以创建动态响应。许多网站只提供静态页面。 Web 应用程序可以与数据库和业务逻辑引擎进行交互 自定义响应。
基于 JavaServer Pages 的 Web 应用程序有时混合数据库 代码、页面设计代码和控制流代码。在实践中,我们发现 除非并且直到这些关注点被分开,否则更大的应用程序会变得 难以维护。
在软件应用程序中分离关注点的一种方法是使用 模型-视图-控制器 (MVC) 体系结构。模型代表业务或 数据库代码,视图表示页面设计代码,控制器表示 表示导航代码。Struts 框架旨在帮助 开发人员创建利用 MVC 体系结构的 Web 应用程序。表达式引擎–OGNL - 掘金 (juejin.cn)
该框架提供三个关键组件:
应用程序开发人员提供的映射到标准 URI 的“请求”处理程序。 一个“响应”处理程序,用于将控制权转移到完成响应的另一个资源。 一个标记库,可帮助开发人员使用服务器页面创建基于表单的交互式应用程序。 该框架的架构和标签符合流行语。支柱工作原理 适用于传统的REST应用程序以及SOAP和AJAX等技术。
struts2的内部三大核心体系
其中的控制流体系就是控制程序逻辑执行的先后顺序,其中有过滤器(Filter),控制器(Action),拦截器(Interceptor)
默认拦截器Interceptors (apache.org)按默认的顺序来完成
拦截器与过滤器的差别:Struts2中过滤器和拦截器的区别_strut2 拦截器和顾虑器-CSDN博客
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调
2、过滤器依赖与servlet容器,而拦截器不依赖与servlet容器
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
struts2处理请求流程详解 - pangbangb - 博客园 (cnblogs.com)
环境搭建
以6.3.0为例搭建
1 | <dependency> |
定义一个UploadAction
1 | package com.struts2; |
注意修改package的路径
在struts.xml当中,通常默认配置下这个文件在项目路径的/WEB-INF/classes路径下
1 | <?xml version="1.0" encoding="UTF-8"?> |
以及在web.xml当中配置好filter
1 | <filter> |
启动
使用tomcat9.0,注意自己配置的端口,
然后启动就可以
测试
在test/resources/下创建一个uploadtest.http
1 | POST /demo_war_exploded/upload.action HTTP/1.1 |
注意修改路径
在upload目录下可以看到文件
debug分析
使用f7 调试的时候遇到方法体的时候会进入到方法体内部 每个方法依次执行
使用f8 调试的时候 遇到方法体不会进入方法内部 只会依次执行
使用f9 调试的时候 只会执行 打断点的地方
在UploadAction.java添加断点
然后再次请求,可以看到很多的拦截器
第一列是方法,第二列是类名,第三类是类名对应的包名
找到一个类名为
FileUploadInterceptor的类名,进入后看到有很多的方法,转到这个抽象方法
猜想程序是从这里调用的,所以在这里添加一个断点,调试可以看到传的参数。
继续往下执行,可以看到传递的参数
这里已经可以看到上传的文件名。
接着往下翻
通过这一步拿到文件名
然后运行到这里,可以看到inputName是upload
然后步入
F7,F8可以看到这里只有之前上传的1.txt文件,然后返回文件名数组
看到拼接
进入这个函数看一眼
里面是一些过滤的东西
然后在外部库找
可以看到有很多的拦截器
然后查看setParameters
可接受参数类型为map,但实际上是一个treeMapJava TreeMap - Java教程 - 菜鸟教程 (cainiaojc.com)
java知识太少,博主转来转去实在跟不上了,就先放放,等学一些开发再来学习
TreeMap底层是基于红黑树(二叉排序树,数据结构学过)实现的,该映射根据其键的自然顺序进行排序,或根据创建映射时提供的Comparator进行排序,关键在于使用的构造方法的不同。
重点是TreeMap的一个特性就是键在遍历(中序遍历)的时候是ASCII码在前的就在前面,ASCII靠后的就在后面测试如下
1 | import org.junit.jupiter.api.Test; |
接下来就是判断这个参数是否可以接受
任意路径文件上传原理以及构造
程序将我们所以请求的参数放到一个参数map里面
文件的命名方式为InputName+FileName
1 | --------------------------xmQEXKePZSVwNZmNjGHSafZOcxAMpAjXtGWfDZWN |
把InputName构造为Upload
利用TreeMap的特性
保证InputName为uploadFileName在后面,通过反射注入到action
会有函数时会将set拼接到前面第三个字母大写,然后构造的会覆盖掉正常被过滤的文件名。
参考:
深入struts2_控制流体系 - 简书 (jianshu.com)
Apache Struts2 文件上传分析(S2-066) (y4tacker.github.io)
漏洞分析|S2-066—Apache Struts2 文件上传漏洞(CVE-2023-50164)_struts2-066-CSDN博客
【漏洞原理】代码审计 Debug源码分析 Struts2 S2-066漏洞(CVE-2023-50164)_哔哩哔哩_bilibili
Struts2请求流程与S2-066漏洞深入分析_哔哩哔哩_bilibili
Struts2 历史 RCE 的学习与研究:附最新 S2-066(CVE-2023-50164) (seebug.org)