JSP Webshell Cookbook: Part-1

jdlkajflkd
7 min readAug 30, 2020

--

Preface

Recently I have been learning various of principles and implementation technologies about JSP webshell. I’ll talk about these technologies through series of articles. Now let’s get it!

What’s JSP?

Java Server Pages (JSP) is a server-side programming technology that enables the creation of dynamic, platform-independent method for building Web-based applications. JSP have access to the entire family of Java APIs.

.jsp -> .java -> .class

When a JSP file that in Web Server is accessed for the first time, it will be translated into a Java source file(.java) by Web Server, such as Tomcat. Then the Java source file will be compiled into a java bytecode file(.class) by JDK, and finally the java bytecode file will be loaded to memory by JVM.

The difference with PHP is that JSP is a language with strict language features. Such as there is no eval function in JDK(before JDK 9). So It's more difficute to make JSP webshell variant that avoiding killing. But there are still a lot interesting magics to make JSP webshell variant.

1. Implement via commonly used classes and methods

There are some commonly used classes and methods that used to implement a webshell. I’ll show you as below.

1.1 Simplest and the most direct way

  • java.lang.Runtime#exec
  • java.lang.ProcessBuilder

1.2 Use reflection mechanism

Reflection is very powerful mechanism which can make dynamic loading classes. Because it can load class depending on the class name that you specify, and so we can hide the malicious class name such as Runtimevia encrypting the class name string.

1.3 Load the java bytecode

The class ClassLoader is used to load java bytecode into a Classobject. There are three key methods invoked in it

  • loadClass : load a class according to the class name you specify. In the first place, it invokes recursively its parent’s loadClass() to determine if the class loaded. And if not, it will call findClass() .
  • findClass : load a class according to the class name you specify into a byte-array, and call defineClass() .
  • defineClass : load the bytecodes into Class object.

1.3.1 one-sentense webshell via defineClass

There is no API provided to load a bytecode array in JDK. But there is a protectedmethod name defineClass() in internal of class ClassLoader . So we have two ways to achieve our purpose:

(1) Define a custom class that extends ClassLoader ,and define a method that invoke super.defineClass()

(2) make it through reflection mechanism.

For example, Behinder, a famous webshell manage tool implements a one-sentense jsp webshell via defineClass . Its source shown as below:

So, I wrote a webshell via defineClass just like Behinder, but there is no traffic encryption through AES in mine.

The value of param ladypwd is bytecodes of a custom class while bytecodes are encoded with Base64. The define of the class is just like shown as below.

1.3.2 via BCEL bytecode

  • com.sun.org.apache.bcel.internal.util.ClassLoader

In com.sun.org.apache.bcel.internal.util.ClassLoader , there is a method namedloadClass . It will transform BCEL bytecodes into standard java bytecodes if its param class_name contains $$BCEL$$ string, and call methoddefineClass to obtain a Class object.

Now have a look at a webshell shown as below which implemented via loading BCEL bytecodes.

1.3.3 load java bytecode from remote jar via URLClassLoader

URLClassLoader is one of subclasses of ClassLoader , it usually used to load classes via local directory or remote url you specify.

An example of webshell implemented to load a remote class by using URLClassLoader .

1.3.4 load java bytecode from local .class file via URLClassLoader

If you want to use URLClassLoader to load a local class file to implement a webshell, you should write a java source file to server side, and compile it in server side. Finally load the .class file through URLClassLoader .

An example shown as below:

1.4 Expression-related classes

1.4.1 ScriptEngineManager#eval()

If you want to make it communicate between Java and JavaScript, you can use javax.script.ScriptEngineManager . There is no eval method in Java, but inScriptEngineManager . ScriptEngineManager#eval() can invoke Java objects and methods.

An example of webshell implemented via ScriptEngineManager#eval .

1.4.2 Tomcat EL (Expression Language) -> ELProcessor#eval()

ELProcessor also has eval() method that can invoke directly Java object to execute command.

An example of webshell implemented via ELProcessor#eval .

PS: ELProcessor is in el-api.jar of Tomcat.

1.4.3 Expression#getValue()

1.5 via deserialization

1.5.1 Overwrite ObjectInputStream#resolveClass()

An example of webshell implemented via Java object deserialization.

Demo4.java

As shown in screenshot above, maybe you would ask that why don’t you override the method readObject of ObjectInputStream in Custom ? Because the method readObject of ObjectInputStream is final :

When Custom#readObject is invoked, the methodresolveClass of ObjectInputStream would be invoked, meanwhile it return a Classobject which will be used to deserialization. Besides, the method ObjectInputStream#resolveClass can be overrided, so I choose to override the method resolveClass in classCustom .

The call stack of methods of deserialization shown as below:

Custom#readObject()
->ObjectInputStream#readObject()
->ObjectInputStream#readObject0()
->ObjectInputStream#readOrdinaryObject()
->ObjectInputStream#readClassDesc()
->ObjectInputStream#readNonProxyDesc()
->ObjectInputStream#readClassDescriptor()
->Custom#resolveClass()
->ObjectInputStream#readSerialData()
->ObjectStreamClass#invokeReadObject()
->Demo4#readObject() <--readObjectMethod.invoke(obj,new Object[]{ in});

1.5.2 XMLDecoder ( //TODO)

1.5.3 XSLT (//TODO)

1.6 via JNDI Injection

You can read my previous article to learn about JNDI Injection.

An example of webshell implemented via JNDI Injection shown as below:

About this way of implementation, what you need to know is, it’s also restricted by JDK version even if you set property com.sun.jndi.ldap.object.trustURLCodebase to true .

Because when Tomcat server starts and JDK version ≥ 8u191, it will assign the value of property com.sun.jndi.ldap.object.trustURLCodebase (default: false) to a static variable named trustURLCodebase , and you can’t modify its value.

So when you use JNDI Injection to lookup a remote class, it will check the static variable trustURLCodebase if it’s true. From this point of view,the value of trustURLCodebase will be always false

1.7 via JNI

JNI (Java Native Interface) is provided for communication between Java and C/C++. So you can write C/CPP code to executes system commands, and invoke it in Java.

Java code:

public class CommandExecution {
public static native String exec(String cmd);
}

CPP code:

Compile the cpp code above into a binary file, then read and encode the binary file contents with Base64, and pass it as parameter to webshell.

An example of Webshell implemented via JNI shown as below:

1.8 via JShell (JDK ≥ 9)

The Java Shell tool (JShell) is an interactive tool for learning the Java programming language and prototyping Java code. JShell is a Read-Evaluate-Print Loop (REPL), which evaluates declarations, statements, and expressions as they are entered and immediately shows the results. The tool is run from the command line.

An example of one-sentence webshell via JShell shown as below:

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

Reference

[1]https://github.com/rebeyond/Behinder

[2]https://github.com/threedr3am/JSP-Webshells

[3]https://javasec.org/javase/JNI/

[4]https://mp.weixin.qq.com/s/XZvQgh6g69AUNUi_QK9FbQ

--

--

jdlkajflkd

A low-level hacker who focuses on vulnerability research.😝