Apereo CAS反序列化漏洞中数据加解密研究

news2024/10/4 15:30:52

Apereo CAS反序列化漏洞中数据加解密研究

  • 0x01、简介
  • 0x02、网上获取资料
  • 0x03、初步运行失败
    • 1、分析:
    • 2、Tips:
  • 0x04、分析原因
    • 1、自己写解密算法 / 直接使用cas工程的相关jar包、java文件,调用解密函数
    • 2、为什么会解密失败?
  • 0x05、断点调试
      • Tips:在步入得到过程中,有的方法[步入]功能不进去,需要使用[强制步入]功能
  • 0x06、代码分析
    • 1、key/iv初始化函数确定
      • key是固定的,iv是根据密文计算得来的
    • 2、key初始化函数分析
    • 3、iv初始化函数分析
  • 0x07、总结
      • 以此可以推断出,密文的实际构造情况为
      • 运行通过

0x01、简介

Apereo CAS,全称为 Central Authentication Service,是一种开源的单点登录(SSO)解决方案。它提供了一个可扩展的、可定制的平台,用于统一身份验证和访问控制,支持多种认证协议和技术。Apereo CAS 可以轻松地集成到现有的应用程序和服务中,为用户提供单点登录和数据交互能力。该系统具有高度的安全性和可靠性,同时也支持多种操作系统和编程语言。Apereo CAS 是一个成熟的、高度稳定的 SSO 解决方案,已经被广泛地应用于大型机构、企业和政府机构中。

4.1.7版本之前存在AES默认密钥的问题,利用这个默认密钥我们可以构造恶意信息触发目标反序列化漏洞,进而执行任意命令。

最近在写攻防中用到的工具(红队版:一键开天门;蓝队版:一键守天门)

其中蓝队版的一个功能:一键解密流量工具,为实现一键智能化

(毕竟蓝队猴子怎么会看得明白什么是shiro、什么是cas、什么是哥斯拉\蚁剑\冰蝎等等呢(对不起,不是所有人但是真的有))

因此需要研究一下CAS漏洞利用中execution值的加解密算法!结果网上没有一个深入研究构造结构体的,大多都是误导文章,而且都是直接套用cas原jar包

结论:UUID + _ + 头部长度标识(7byte) + iv长度标识(1byte) + iv值(16byte) + keyName(10byte) + AES密文
在这里插入图片描述

0x02、网上获取资料

针对于网上搜集到的资料进行总结:

  1. execution值处理流程过程为: 剔除头部UUID字段+base64解密+AES解密+Gzip解密
  2. AES存在默认密钥
  3. AES加密模式为 AES/CBC/PKCS7
  4. 【很多文章里都没有提到】 AES加解密相关的配置会 先去配置文件中获取 ,没有配置密钥信息的会使用 jar包默认的密钥信息
  5. 密钥库 的密码为 changeit
  6. 默认 keystore文件 位于 spring-webflow-client-repo-1.0.0.jar!/etc/keystore.jceks
  7. 网上有很多exp工具,直接使用cas工程的相关jar包、java文件,来调用加密函数,不需要自己写
  8. cas工程使用的是 Cryptacular 第三方库进行的AES加解密。

真的如网上所说的这些信息就够了么?开始调试

0x03、初步运行失败

1、分析:

  1. 剔除头部uuid;
  2. AES\CBC\PKCS7Padding解密,key为changeit;
  3. iv向量为多少?网上没提到,猜测随机生成;
