Representing Ransomware payments using STIX and Neo4j

CrocSec
6 min readJun 28, 2024

--

Part I – Building the foundations

Objective

Following on from this excellent blog post, Graphing of Ransomware Payments by David Greenwood from Dogesec, I wanted to:

  1. Visualise the STIX bundle created using Neo4j which would help me;
  2. Explore the utility of the cryptocurrency STIX objects and ransomwhe.re dataset;

Why do we want to do this?

According to the latest Mandiant M-Trends report 2024, ransomware continues to lead the charts in investigations. In 2023, global investigations involving ransomware increased five percentage points to 23% compared to 18% in 2022. These levels of ransomware investigations have not been seen since 2021 (M-Trends, 2024). The rebound in ransomware investigations shows the persistence of this threat and the need for a more open and standardised way of sharing intelligence between organisations and security researchers.

STIX and Stones

Turner et al (2020) proposed the ransomware-Bitcoin intelligence-forensic continuum model for scoping a ransomware investigation as it transitions through the phases of a typical kill chain. Much attention is drawn to the malware involved in a ransomware attack as the delivery, exploitation and installation of the payload cause devastating effects to the victim’s data and systems. However, during a ransomware attack, there is an interesting behavioural nexus that emerges. The attacker can be seen to pursue a dual modus operandi. Using the attack as a tool of destruction whilst pursuing a financial motivation to profit from the proceeds of cybercrime to fund further nefarious activities. This is the point where we cross the Vantasner Danger Meridian (VDM) and STIX provides us an industry standard to follow the ransomware attacker and share intel across the increasingly complex world of cryptocurrencies.

STIX, Malware and Cryptocurrencies

The good folks at Dogesec have made available two new custom STIX cryptocurrency objects.

As per the Dogesec blog post, they have modelled these objects based on the data and structure of the ransomwhe.re dataset. There are other schools of thought on the generation of STIX cryptocurrency objects. In my opinion, they are highly dependent on the dataset you wish to use to populate them with. For example, Turner et al (2022), similarly proposed two new STIX objects x-cryptocurrency-address and x-cryptocurrency-transaction which would use the walletexplorer.com API to populate two networks related to a ransomware attack. The ‘cash-in’ payment network representing victims paying attacker controlled Bitcoin wallets. Also, the ‘cash-out’ payment network representing the attacker controlled wallets movement of collected ransom funds. Either way you look at it, there is a need for this intel to be collected and disseminated in a standardised and accessible way.

Visualise and Manifest

No, we are not meditating in the hope of a ransomware attacker returning our precious data after we have transferred some crypto to a designated attacker controlled wallet. Hope is not a strategy. With the creation of these STIX objects it gives researchers a chance to investigate and share intel based on ransomware payment profiles. One of the best ways to manifest a ransomware attacker’s profile, besides their Malware signature, is to look at the movement of their crypto payments. This is where we can combine the threat intelligence collection standard STIX with the power of graph databases and visualisation to support the analysis and dissemination of the intel.

As a first pass, we will take the ransomwhere-bundle.json output from the Dogesec STIX objects and load it into Neo4j using the apoc.load.json function:

:param file_name: "file:///ransomwhere-bundle.json"
CALL apoc.load.json($file_name)
YIELD value

Now we have access to the JSON values from the STIX schema. This will allow us to map between STIX objects in the bundle and our Neo4j nodes, relationships and properties. The following code aims to “unwind” the schema and create the nodes, relationships and properties we desire.

:param file_name: "file:///ransomwhere-bundle.json"
CALL apoc.load.json($file_name)
YIELD value
UNWIND value.objects as obj
WITH obj
FOREACH (i in CASE WHEN obj.type = "relationship" THEN [1] ELSE [] END |
MERGE (relationship:Relationship {type: "relationship", relTypes: coalesce(obj.relationship_type, "NA"), id: obj.id, source_ref: coalesce(obj.source_ref, "NA"), target_ref: coalesce(obj.target_ref, "NA")})
)
FOREACH (i in CASE WHEN obj.type = "malware" THEN [1] ELSE [] END |
MERGE (malware:Malware {type: "malware", malTypes: coalesce(obj.malware_types, "NA"), id: obj.id, name: coalesce(obj.name, "NA")})
)
FOREACH (i in CASE WHEN obj.type = "indicator" THEN [1] ELSE [] END |
MERGE (indicator:Indicator {type: "indicator", indTypes: coalesce(obj.indicator_types, "NA"), id: obj.id, name: coalesce(obj.name, "NA"), description: coalesce(obj.description, "NA"), pattern: coalesce(obj.pattern, "NA")})
)
FOREACH (i in CASE WHEN obj.type = "cryptocurrency-wallet" THEN [1] ELSE [] END |
MERGE (cryptoWallet:Wallet {type: "cryptocurrency-wallet", id: obj.id, address: coalesce(obj.address, "NA")})
)

