Unveiling RIFT: Enhancing Rust malware analysis through pattern matching | Microsoft Security Blog

Compatibilità
Salva(0)
Condividi

Today, Microsoft Threat Intelligence Center is excited to announce the release of RIFT, a tool designed to assist malware analysts automate the identification of attacker-written code within Rust binaries. Known for its efficiency, type safety, and robust memory safety, Rust has increasingly become a tool for creating malware, especially among financially motivated groups and nation-state entities. This shift has introduced new challenges for malware analysts as the unique characteristics of Rust binaries make static analysis more complex.

One of the primary challenges in reverse engineering malware developed with Rust lies in its layers of abstraction added through features such as memory safety and concurrency handling, making it more challenging to identify the behavior and intent of the malware. Compared to traditional languages, Rust binaries are often larger and more complex due to the incorporation of extensive library code. Consequently, reverse engineers must undertake the demanding task of distinguishing attacker-written code from standard library code, necessitating advanced expertise and specialized tools.

To address these pressing challenges, Microsoft Threat Intelligence Center has developed RIFT. RIFT underscores the growing need for specialized tools as cyber threat actors continue to leverage Rust’s features to evade detection and complicate analysis. The adoption of Rust by threat actors is a stark reminder of the ever-changing tactics employed in the cyber domain, and the increasing sophistication required to combat these threats effectively. In this blog post, we explore how threat actors are increasingly adopting Rust for malware development due to its versatility and how RIFT can be used to combat this threat by enhancing the efficiency and accuracy of Rust-based malware analysis.

Threat actors continue adopting Rust

As Rust gains popularity as a rapidly growing programming language, its use by malware authors is becoming more noticeable. Over the past five years, Microsoft Threat Intelligence Center and the broader security industry have observed financially motivated and state-supported groups increasingly using Rust for malware development.

Figure 1. Timeline of Rust-based threats

In 2021, the group behind the notorious BlackCat ransomware was among the first significant entities in the ransomware field to write their malicious programs in Rust. Following the appearance of the first malware families written in Rust, reverse engineers indicated that such malware presents a unique challenge for analysis.

Subsequently, several other groups began developing or rewriting their tools in the programming language. Nation-state threat actors have also selectively developed their malware in Rust.

Rust is a versatile language known for its performance, type safety, concurrency, and memory safety. While these features benefit legitimate development, they also complicate static analysis of malicious files. The community has extensively addressed many of these challenges. One of the core issues in analyzing Rust binaries is differentiating between library code and code written by malware authors.

To illustrate the significance of this problem, Microsoft Threat Intelligence Center conducted a simple experiment. A small PE EXE file that downloads data from a website and saves it on disk as sample_data.txt is generated with Microsoft 365 Copilot. The program is first compiled in C++ and then in Rust. The C++ program is compiled using Microsoft Visual C++ (MSVC) with Visual Studio 2022, in release mode for the 64-bit architecture and dynamically linked, using default settings. The Rust binary is compiled using compiler version rustc 1.89.0-nightly (16d2276fa 2025-05-16), also in release mode and with default settings.

Figure 2. Simple downloader program in C++ to the left and Rust to the right

Next, both programs are loaded into IDA Pro, and a simple complexity analysis is performed by counting and comparing the number of disassembled and identified functions. Additionally, functions are categorized as annotated or not annotated. An annotated function is one that is automatically detected by IDA’s built-in signatures or algorithms. It should be noted that IDA has capabilities to enhance library recognition, but these were not used for this experiment.

While both programs implement similar functionalities, the total number of disassembled functions in the C++ program is lower than 100, while the Rust programs pack almost 10,000 functions. Furthermore, the size of the C++ program is lower than 20 KB, while the Rust program is larger than 3 MB.

Programs written in Rust are typically statically linked, embedding all dependencies directly into the executable. As a result, binaries are larger with a high volume of functions, requiring analysts to distinguish first between third-party library code and attacker-authored logic.

To address this key problem, Microsoft Threat Intelligence Center is releasing an internally developed tool: RIFT.

This open-source project is designed to help reverse engineers and analysts more efficiently identify attacker-authored logic within Rust-based malware.

From source code to binary

Figure 3. Overview of Rust developer toolset

Before delving into the inner workings of RIFT, it is essential to have a fundamental understanding of how Rust binaries are compiled. As illustrated in the diagram above, Rust developers typically engage with three primary components and two endpoints:

  • cargo – The package manager
  • rustc – The Rust compiler
  • rustup – The Rust update manager
  • static.rust-lang.org – S3 bucket that hosts pre-compiled compilers and toolchains
  • crates.io – Rust community’s crate registry

Once a developer has conceptualized what they intend to develop, a typical workflow may proceed as follows:

  1. Using the cargo tool, the developer initializes a new projected named “test”.
  2. They opt not to use the latest Rust compiler but a specific version. They execute rustup install 1.84.0-x86_64-pc-windows-msvc to install the desired compiler version and configure the project to use the installed compiler.
  3. They determine that their project should communicate via HTTP and incorporate a third-party dependency. They run cargo add request to install the latest version of the third-party library, request.