import com.hotboy.utils.aesUtils;
import com.hotboy.utils.strUtils;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class casDecryptTest {

    public static byte[] decrypt(String input,aesUtils aes){
		String input = "7b951c2a-e78f-4286-95fe-970782352a84_AAAAIgAAABDE2HZ3uiB2bzFHXNO5uObYAAAABmFlczEyOCPJEIAG4U8FA%2b%2bSoqcRzovVlpSWfd/raZVfVf3gXUc8f9Xz%2beN25UhRBwwmMRAv%2byjSVLKbWeRzPkGeVvof/44rS4PcFfF9pzwo%2bEbqJz6ZBCo5%2blAczVCAp8UBjRy7R/jkb/YSZj6YBHDMJ0ejjqFly779A9b3opzyMwCIJod0yvs0qtYNd0qXhd8yY/XpatlelngVKxqLDp0lrwXmP7W3YblsIV/r3bJv2mHk1qAgVL54yTX4en4i17z37qKv6CBkRxZN5ORAERYUm4E%2bsmckjnQEKcjb2bqMWSi7WKxc57DPIBwnjmUJ8plB8aoNsjKxuOYri%2bsBpkZDcFucuyiTOwPOlGm4CaUYHxpoCiaLSJb%2bsj/0Iml107F8L%2bH/pzwtH1BHQef1eVtcml0AKGpVf0YzR9UUua8PysUxbqQpFa36nC3RgIoz97v9Hi6oCkBg9WlarS0QVE7lUSG6SquiT/hzPz9TvP%2b3Yw48BrU04JltH76rboR07zDvgMy3sBk32hKb8w2qbBk5Vo3xVRqwS8Z6fUm1Zl9BnRXr1/kIFP58dLbkwraq%2bD4/%2bbtAMb4F7LB8c%2b1jihZU3vHI7gvpQTtrLr4z%2bqtm8C8NN4rkcbT77QLfKM%2bMCqRhSFzoGyy9Qs%2bhO3Xi34tHUh6oANU3FPP/Cd%2b3/B6w%2bw9W13ecESXG8H6w374I64UiWRQRSnWqciO%2b4BVhkdRtfOF2d7UCw9zL/vqwrTcMgUtbCPtbD1Wj/ucxvut5oeeBPlfPicGz0Ohr9FI0C9j1myRP6DZ6Uv2SlomPnNhrY/z7C6SbjkSF9CLvOhN92XA9OklEkm1HOIm7uFhQ5JL1Fv60muZW/ifqjUrR2kkhUv/LA4nixpcjUpOYRdbT2O7/wIXs7jhInwlFVACx3TL4KrRO6Zb79uGJJdMlHdWREfPr81dxJ9G8u%2bYNYz2djvrbTV%2b/BgZxMRmVugXLH7%2bQRAOhozyvDm5XMnGusjga/NLBFQO4A341puj2QzTdG2R%2bzUexFLeXc2TZ5XgrKqVvjIKeNYPECSKDiZZc%2bj1ivpQt4bUNS2Nx7Hup2%2bUhiwxA8pVtxiVY0YU3QWvphUXUSdu5nFp7qOGz0Yy9m5wOU4kIKyIlJnEeaKVMTETd7TtzhQ5sYEATKpzGUrezaaHei0%2bbkRjGAgK7q8/Wkb/tueJZ2af3IeHOeyulA/%2bHRpvDDMzS0AiGvhvLMVu7PNUKpIKrGuPgIPuXTy6N2WoYQiewnAKekaS03DukE2g%2bTTMFVJ2OXUBpF5MHxAs68NoJCw0s/UgdzKaYWUHH6dS9AjMiYx0eSS7RtUf6bbgMZkLQzL%2bW4CV8gRvacJ7Jn0bsg3zZBGt/8CaVfhNpXU4g3MqAYz9w0iinx0mUPgP7%2bYwf2D2Qg/KkGeY8Qg3sJH4T7oEjs4PjeqbYpPGxjQZd7Uhlv%2b8TkorNmfGXBrl6M9Deow3lWGX/zl1u9uH%2bbNTiSRJAb1dGqqiVtGE3j1Ld/RRgxCI9/TB93dBdovtdKVmhZyjfsIWQS5ypneaYgFbv/l0WBG8GvwGs3QBbYDQbNC8lF5OQ7OS0xCYWBecHqvOlEjU68Yb8crUe3f0q9YXkDum%2b3OlDwmg/SQylqrmO9taYI%2brU8JByMu/ZnTjiWPMOyUx9Codsj5ml6PxMK3OcZHBj7G9BLJJz4XHPisOZxt0LUjKMeH/0itQmxeEnPn%2blvcOWp%2bYNsF6UjmYdnKkSEd9s61jN9lPwCB3m10w%2bRWEIPbbgm6Gn/Yelf1dd4T%2b4e70tJeTWI%2bImR%2be97HxOLEyw3D7aCSzS0LSzTvtGsdk7XrvmiYTzK3aR/i/SQypZCNgpi4vncxLAQ8iJ9xT541Z0gqhWYfJB7XS087RhmnhGfwISZ5rkjYBZPr12Ho5M4xaRKfpu2qWgbnvuBBqMNJWu5JOPMbgVMm3C3DzcRnTs9oVnI81go4j1yJ0tOAguj8iviikAk1/2s3EIgFGnA6gxSZV6XlDwhqpBFzMwQNne/O0bvezmLwQ/Rx%2bjbNSo8ZhmdQ%3d%3d";
		byte[] res = null;
        String code = "";
        input = strUtils.urlDecode(input);

        Pattern pattern = Pattern.compile("([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})_(.*)");
        Matcher matcher = pattern.matcher(input);
        if (matcher.find()) {
            code = matcher.group(2);
        }
        if( code == null || code == "") {
            return res;
        }
		try {

            byte[] encryptData = strUtils.base64Decode(code.getBytes(StandardCharsets.UTF_8));

            byte[] key = "changeit".getBytes(StandardCharsets.UTF_8);
            byte[] iv = aes.generateRandomBytes(16);
            
            String mode = "CBC";
            String padding = "PKCS7Padding";

            byte[] result = aes.decrypt(
                    encryptData,
                    key,
                    iv,
                    mode,
                    padding
            );
            
            aes.mode_AES.set(mode);
            aes.padding_AES.set(padding);
            aes.key_AES.set(Arrays.toString(key));
            aes.iv_AES.set(Arrays.toString(iv));
            res = result;
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return res;
    }
}

2、Tips:

你们直接运行不了,很多都是单独封装的模块,可以根据文中意思自己大概写一下。Sorry~ 娜扎

0x04、分析原因

  • 1、自己写解密算法 / 直接使用cas工程的相关jar包、java文件,调用解密函数

  1. 工程内,自己已经使用了基于java标准API的crypto封装了更完善的AES加解密方法,并且支持自动检测结果是否存在gzip\class\反序列化特征并再处理,不同于Cryptacular,支持低级别的数据加密。
  2. 作为高级软件开发工程师,怎么能容忍自己的代码变成屎山呢?
  • 2、为什么会解密失败?

  1. 突然想起来,自己傻Der了~,Aes的Key怎么可能是changeit,Key应该是16位
  2. 全网都在提 “changeit” 硬编码,以下代码,被误导了:
public EncryptedTranscoder() throws IOException {
    final BufferedBlockCipherBean bufferedBlockCipherBean = new BufferedBlockCipherBean();
    bufferedBlockCipherBean.setBlockCipherSpec(new BufferedBlockCipherSpec("AES", "CBC", "PKCS7"));
    bufferedBlockCipherBean.setKeyStore(createAndPrepareKeyStore());
    bufferedBlockCipherBean.setKeyAlias("aes128");
    bufferedBlockCipherBean.setKeyPassword("changeit");
    bufferedBlockCipherBean.setNonce(new RBGNonce());

    setCipherBean(bufferedBlockCipherBean);
}
  1. 因为没有用过Cryptacular库,学习一波才明白,使用"changeit"这个pass初始化密钥库,然后根据Alias(“aes128”)这个keyName获取Aes真正的key。
方法作用
setKeyStore初始化密钥库
setKeyAlias设置获取密钥类型
setKeyPassword设置密钥库密码(changeit)
  1. 那必然是需要部署相关jar包、java文件调试了(Tips:直接使用网上exp工具项目,他们已经抽取完毕了)

0x05、断点调试

  1. java中进行AES解密,基本上所有第三方包,都会进入AES解密时的java标准api
方法作用
cipher.init()初始化密码(Cipher)对象,需要传入加密的 mode / key / iv
cipher.doFinal()执行加密或解密操作
  1. 想要获取key及iv向量,关键的就在cipher.init(),找到位置打断点获取key/iv的值,但是无法直接全局搜索cipher.init(因为该方法在jar包中,编辑器不支持搜索jar中class的内容)
  • 手动点开jar,根据可能的文件名点开查看寻找;(不推荐)
  • 调用外层java中的解密方法,手动根据方法调用找方法(如果开发者再次封装的不繁杂的话可以)
  • 调用外层java中的解密方法,打断点先大跳(步过)粗略走流程,然后根据可能位置逐步步入判断
  1. 采用了第二种方案,结果发现封装的裂开,很容易走错。采用第三种方案:

最终在找到在BufferedBlockCipherBean.class中,调用到了cipher.ini()

Tips:在步入得到过程中,有的方法[步入]功能不进去,需要使用[强制步入]功能

在这里插入图片描述

0x06、代码分析

1、key/iv初始化函数确定

key初始化方式的确如之前分析的根据keyName在密钥库中寻找lookupKey

key初始化位置在:params = new ParametersWithIv((cipherParameters)params, header.getNonce())

通过打断点获取到了key真正的值(byte[]):[78, -47, -80, -25, 76, 55, -57, -111, -81, -3, -54, 62, 118, 15, 113, 0]
在这里插入图片描述

iv初始化位置在:params = new ParametersWithIv((cipherParameters)params, header.getNonce())

这一步会获取IV向量值存放在params中,通过多次断点调试,发现针对于相同密文,他的IV是不变的,不同密文,他的IV值是会变的。因此得到一个结论:

key是固定的,iv是根据密文计算得来的

2、key初始化函数分析

  • keys固定的,就不带着函数挨个进了,流程如下:

读取配置文件中的配置;默认无,则使用"changeit"这个固定pass初始化解密读取默认密钥库(spring-webflow-client-repo-1.0.0.jar!/etc/keystore.jceks),然后根据Alias(“aes128”)这个keyName遍历获取获取Aes真正的key

3、iv初始化函数分析

在这里插入图片描述
代码解读:

  • 1、将原始密文读取如bb变量; // 如 [0,0,0,34,0,0,0,16,…]
  • 2、大端序重新排列; // 不变
  • 3、从bb根据第二部的大端序读取带4个字节兵解释为带符号的整数值,作为header头部长度; // 34
  • 4、创建byte[] nonce,大小为iv长度标识; // byte[]{0,0,0,0,0,0…} 大小为16
  • 5、从bb的iv长度标识后中读取对应长度放入nonce;
  • 6、header头部处理完剩余的部分放入keyName字段; // aes128

0x07、总结

以此可以推断出,密文的实际构造情况为

UUID + _ + 密文

UUID + _ + header头部(34byte) + AES密文

UUID + _ + 头部长度标识(7byte) + iv长度标识(1byte) + iv值(16byte) + keyName(10byte) + AES密文

运行通过

package com.hotboy.content.blueTeam;

import com.hotboy.utils.aesUtils;
import com.hotboy.utils.strUtils;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Potato
 */
public class casDecrypt {

    public static byte[] decrypt(String input,aesUtils aes){
        byte[] res = null;
        String code = "";
        input = strUtils.urlDecode(input);

        Pattern pattern = Pattern.compile("([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})_(.*)");
        Matcher matcher = pattern.matcher(input);
        if (matcher.find()) {
            code = matcher.group(2);
        }
        if( code == null || code == "") {
            return res;
        }

        try {

            byte[] encryptData = strUtils.base64Decode(code.getBytes(StandardCharsets.UTF_8));

            byte[] key = {78, -47, -80, -25, 76, 55, -57, -111, -81, -3, -54, 62, 118, 15, 113, 0};
            byte[] iv = new byte[16];
            System.arraycopy(encryptData, 8, iv, 0, 16);
            String mode = "CBC";
            String padding = "PKCS7Padding";

            // 剔除header头部34个标志性字节
            byte[] tmpEncryptData = new byte[encryptData.length - 34];
            System.arraycopy(encryptData, 34, tmpEncryptData, 0, encryptData.length - 34);
            encryptData = tmpEncryptData;

            byte[] result = aes.decrypt(
                    encryptData,
                    key,
                    iv,
                    mode,
                    padding
            );

            aes.mode_AES.set(mode);
            aes.padding_AES.set(padding);
            aes.key_AES.set(Arrays.toString(key));
            aes.iv_AES.set(Arrays.toString(iv));

            res = result;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return res;
    }
}

运行成功,成功获取源代码:

在这里插入图片描述


感谢Allan、Songqb、小严同学,虽然没给我解决任何问题:)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1015828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业级数据仓库-数仓实战

数仓实战 安装包大小 安装清单 环境搭建 一、环境搭建01(机器准备) 准备好三台虚拟机,并进行修改hostname、在hosts文件增加ip地址和主机名映射 。 1、设置每个虚拟机的hostname vi /etc/sysconfig/network 修改HOSTNAMEnode02修改hostna…

PY32F003F18之输入捕获

输入捕获是定时器的功能之一,配合外部引脚,捕获脉宽时间或采集周期。 CPU中的定时器最基本的功能就是计数功能,其次是输入捕获(IC),再次就是比较输出(OC),还有就是使用引脚对外部时钟进行计数,触发信号捕捉…

6- 华为云查看容器日志

1 查看位置 二 进入容器查看 ls cat main.py # 退出命令是 exit() 或者 quit() cat main.py 在docker使用该命令进入文件后的退出命令

Mapbox gl HTML经纬度点渲染,动态轨迹播放,自定义图形以及轨迹上显示箭头方向

Mapbox gl HTML经纬度点渲染,动态轨迹播放,自定义图形以及轨迹上显示箭头方向 1. 效果图2. 源码2.1 line.html2.2line_arrow.html 参考 今天要排查个问题,需要显示多个经纬度点连接成线段的方向,于是尝试下展示。 1. mapbox渲染经…

element plus封装el-select添加后缀图标并添加远程搜索和对话框功能

当提交的表单Form需要填某个实体的外键ID时,当然不可能使用el-input组件,这个适合提交字符串,然后用户又不可能记住某个引用的外键ID,这时候使用el-select还是必要的。 el-select组件一般都作为下拉选择框使用,但仅在…

Java实现通过文字生成图片

一、前言 在实际应用中,我们可能需要将用户姓名作为头像显示,那么我们可以通过Java来实现。 二、如何实现 1.定义一个工具类,代码如下: import org.slf4j.Logger; import org.slf4j.LoggerFactory;import javax.imageio.ImageIO…

三分钟使用ngrok实现内网穿透

1.官网注册 官网地址:https://ngrok.com/ tips:若使用邮箱注册自行认证 2.下载对应部署电脑 压缩包(此处笔者使用自己电脑因此以Windows11作为案例) 解压下载的ngrok压缩包,在对应目录进入命令提示符装口(也可直接…

竞赛 基于机器视觉的银行卡识别系统 - opencv python

1 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的银行卡识别算法设计 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng…

基于 I2C 协议的 AD实验(附代码)

目录 1. 理论学习 1.1 AD介绍 1.2 I2C 简介 1.2.1 I2C物理层 1.2.2 I2C协议层 1.3 PCF8591芯片简介 1.3.1 引脚信息 1.3.2 功能描述 2. 实验 2.1 硬件资源 2.2 模块框图 2.3 程序设计 2.3.1 工程整体框图 2.3.2 I2C驱动模块 1. 模块框图 2. 波形图分析&#xf…

来看看Python MetaClass元类详解

MetaClass元类,本质也是一个类,但和普通类的用法不同,它可以对类内部的定义(包括类属性和类方法)进行动态的修改。可以这么说,使用元类的主要目的就是为了实现在创建类时,能够动态地改变类中定义…

Docker网络学习

文章目录 Docker容器网络1.Docker为什么需要网络管理2. Docker网络简介3. 常见的网络类型4. docker 网络管理命令5.两种网络加入差异6.网络讲解docker Bridge 网络docker Host 网络docker Container 网络docker none 网络 Docker容器网络 1.Docker为什么需要网络管理 容器的网…

Linux启动速度优化方法总结

文章目录 一、启动耗时统计printk timeinitcall_debugbootgraphbootchartgpio示波器 二、内核优化方法kernel压缩方式加载位置内核裁剪预设置lpj数值initcall优化内核initcall_module并行减少pty/tty个数内核module 三、其他优化ubootXIP 四、总结 要对Linux系统启动速度进行优…

Discuz论坛网站标题栏Powered by Discuz!版权信息如何去除或是修改?

当我们搭建好DZ论坛网站后,为了美化网站,想把标题栏的Powered by Discuz!去除或是修改,应该如何操作呢?今天飞飞和你分享,在操作前务必把网站源码和数据库都备份到本地或是网盘。 Discuz的版权信息存在两处…

七、安卓手机环境检测软件分享

系列文章目录 第一章 安卓aosp源码编译环境搭建 第二章 手机硬件参数介绍和校验算法 第三章 修改安卓aosp代码更改硬件参数 第四章 编译定制rom并刷机实现硬改(一) 第五章 编译定制rom并刷机实现硬改(二) 第六章 不root不magisk不xposed lsposed frida原生修改定位 第七章 安卓…

生信教程|最大似然系统发育推断

动动发财的小手,点个赞吧! 简介 顾名思义,最大似然系统发育推断旨在找到进化模型的参数,以最大化观察手头数据集的可能性。模型参数包括树的拓扑结构及其分支长度,还包括推理中假设的替代模型(例如HKY或GTR…

09MyBatisX插件

MyBatisX插件 在真正开发过程中对于一些复杂的SQL和多表联查就需要我们自己去编写代码和SQL语句,这个时候可以使用MyBatisX插件帮助我们简化开发 安装MyBatisX插件: File -> Settings -> Plugins -> 搜索MyBatisx插件搜索安装然后重启IDEA 跳转文件功能 由于一个项…

Linux用户和用户组信息管理

文章目录 用户管理用户密码信息/etc/shadow详解 ⽤户组的管理(切换到root)/etc/group 内容详解用户组的添加用户组的删除用户组的查看用户组的修改 ⽤户组和⽤户的关联 用户管理 ⽤户的管理(/etc/passwd) ⽤户的添加(useradd) ⽤户的删除(us…

Spring boot原理

起步依赖 Maven的传递依赖 自动配置 Springboot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。 自动配置原理: 方案一…

MySQL——一、安装以及配置

MySQL 一、windows下的安装以及配置常规方法二、windows下的安装以及配置简单方法三、Linux下的数据库安装以及配置 一、windows下的安装以及配置常规方法 准备工具: 链接:https://pan.xunlei.com/s/VNeRbKScnTd6MbgZ-jwubY6-A1?pwdtaxz# 这里我准备的…

sync.Once-保证运行期间的某段代码只会执行一次

初入门径 sync.Once提供了保证某个操作只被执行一次的功能,其最常应用于单例模式之下,例如初始化系统配置、保持数据库唯一连接,以及并发访问只需要初始化一次的共享资源。 单例模式有懒汉模式和饿汉模式两种 饿汉模式 顾名思义就是比较饥饿,所以一上来(服务启动时)…