Mandiant Inc.

06/30/2022 | Press release | Archived content

Get Your Kicks on Route Sixty-Sink: Identifying Vulnerabilities Using Automated Static Analysis

Introduction

Today, we are releasing Route Sixty-Sink, an open-source tool that enables defenders and security researchers alike to quickly identify vulnerabilities in any .NET assembly using automated source-to-sink analysis. Route Sixty-Sink has already been used to find and exploit dozens of critical security issues, an example of which will be discussed in this blog post.

Background: Source-to-Sink Analysis

Identifying vulnerabilities within application binaries or source code is often a long and tedious process. To help with this, source-to-sink analysis is used-a form of data flow analysis that attempts to identify user input (a source) that is passed as the argument of a function call of interest (a sink).

A source is an entry point where user input enters an application and a sink is an action performed by the application, using user input from the source. When performing vulnerability research, sinks can be thought of as dangerous function calls that are executed using input from a source. Tables 1 and 2 provide several examples of sources and sinks.

Sources

Table 1: Common sources

Source

Example Scenario

System.Web.HttpRequest::get_QueryString

A web application that parses a GET parameter from a URL

System.IO.File::Open

A console application that parses input from a file editable by an unprivileged user

Microsoft.AspNetCore.Mvc.HttpPutAttribute

A web application route that accepts a PUT request with user input

Sinks

Table 2:Common sinks

Source

ExampleScenario

System.Diagnostics.Process::Start

An application spawns a new process to execute a command. This could allow for command injection.

System.Web.HttpPostedFile::SaveAs

A web application saves a file to disk. This could allow for uploads of web shells or other malicious files.

System.Net.WebClient::DownloadFile

A web application fetches a file from another web page. If the URI is user controlled, SSRF may be possible.

By enumerating a list of sinks, identifying them within an application, and backtracking them to user-controlled input, source-to-sink analysis can effectively identify vulnerabilities. As a straightforward example, imagine a .NET web application with an API route handler that looks like this:

using System.Diagnostics; 
using System.Web; 

public class HomeController : Controller 
    { 
        public IActionResult Index() 
        { 
            string arg = HttpUtility.ParseQueryString(myUri.Query).Get("q"); 
            Process.Start("getHealth.exe", arg); 
            return View(); 
        } 
    } 

Assuming the System.Diagnostics.Process.Start()API call was a sink we were interested in using, we would:

  1. Search the code base for the System.Diagnostics.Process.Start()function
  2. Identify the qquery parameter
  3. Identify the API controller and route (/home?q=)
  4. Analyze the user-supplied argument (q) passed to the sink and determine whether it introduces a vulnerability
  5. Craft an exploit to obtain command injection (GET /home?q=)

What Does Route Sixty-Sink Solve?

Although it is effective, proper source-to-sink analysis is a time-consuming and manual process that is often infeasible. The following table identifies the main three pain points with manual static analysis and explains how Route Sixty-Sink helps resolve them.

Table 3:Source-to-sink problems and solutions

Problem

Manual Analysis

Route Sixty-Sink

Complex Input

Tracing

Identifying an application's inputs can be difficult, especially in web applications where MVC architectures are used

Route Sixty-Sinkhandles a wide variety of routing and input parsing scenarios to automate this process by programmatically identifying endpoints

Application Size

Large C# applications are often infeasible to obtain full code coverage of using manual analysis

Route Sixty-Sinkautomates this process to allow analysis of most programs within seconds

Nested Sinks

Sinks may be overlooked that are hiding within interfaces, extended classes, or a series of nested function calls

Route Sixty-Sinkidentifies these sinks by creating a call graph of all classes and method calls and then recursively tracing the call flow to nested sinks

How Does it Work?

Overview

Route Sixty-Sinktraces the flow of user input through any .NET assembly and determines whether it is passed as an argument to a dangerous function call (a "sink"). Route Sixty-Sinkdoes this using two main modules:
  1. RouteFinder, which enumerates API routes in ASP.NET Core MVC and classic ASP page web applications.
  2. SinkFinder, which takes an entry point and creates a call graph of all classes and method calls. Then, it queries strings, method calls, and class names for sinks.

