Tricard - Malware sandboxes fingerprinting

Introduction to malware sandboxes fingerprinting

_config.yml

https://github.com/therealunicornsecurity/tricard

TLDR;

→ Security software: I will protect your system and defend against malicious activity!
→ Malware: I will detect and evade your defenses!
→ Security software: I will then detect your detection mechanisms!
→ Malware: I will .. uh..

_config.yml

Table of contents:

0. Origins, and purpose

Tricard was initially a simple recursive function for querying the registry. During red team engagements, it is quite common to try and understand what the devices are used for. One of the most common uses of this program was to find configurations linked to PuTTY, in order to identify system administrator’s workstations. After being deployed many times for gathering information, it was noticed that our collect server was receiving surprising information: it was unintentionaly collecting data coming from malware sandboxes. Our binary was analyzed by the tools deployed in our customers networks, and their sandboxes were also executing our program. That’s when we found out another potential use for this simple auditing tool.

1. Compile and dispatch

All data is concatenated in a JSON array. As it can be very heavy very quickly, the JSON payload POST requests are compressed using Zlib. The client and server are configured to communicate over HTTPS.

1.1 Collector functions

Each collector function appends data in a JSON buffer passed as a char* parameter.

Prototypes and overview

1. ListProcessesToJson(char* jsonData, size_t jsonDataLen):

  • Overview:
    • Lists running processes on the system.
    • Appends process information, including process name, to a JSON-formatted string.
    • JSON data is stored in the provided jsonData buffer.

2. ListLoadedModulesToJson(char* jsonData, size_t jsonDataLen):

  • Overview:
    • Lists loaded modules (DLLs) in running processes.
    • Appends module information, including module name, to a JSON-formatted string.
    • JSON data is stored in the provided jsonData buffer.

3. GetPSHistory(char* jsonData, int jsonDataLen):

  • Overview:
    • Retrieves PowerShell command history.
    • Encodes history in base64 and appends it to a JSON-formatted string.
    • JSON data is stored in the provided jsonData buffer.

4. GetSysinfo(char* jsonData, int jsonDataLen):

  • Overview:
    • Retrieves various system information (OS version, architecture, user information).
    • Encodes information in base64 and appends it to a JSON-formatted string.
    • JSON data is stored in the provided jsonData buffer.

5. GetNetworkInterfacesJSON(char* jsonBuffer, size_t bufferSize):

  • Overview:
    • Retrieves network interface information (MAC addresses, IP addresses).
    • Appends information to a JSON-formatted string.
    • JSON data is stored in the provided jsonBuffer buffer.

6. GetBasicInfo(char* jsonData):

  • Overview:
    • Retrieves basic system information (computer name, username, domain).
    • Initializes JSON data, storing it in the provided jsonData buffer.

7. LoopFolders(char* jsonData):

  • Overview:
    • Loops through specified folders (temporary folder and desktop).
    • Lists files in these folders, including details like filename, type, magic number, creation date, and modification date.
    • Appends file information to a JSON-formatted string stored in the provided jsonData buffer.

8. SendGzipCompressedPOSTRequest(const char* postData):

  • Overview:
    • Sends a POST request to a specified server with Gzip-compressed data.
    • Uses WinHTTP functions to open a TLS session, connect, and send the request.
    • Compression is done using the zlib library.

9. main():

  • Overview:
    • Main function that orchestrates the collection and transmission of system information.
    • Initiates a Gzip-compressed HTTP/TLS POST request to send the data to a server.

1.2 JSON Structure

_config.yml

The structure of the JSON file respects the collector functions described above.

Data types and overview

  1. Username:
    • Data Type: String
    • Summary: Represents the username associated with the data.
  2. MalOne:
    • Data Type: String (UUID)
    • Summary: Unique identifier (UUID) associated with the sample.
  3. Computername:
    • Data Type: String
    • Summary: Represents the name of the computer.
  4. NetworkInterfaces:
    • Data Type: List of Dictionaries
    • Summary: Contains information about network interfaces, including adapter name, description, IP address, and MAC address.
  5. PSHistory:
    • Data Type: String
    • Summary: Contains base64 encoded PowerShell history of the running user.
  6. Sysinfo:
    • Data Type: String
    • Summary: Contains base64 encoded system information, including the host name, OS version, architecture, and more.
  7. Files:
    • Data Type: Dictionary
    • Summary: Represents a file system structure with information about directories and files. Includes details like filename, file type, magic number, creation date, and modified date.
  8. RunningProcesses:
    • Data Type: List of Strings
    • Summary: Contains a list of currently running processes.
  9. LoadedModules:
    • Data Type: List of Strings
    • Summary: Contains a list of loaded modules.
  10. Regdump:
    • Data Type: Dictionary
    • Summary: Represents a registry dump with information organized under different registry hives (e.g., HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE).

