JSP Webshell Cookbook: Part-1
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 Runtime
via encrypting the class name string.
1.3 Load the java bytecode
The class ClassLoader
is used to load java bytecode into a Class
object. 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’sloadClass()
to determine if the class loaded. And if not, it will callfindClass()
.findClass
: load a class according to the class name you specify into a byte-array, and calldefineClass()
.defineClass
: load the bytecodes intoClass
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 protected
method 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 Class
object 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