RouteFinder

RouteFinder quickly parses all identified route entry points exposed by a web application.

Input

As input, RouteFinder takes a compiled .NET web application assembly or a directory containing assemblies that make up the web application. RouteFinder supports the following routing:

  1. ASPX, ASMX, ASHX, and ASCX Pages
  2. ASP.NET Core MVC Routing
    1. Attribute Routing
    2. Conventional Routing (Basic default convention)

Output

RouteFinderoutputs each assembly, controller, prefix, route, action, and authorization policy for all identified API endpoints within the application. An example is shown in Figure 1:Figure 1: RouteFinder route parsing

SinkFinder

Given an entry point, SinkFinder recursively follows method calls within an application to find a user-defined list of "sinks." It uses the following algorithm to do so:

  1. Enumerate classes and methods from input and dependency C# binaries and store them in a call graph
  2. Parse Common Intermediary Language ("CIL") instructions starting with the entry point and identify all method calls
  3. If a sink is found, inform the user
  4. For each method call:
    1. Look up the method within the call graph and access it
    2. Repeat steps 2-3

Input

As input, SinkFinder takes a compiled .NET assembly or directory of assemblies. It also optionally accepts an additional .NET assembly or directory of assemblies to use as dependencies. The dependencies will be loaded into the call graph and their functions will be traversed to search for sinks.

Output

If any sinks are found, SinkFinder outputs a complete call graph of all method calls that lead to the sink. This allows the researcher to quickly identify the data flows resulting in a sink call.

Figure 2 shows an example of a SinkFinder call graph ending at a sink. Notice how SinkFinder can identify a sink through multiple nested function calls and third party dependencies. This would be very difficult to do using traditional static analysis approaches.

Figure 2: SinkFinder identifying a nested sink

Using Standalone SinkFinder to Query an Assembly

Let's say you have a C# executable, and you'd like to determine whether it calls dangerous functions that could allow for local privilege escalation. This process becomes very simple using SinkFinder.

First, we would specify a list of sinks to search for in JSON format. A list of common sinks that will get most security researchers off to a great start can be found within the sinks.jsonfile of the Route Sixty-SinkGitHub repository. We run SinkFinder, and bam! We've already got two great leads for a local privilege escalation.

Figure 3: Finding sinks with standalone SinkFinder

Route Sixty-Sink: Combining RouteFinder and SinkFinder

The real magic happens when the RouteFinder and SinkFinder modules are combined to create Route Sixty-Sink-a tool that performs comprehensive source-to-sink analysis from API routes to sinks within seconds, allowing security researchers to spend less time reviewing source code and more time discovering vulnerabilities.

Figure 4: Route Sixty-Sink harnesses the combined power of RouteFinder and SinkFinder

Case Study: Using Route Sixty-Sink to Find a Real World Vulnerability

About six months ago, we tested a large web application using the .NET MVC architecture for a client who had provided us with the application's compiled assemblies. Before we dive into the vulnerability we found and how Route Sixty-Sink helped identify it, we need a small amount of background information.

Insecure Deserialization in Newtonsoft.Json

The Newtonsoft.Json.JsonConvert::DeserializeObject()function is a sink we commonly hunt for (and is included within our starter sinks.jsonlist). This function instantiates an object using properties specified in a passed JSON string. This process is known as deserialization, and if it is not performed safely, critical vulnerabilities may be introduced that can lead to arbitrary object instantiation resulting in remote code execution. Muñoz and Miroshprovide an excellent overview to unsafe deserialization vulnerabilities.