Each received JSON file is named using a specific pattern:

_config.yml

It respects the following scheme:

source IP _ sample name _ epoch compilation date _ json reception timestamp.json

1.3 Compiled binaries

Each source file is modified by the dispatcher using sed. It places one UUID in two positions:

  • Cookies
  • JSON payload

It is so in order to detect systems that go through TLS termination proxies, or use HTTP modules, and rewrite headers.

Binaries are compiled using mingw for Linux. The compilation line is the following:

x86_64-w64-mingw32-g++ -w -static -Os -s tmpsrc/tricard.test.cpp -o tmpbuild/tricard.test.exe -lwinhttp -liphlpapi -lz -lcrypt32 -std=c++14

The original binary is heavy (around 1MB) but can be compressed using upx or any other packer. However, as tricard is already flagged by most AV solutions, packing it only makes things worse.

It follows the naming pattern:

tricard.sample name.exe.

The sample’s name is the one given to dispatcher.py.

2. Examples

Below are a few examples of blatant sandboxing

_config.yml

_config.yml

_config.yml

3. Server setup

3.1 Registering a new tricard sample

_config.yml

  • Step 1: Compile locally using dispatcher.py
  • Step 2: Send binary information to the server
    • MalOne: UUID for identifying each sample
    • Base 64 charset permutation
    • Each message is signed using HMAC to prevent data pollution
  • Step 3: Send sample to sandboxes
  • Step 4: Data is collected from backend server (srv.py)
  • Step 5: If the UUID of the emitting binary matches one found in statham.json, it is tagged accordingly. If not, it contains the string __unkowntarget__
  • Step 6: ???
  • Step 7: Profit

3.2 Server Endpoints

1. POST /GetData

  • Method: POST
  • Function: receive_post_data()
  • Purpose:
    • Receives compressed data via a POST request.
    • Reads a JSON file (“statham.json”) containing a database.
    • Decompresses the received data.
    • Matches a specific key (“MalOne”) from the data to a key in the database.
    • If a match is found, updates the data and saves it to a file in the “data/” directory.
    • If no match is found, but JSON data is received, an unknowntarget file is written

2. POST /MalOne

  • Method: POST
  • Function: mal_one()
  • Purpose:
    • Stores the newly registered samples.
    • Receives JSON data containing a message and an HMAC (Hash-based Message Authentication Code).
    • Verifies the integrity of the message using an HMAC with a secret key.
    • If the verification is successful, updates a JSON file (“statham.json”) with the received message, and the new sample is therefore registered, along with its Uuid and its Base64 charset
    • Responds with “Updated” and HTTP status 200 on successful update; otherwise, responds with “Failed” and HTTP status 401.

3.3 JSON samples database

_config.yml

Each new sample is registered with the following information:

  • Key: sample name_epoch compilation date
  • Values:
    • UUID identifying each sample
    • Base 64 charset permutation, (used only for Powershell History at the moment)

4. Analysis

The analysis phase is purposely left out of the picture here. In fact, tricard will most likely still be used for audits and data gathering. After all, it is nothing but an agnostic data collection tool. It is the analysis and what is made out of it, that defines its purpose. For that matter, the code has been released, as we would like to see it grow.

5. References

  • https://github.com/MISP/misp-warninglists/tree/main
  • https://github.com/albertzsigovits/malware-tools
  • https://ryandinho.me/2022/01/27/malware-analysis-resources.html#online-sandboxes-and-analyzers

When we first had the idea of using tricard for sandboxes fingerprinting, we stumbled upon this research article:

  • https://www.christian-rossow.de/publications/sandprint-raid2016.pdf

That’s all folks!

Stay classy netsecurios, and happy Unicorns day!


December 2023 —

Written on December 1, 2023