Following these steps will result in a fully configured project. Upon completion, the developer may run cargo build to finalize the binary, compiling the project.

Static artifacts and where to find them

Reverse engineers are usually handed the final development product of the malware author, oftentimes without information such as the compiler used or third-party dependencies. While it is highly likely that malware authors use the same tools as reverse engineers for development, no insights into the exact environment are available.

However, understanding the development toolchain can assist in quickly distinguishing library code from author written logic. Fortunately, various indicators can be extracted that provide insights.

Rust compiler version

Rust binaries typically include metadata from the compiler that identifies the Rust version used to compile the binary. A config.toml file is provided alongside pre-compiled Rust compilers and toolchains. This configuration file contains the commit hash and the corresponding Rust compiler version of the pre-compiled product. By extracting the commit hash from the final binary output, it is possible to map the Git commit hash back to the appropriate Rust compiler version by parsing all available config.toml files from the official release channels.

Rust crates

As mentioned above, cargo is used to add dependencies to a project. Next to the Git commit hash, metadata extracted from Rust binaries also include the statically linked dependencies and their versions.

Figure 4. Extractable dependencies from strings

The above image shows how filtering for certain strings can display which dependencies were likely statically linked into RALord ransomware.

Introducing RIFT

RIFT is an open-source tool consisting of a set of IDA Pro (supporting versions >=9.0) plugins and Python scripts that aim to assist reverse engineers and other software analysts in annotating library code in Rust malware. It essentially consists of three components:

RIFT Static Analyzer: IDA Pro plugin to extract the Rust compiler commit hash and embedded dependencies from a binary.

RIFT Generator: A Python program to automate the process of Rust compiler identification, FLIRT signature generation of used Rust compiler and dependencies, as well as automation of binary diffing.

RIFT Diff Applier: IDA Pro plugin to consume binary diffing information generated by RIFT Generator.

In the previous section, we listed which indicators can be extracted from Rust binaries that give insights into which Rust compiler and dependencies were used. RIFT Static Analyzer automates the extraction process and stores the information in a JSON file for further processing. Furthermore, the plugin also extracts the architecture the binary was compiled for and the target operating system. In the below image, the target operating system is labeled as target_triple.

Figure 5. Overview of RIFT Static Analyzer

RIFT Generator: Automating FLIRT signature generation and auto diffing

Information gathered and stored by RIFT Static Analyzer can then be further processed by RIFT Generator.

Figure 6. RIFT Generator command line options

The Python program automates the process of compilation, data collection, FLIRT signature generation, and binary comparison.

It is essentially a wrapper around the following tools:

  • Cargo (Rust package manager) to manage the downloading and compiling of dependencies
  • Hexray’s FLAIR tools, specifically sigmake.exe and pcf.exe, to generate FLIRT signatures
  • Hexray’s text interface version of IDA, idat.exe, to automate binary analysis and disassembly
  • The open-source tool Diaphora to facilitate binary diffing
Figure 7. Phases of RIFT Generator

The above image provides an overview of the phases RIFT Generator processes through. RIFT Generator reads the JSON file produced by RIFT Static Analyzer and downloads the corresponding Rust compiler, as well as the dependencies.

It is worth noting that upon completion of phase 1, both the code of the downloaded compiler and compiled crates are compressed as COFF files into RLIB files. RLIB is essentially a Rust-specific archive format similar to TAR. Once decompressed in phase 2, the COFF files are extracted and further processed.

FLIRT signatures and binary diffing

To provide information necessary for annotating library code in Rust binaries accurately, RIFT uses two known techniques for pattern matching: FLIRT signatures and binary diffing.

FLIRT stands for Fast Library Identification and Recognition Technology and enables IDA to identify standard library functions produced by its supported compilers. A characteristic of this technology is that library recognition is very precise. Therefore, functions that have a high similarity may not be flagged by FLIRT signatures due to their strict criteria.

Additionally, RIFT automates the process of binary diffing the collected COFF files against the target binary by leveraging IDA’s command line utility (idat.exe) and the Diaphora plugin.

Figure 8. Overview of experimental batch binary diffing process

In general, both approaches have their own advantages and disadvantages, which are listed below.

FLIRT signatures approachBinary diffing approach
Highly reliable annotation, low false positive rateHigher false positive rate, but less strict and can fill gaps where FLIRT signatures fail due to strictness
With RIFT, in majority of cases, FLIRT signatures can be generated quicklyIn current state, batch binary diffing approach might take multiple hours
Not well applicable if dependencies and Rust compiler version are not availableApproach might yield useful results even if Rust compiler version and dependencies were not available

Consuming binary diffing information

If the binary diffing approach is applied, a second IDA plugin called RIFT Diff Applier can be used to apply the diffing results. In contrast to FLIRT signatures, the RIFT Diff Applier offers analysts an interactive, semi-manual method for identifying library code. It operates in two modes:

  1. Interactive mode
  2. Auto rename mode
Recapiti
stclarke