Default deserialization with Newtonsoft.Json.JsonConvert::DeserializeObject()is normally safe, as it will not instantiate any object other than a specified expected type. However, the Newtonsoft library allows developers to pass in a special Newtonsoft.Json.JsonSerializerSettingsobject as a parameter which can allow for arbitrary objects to be instantiated. Table 4 shows an unsafe Newtonsoft.Json.JsonSerializerSettingsobject being passed to the deserialization call which introduces a vulnerability if user input ends up within the JSON string.

Table 4:Unsafe Deserialization
var deser = JsonConvert.DeserializeObject(json, new 
    JsonSerializerSettings 
        { 
            TypeNameHandling = TypeNameHandling.All 
        });

Finding Unsafe Deserialization with Route Sixty-Sink

With this in mind, we crafted a regex query shown in the figure below in order to identify calls to Newtonsoft.Json.JsonConvert::DeserializeObject()that accepted a Newtonsoft.Json.JsonSerializerSettingsobject as a parameter.

Table 5:Regex for identifying unsafe deserialization
Newtonsoft.Json.JsonConvert::DeserializeObject.*JsonSerializerSettings

Using the custom regex, we then ran Route Sixty-Sink on the compiled MVC application. As shown in Figure 5, Route Sixty-Sink quickly identified an API endpoint which called a function that matched the regex we built.

Figure 5: RouteFinder parsing MVC routes

Notice that Route Sixty-Sink conveniently identified both the controller (Discussions) and the assembly (BaseCommon.dll) and composed a call graph of all methods in the controller, one of which eventually led to the execution of our sink.

Next, we popped BaseCommon.dllinto DnSpy, a useful .NET decompiler, and located the specific class and method that executed our sink. The decompiler output is shown in Figure 6.

Figure 6: Unsafe deserialization of user input

As shown, the application took the DiscussionContextparameter from the POSTrequest and deserialized it unsafely! We were then able to use ysoserial.netto generate a JSON payload that, when deserialized, would issue a DNS request to a server controlled by us. Our final exploit is shown in Figure 7.

Figure 7: Deserialization RCE Payload

A few moments later, we received a callback to our DNS server, proving that we had executed code on the application server.

Figure 8: DNS callback proving code execution

Contributing

The decision to release Route Sixty-Sink was made to allow the security community to both reap its benefits and continue its growth. The following sections explain the highest priority next steps for Route Sixty-Sink.

Use Route Sixty-Sink!

Our biggest hope for Route Sixty-Sink is that researchers and developers to use the tool to allow us to identify its strengths, shortcomings, and to make improvements.

Adding to the sinks.jsonList

Provided with Route Sixty-Sink is a sinks.jsonfile containing a list of common .NET sinks that have been commonly used to discover vulnerabilities. We hope the security research community will continue to add to this file in order to improve Route Sixty-Sink's sink detection capabilities.

Advanced Source-to-Sink Analysis

We hope to add additional features to Route Sixty-Sink in future releases, including:

  • Parameter parsing for use as sources
  • Advanced taint analysis
  • Symbolic execution
  • Low/High vulnerability fidelity assessment based on source-to-sink depth, intermediary validation, and/or sanitization functions
  • Identification of non-web-based user-controlled inputs, including data from IPC mechanisms, data on disk, sockets, etc.

Additional Development

We designed Route Sixty-Sink with a modular design in order to encourage community collaboration and continued development. A researcher who wishes to define a new type of source can easily add it to Route Sixty-Sink and benefit from automated source-to-sink analysis.

For example, one might implement a module that identifies all .NET deserialization sources. Then, it would be possible to connect the module to SinkFinder to quickly identify interesting gadget chains.

If you have an idea for a new module, please reach out to us or submit a pull request!

Conclusion

We have introduced Route Sixty-Sink, an open source utility to perform automated source-to-sink analysis within .NET binaries. We have already used Route Sixty-Sink to identify dozens of issues in open-source and proprietary web applications, and we hope it will continue to identify more issues in order to improve the state of application security worldwide. We urge the reader to use and contribute to this project!

GitHub Repository

Our GitHub repository can be found at https://github.com/mandiant/route-sixty-sink