专栏名称: 国舜股份
移动互联网时代的综合性网络安全解决方案供应商。专业的安全产品,专业的安全服务团队,全面的安全服务资质,安全不变,国舜同行。
目录
相关文章推荐
笔吧评测室  ·  模块化硬件厂商 Framework 预告 ... ·  15 小时前  
笔吧评测室  ·  荣耀 MagicBook Pro 14 ... ·  昨天  
笔吧评测室  ·  华硕无畏 Pro 14 骁龙版 2025 ... ·  昨天  
笔吧评测室  ·  荣耀 MagicBook Pro 14 ... ·  2 天前  
笔吧评测室  ·  聊一台售后长达3年的一线笔记本 ·  2 天前  
51好读  ›  专栏  ›  国舜股份

Apereo CAS 反序列化漏洞分析

国舜股份  · 公众号  ·  · 2020-01-19 16:47

正文


声明: 本文内容仅供安全研究之用,不恰当使用会造成危害,严禁用于非法用途,否则由使用者承担全部法律及连带责任。

0x00 前言

CAS全称Central Authentication Service(中心认证服务),是一个单点登录(Single-Sign-On)协议,Apereo CAS是实现该协议的软件包。CAS最初由Yale大学的Shawn Bayern开发实现,随后由Yale大学的Drew Mazurek负责维护。2016年4月CAS官方披露了 v4.1.x和v4.2.x [1] 版本存在反序列化漏洞,本文对 Apereo CAS反序列化漏洞的成因进行了相关分析,如有不足之处欢迎指正。

0x01 漏洞分析

1. 了解Java反序列化

序列化是把对象转换成字节流,反序列化是逆过程,把字节流还原成对象。Java中ObjectOutputStream类的writeObject()方法可以实现对象的序列化,ObjectInputStream类的readObject()方法用于反序列化。如果被序列化的类重写了readObject()方法,在反序列化的时候,会调用重写的readObject()方法,如果重写的readObject()方法里面被插入了恶意代码,那在反序列化的过程中恶意代码就会被自动执行。具体看示例代码容易理解一点:

定义需要被序列化的类Users

import java.io.IOException;import java.io.Serializable;
//需要被序列化的类,必须实现Serializable接口public class Users implements Serializable {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
//重写readObject方法 private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { //调用默认readObject方法,不破坏原逻辑 in.defaultReadObject(); System.out.println("重写的readObject方法..."); }
}

把Users对象序列化,保存到本地users.bin文件中

    public void serialize() throws IOException {        FileOutputStream out = new 




    
FileOutputStream("users.bin");        ObjectOutputStream obj = new ObjectOutputStream(out);        Users user = new Users();        user.setName("Apereo CAS");        obj.writeObject(user);    }

反序列化还原user.bin文件中的Users对象

    public static void deserialize() throws IOException, ClassNotFoundException {        FileInputStream in = new FileInputStream("users.bin");        ObjectInputStream obj = new ObjectInputStream(in);        //Users类中重写了readObject方法,会自动调用Users类中的readObject方法        Users user = (Users) obj.readObject();        System.out.println(user.getName());    }

调用deserialize方法后输出如下,发现Users类中的readObject方法被自动调用

2. 环境准备

cas-4.1.5 [2] 版本为例,下载war包,配置好tomcat运行环境,在tomcat bin目录下的catalina.bat文件中新增启动参数,使tomcat支持jdb动态调试

set CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8899



启动tomcat,8899端口开启说明可以使用jdb调试


访问cas应用,抓取登录请求数据包如下


3. execution参数分析

根据poc知道造成漏洞的原因的post参数execution造成的,所以需要找到处理execution参数的servlet,先看看web.xml配置文件, /cas/login 接口由 org.springframework.web.servlet.DispatcherServlet 处理。
注: spring中servlet的url-pattern匹配规则需要减去应用上下文路径,以剩余的字符串作为servlet映射。



搜索下DispatcherServlet类所在文件


反编译spring-webmvc-4.1.8.RELEASE.jar,根据Spring DispatcherServlet请求分发流程可知,最终的核心处理方法是DispatcherServlet的doDispatch方法


需要关注的代码是939行的 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); ,这里获取此次请求的HandlerAdapter,然后在959行 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 调用实际实现的handle方法处理具体逻辑。

使用JDB断点939行,看看处理当前登录请求的HandlerAdapter实现类


grep搜索SelectiveFlowHandlerAdapter类所在文件



反编译cas-server-webapp-support-4.1.5.jar,handle方法实现在SelectiveFlowHandlerAdapter的父类 ./WEB-INF/lib/spring-webflow-2.4.1.RELEASE.jar!/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.class 文件中



静态分析handle方法,可知在224行调用了getFlowExecutionKey方法处理request,使用JDB动态调试看一看,其实现在org.jasig.cas.web.flow.CasDefaultFlowUrlHandler类中,该类在cas-server-webapp-support-4.1.5.jar文件里


getFlowExecutionKey方法实现如下,可知这里获取了post参数execution


回到handle方法,getFlowExecutionKey返回结果不等于null,进入225行的if判断,然后把获取的execution参数值传入resumeExecution方法,继续JDB调试,resumeExecution方法实现在FlowExecutorImpl类中


spring-webflow-2.4.1.RELEASE.jar!/org/springframework/webflow/executor/FlowExecutorImpl.class的resumeExecution方法实现如下



继续跟进164行的parseFlowExecutionKey方法,其实现在spring-webflow-client-repo-1.0.0.jar!/org/jasig/spring/webflow/plugin/ClientFlowExecutionRepository.class文件中



parse方法实现在ClientFlowExecutionKey类中,这里把execution参数值通过" _ "符号split存放在String数组里面,然后base64解码再作为参数传入ClientFlowExecutionKey构造函数并RETURN



返回去接着看spring-webflow-2.4.1.RELEASE.jar!/org/springframework/webflow/executor/FlowExecutorImpl.class的resumeExecution方法,把return的ClientFlowExecutionKey对象传入getFlowExecution方法



getFlowExecution方法实现如下



88行先getData()获取execution参数" _ "符号分割的后部分数据,data通过ClientFlowExecutionKey构造函数赋值



然后把获取到的数据传入this.transcoder.decode方法中,该方法实现在spring-webflow-client-repo-1.0.0.jar!/org/jasig/spring/webflow/plugin/EncryptedTranscoder.class文件



分析decode方法可知,首先把传入的data通过 cipherBean.decrypt 方法解密,最后解密的数据在117行 in.readObject() 处触发Java反序列化漏洞。这里数据加解密使用的是AES对称算法



4. 构造POC

通过分析我们知道Apereo CAS应用RCE漏洞是Java反序列化造成的,所以可以借助GitHub开源工具 ysoserial [3]







请到「今天看啥」查看全文