Starting with the relationship objects. These are the glue between the observable intel. By creating this node in Neo4j we link our STIX indicators to the cryptocurrency wallets and transactions associated with a ransomware campaign in the ransomwhe.re dataset. We will use this as an “invisible” node in our Neo4j implementation. Invisible in the sense that we need it for linking the STIX objects, but not to be visible as a node in our ultimate graph.

Subsequently we create the “malware”, “indicator” and “cryptocurrency-wallet” nodes in our graph using the cypher code above. The respective properties of our nodes are contained within the braces, {..}, of the above code.

Next, we need to build the relationships between our designated nodes (the objects from the STIX bundle). To do this we use our “invisible” node “Relationship”. In our Neo4j graph database we would like to use the “Indicator” object as the pivot between the observed “Malware” and their related “cryptocurrency-wallet”. The following code builds this lineage:

/*Create the relationships*/
/*Indicator -> indicates Malware*/
MATCH (i:Indicator), (m:Malware), (rel:Relationship)
WITH i,m,rel
WHERE i.id=rel.source_ref AND m.id=rel.target_ref
MERGE (i)-[p:INDICATES]->(m)

/*Indicator -> contains a pattern of crypto wallet addresses*/
OPTIONAL MATCH (i:Indicator), (w:Wallet), (rel:Relationship)
WITH i,w,rel
WHERE i.id=rel.source_ref AND w.id=rel.target_ref
MERGE (i)-[q:PATTERN_CONTAINS]->(w)

With the “Malware”, “Indicator” and “Wallet” nodes created and linked via the “Relationship” defined in the STIX bundle, it is time to link the respective transactions associated to those ransomware sited addresses in the attacker controlled wallets. To do this we need to “unwind” another layer of the bundle, “output”. The “output” layer reveals the address references to the cryptocurrency-wallet. In doing so it enables us to create the “cryptocurrency-transaction” nodes and link them to the “Wallet” nodes (see code below). It is important to perform this process at the end of the creation script as it builds on the previous created nodes and relationships.

/*Run last: Special case for building relationship between 
wallet and transaction*/

CALL apoc.load.json($file_name)
YIELD value
UNWIND value.objects as obj
UNWIND obj.output as outs
MATCH (w:Wallet)
WITH obj, outs, w
WHERE outs.address_ref=w.id
FOREACH (i in CASE WHEN obj.type = "cryptocurrency-transaction" THEN [1] ELSE [] END |
MERGE (cryptoTx:TX {type: "cryptocurrency-transaction", id: obj.id, hash: coalesce(obj.hash, "NA"), executionTime: coalesce(obj.execution_time, "NA")})
MERGE (w)-[r:PAYS {outputs: coalesce(outs.address_ref, "NA"), amount: coalesce(outs.amount, "NA")}]->(cryptoTx)
)

Outcomes

The ransomwhe.re data contains 105 unique ransomware campaigns. This is evident in our Neo4j graph database using this cypher query:

MATCH (n:Indicator)
RETURN DISTINCT n.name

We can now examine the structures of each of these campaigns. The following graph shows the transactions associated with the addresses associated with the REvil / Sodinokibi group.

MATCH p=(n:Indicator)-[r]->(w:Wallet)
WHERE n.name in ["REvil / Sodinokibi Cryptocurrency Wallets"]
RETURN p

This concludes part I — building the foundations. In the next part we will perform some analysis on our newly formed graphs in Neo4j. Compare and contrast different ransomware campaigns and how their payment patterns and TTPs differ between each malware family. This will allow us to build up a payments profile of the different ransomware actors.

References

Dogesec blog, Graphing of Ransomware Payments, July 23, 2024.

Turner, A. B., McCombie, S., & Uhlmann, A. J. (2020). Discerning payment patterns in Bitcoin from ransomware attacks. Journal of Money Laundering Control, 23(3), 545–589.

Turner, A. B., McCombie, S., & Uhlmann, A. J. (2022). Ransomware-bitcoin threat intelligence sharing using structured threat information expression. IEEE Security & Privacy, 21(3), 47–57.

--

--

CrocSec

CrocSec Bytes are a series of bite sized blogs working at the intersection of intelligence, cyber security and the networks behind them.