If you are reading this blog post via a 3rd party source it is very likely that many parts of it will not render correctly (usually, the interactive graphs). Please view the post on dogesec.com for the full interactive viewing experience.

tl;dr

I recently conducted a project to identify the most prolific ransomware based on the ransom payments being made. Let me walk you through how I did it.

Overview

I have recently been exploring the gap between so called Web2 and Web3 threat intelligence.

More specifically, going from reverse-engineering ransomware to understanding the ecosystem around the malware.

Before a successful ransomware attack a lot of evidence will already exist.

For example, transaction where an affiliate has paid an operator for access to the ransomware.

Similarly after a successful attack a lot of evidence will be generated.

Ransom payments for example.

Most of this data is available on the blockchain and, as I’ve since discovered, in many cases traceable to an individual or group despite the Web3’s illusion of privacy.

I was recently working with a threat research group who wanted to use this data to identify the most prolific ransomware based on the ransom payments being made.

Let me explain step-by-step how I put together such a workflow for these institutions.

Representing the data

I wanted to represent the data as STIX objects for two reasons;

  1. many existing tools can already ingest this natively
  2. it has a network graph structure, perfect for linking together entities

However, STIX has no current SCOs to represent cryptocurrency concepts in its core specification.

So I set out to create some custom extensions. If you’re new to creating custom STIX objects, read this first.

In short I created two new objects:

  1. cryptocurrency-wallet: represents the actual wallet where crypto is stored
  2. cryptocurrency-transaction: represents the transactions of crypto between one or more wallets

Once you have a wallet or transaction hash, it is trivial to get more information about it from the blockchain.

For example, identifying the wallets sending money and receiving money in a transaction.

Or, perhaps more relevant to this example, take a wallet hash (where the ransom demand is to be paid) and identifying if it has received money from any other transactions.

In some cases, it’s also possible to identify the exchange the wallet belongs to (and ultimately who the wallet belongs too!).

Blockchain lookup

There are lots of services to get basic information from the blockchain, here I use https://blockchain.info;

Starting with a transaction hash;

GET https://blockchain.info/rawtx/c462e87a9db8a543102d39a482cd12ce0d5d726a935e444c628f12acb57c4155
{
    "hash": "c462e87a9db8a543102d39a482cd12ce0d5d726a935e444c628f12acb57c4155",
    "ver": 1,
    "vin_sz": 1,
    "vout_sz": 2,
    "size": 369,
    "weight": 1476,
    "fee": 47730,
    "relayed_by": "0.0.0.0",
    "lock_time": 0,
    "tx_index": 2999672112513617,
    "double_spend": false,
    "time": 1599235150,
    "block_index": 646724,
    "block_height": 646724,
    "inputs": [
        {
            "sequence": 4294967295,
            "witness": "",
            "script": "00483045022100b697e3af94dfadaed11211fef1b3d552f8707723a401254f5549dfa33526531c022044f86e74f9d4966b503cd837d46e4ba69ddd82b3638f1f624a8d902634d23b3901473044022076a53f6b630f053d8ea3dddf796a6504bee24657a89aad9539102b26bf4bce0502201671521dc4554121a37a7873fa8545f3d091bd73c70e87ada194559a06c2517a014c6952210204aa2a957cbfd3d4a0c814b5f35e931b079517c5cd683f9eeea9a036db8dd3f921037e8f34347b4db5e7a2cc602f58339297dcedf86de5b59e3b4ee712d47cdc9e002103f77181da33d31f50a57beb8096eb2219bbda92b00145b721d91f3f9979bab44f53ae",
            "index": 0,
            "prev_out": {
                "type": 0,
                "spent": true,
                "value": 2000070490,
                "spending_outpoints": [
                    {
                        "tx_index": 2999672112513617,
                        "n": 0
                    }
                ],
                "n": 0,
                "tx_index": 5686029377179373,
                "script": "a91444acd35b6770ec66cdf1c884268be850b67dc41687",
                "addr": "37x8uirHH9iQxGFQb7TUNU7tw1ub3pg6Gr"
            }
        }
    ],
    "out": [
        {
            "type": 0,
            "spent": true,
            "value": 500000000,
            "spending_outpoints": [
                {
                    "tx_index": 8570421275781059,
                    "n": 0
                }
            ],
            "n": 0,
            "tx_index": 2999672112513617,
            "script": "00145d7aeaedbd265cbbf0db4831702dd1746224809e",
            "addr": "bc1qt4aw4mdayewthuxmfqchqtw3w33zfqy7u0g6km"
        },
        {
            "type": 0,
            "spent": true,
            "value": 1500022760,
            "spending_outpoints": [
                {
                    "tx_index": 5282414730547233,
                    "n": 0
                }
            ],
            "n": 1,
            "tx_index": 2999672112513617,
            "script": "a914f8723cea3c16ad1c951204097cc50f7c7fa609c287",
            "addr": "3QLgMWWQ7aciZbuoLfo4EhXq14SYRAGPaJ"
        }
    ]
}

Shows all the transaction details, most importantly for this use-case the inputs and outputs.

Starting with a wallet;

GET https://blockchain.info/rawaddr/37x8uirHH9iQxGFQb7TUNU7tw1ub3pg6Gr
{
    "hash160": "44acd35b6770ec66cdf1c884268be850b67dc416",
    "address": "37x8uirHH9iQxGFQb7TUNU7tw1ub3pg6Gr",
    "n_tx": 2,
    "n_unredeemed": 0,
    "total_received": 2000070490,
    "total_sent": 2000070490,
    "final_balance": 0,
    "txs": [
        {
            "hash": "c462e87a9db8a543102d39a482cd12ce0d5d726a935e444c628f12acb57c4155",
            "ver": 1,
            "vin_sz": 1,
            "vout_sz": 2,
            "size": 369,
            "weight": 1476,
            "fee": 47730,
            "relayed_by": "0.0.0.0",
            "lock_time": 0,
            "tx_index": 2999672112513617,
            "double_spend": false,
            "time": 1599235150,
            "block_index": 646724,
            "block_height": 646724,
            "inputs": [
                {
                    "sequence": 4294967295,
                    "witness": "",
                    "script": "00483045022100b697e3af94dfadaed11211fef1b3d552f8707723a401254f5549dfa33526531c022044f86e74f9d4966b503cd837d46e4ba69ddd82b3638f1f624a8d902634d23b3901473044022076a53f6b630f053d8ea3dddf796a6504bee24657a89aad9539102b26bf4bce0502201671521dc4554121a37a7873fa8545f3d091bd73c70e87ada194559a06c2517a014c6952210204aa2a957cbfd3d4a0c814b5f35e931b079517c5cd683f9eeea9a036db8dd3f921037e8f34347b4db5e7a2cc602f58339297dcedf86de5b59e3b4ee712d47cdc9e002103f77181da33d31f50a57beb8096eb2219bbda92b00145b721d91f3f9979bab44f53ae",
                    "index": 0,
                    "prev_out": {
                        "type": 0,
                        "spent": true,
                        "value": 2000070490,
                        "spending_outpoints": [
                            {
                                "tx_index": 2999672112513617,
                                "n": 0
                            }
                        ],
                        "n": 0,
                        "tx_index": 5686029377179373,
                        "script": "a91444acd35b6770ec66cdf1c884268be850b67dc41687",
                        "addr": "37x8uirHH9iQxGFQb7TUNU7tw1ub3pg6Gr"
                    }
                }
            ],
            "out": [
                {
                    "type": 0,
                    "spent": true,
                    "value": 500000000,
                    "spending_outpoints": [
                        {
                            "tx_index": 8570421275781059,
                            "n": 0
                        }
                    ],
                    "n": 0,
                    "tx_index": 2999672112513617,
                    "script": "00145d7aeaedbd265cbbf0db4831702dd1746224809e",
                    "addr": "bc1qt4aw4mdayewthuxmfqchqtw3w33zfqy7u0g6km"
                },
                {
                    "type": 0,
                    "spent": true,
                    "value": 1500022760,
                    "spending_outpoints": [
                        {
                            "tx_index": 5282414730547233,
                            "n": 0
                        }
                    ],
                    "n": 1,
                    "tx_index": 2999672112513617,
                    "script": "a914f8723cea3c16ad1c951204097cc50f7c7fa609c287",
                    "addr": "3QLgMWWQ7aciZbuoLfo4EhXq14SYRAGPaJ"
                }
            ],
            "result": -2000070490,
            "balance": 0
        },
        {
            "hash": "879035a1d32c316d6c5298225b81c2bb1604c3df791f6e9c146d77ac094f9ba1",
            "ver": 1,
            "vin_sz": 2,
            "vout_sz": 1,
            "size": 386,
            "weight": 896,
            "fee": 29510,
            "relayed_by": "0.0.0.0",
            "lock_time": 0,
            "tx_index": 5686029377179373,
            "double_spend": false,
            "time": 1599230298,
            "block_index": 646719,
            "block_height": 646719,
            "inputs": [
                {
                    "sequence": 4294967295,
                    "witness": "0247304402206452afc967d26905cfd01122a3b562f140509b831b2375206c66eab5192bcbcc02204934812aebea47a4149c428d48bd9ee8cee93b57d1dc7d3abcd07ccb7f8ad7c6012103deb8c92ceffad0bdf86229f99a355b9fd811210702b3a796d76017ba38c2cb19",
                    "script": "160014ce8f6966e4644c1da931d463af9a54c76be6b44e",
                    "index": 0,
                    "prev_out": {
                        "type": 0,
                        "spent": true,
                        "value": 2000000000,
                        "spending_outpoints": [
                            {
                                "tx_index": 5686029377179373,
                                "n": 0
                            }
                        ],
                        "n": 0,
                        "tx_index": 349494507880829,
                        "script": "a91475789eefd3b52859d45737f1607844ad473e2d0087",
                        "addr": "3CQ9UsVGDz8mFxuxJZqsj9pGcn7ig8Jk62"
                    }
                },
                {
                    "sequence": 4294967295,
                    "witness": "02473044022060a4e3d2de3b9a9663d42a60b1043420617e43f41ff1af7e455f85df1f77c5ab022079a01e6aab132dcaff160d073074adf71960a358ca9a22ae770e3afaa39d80d4012103deb8c92ceffad0bdf86229f99a355b9fd811210702b3a796d76017ba38c2cb19",
                    "script": "160014ce8f6966e4644c1da931d463af9a54c76be6b44e",
                    "index": 1,
                    "prev_out": {
                        "type": 0,
                        "spent": true,
                        "value": 100000,
                        "spending_outpoints": [
                            {
                                "tx_index": 5686029377179373,
                                "n": 1
                            }
                        ],
                        "n": 0,
                        "tx_index": 5000965045408021,
                        "script": "a91475789eefd3b52859d45737f1607844ad473e2d0087",
                        "addr": "3CQ9UsVGDz8mFxuxJZqsj9pGcn7ig8Jk62"
                    }
                }
            ],
            "out": [
                {
                    "type": 0,
                    "spent": true,
                    "value": 2000070490,
                    "spending_outpoints": [
                        {
                            "tx_index": 2999672112513617,
                            "n": 0
                        }
                    ],
                    "n": 0,
                    "tx_index": 5686029377179373,
                    "script": "a91444acd35b6770ec66cdf1c884268be850b67dc41687",
                    "addr": "37x8uirHH9iQxGFQb7TUNU7tw1ub3pg6Gr"
                }
            ],
            "result": 2000070490,
            "balance": 2000070490
        }
    ]
}

Which shows all transactions this wallet has been a part of, whether it sent or received money, and how much.

Using the responses, I can programmatically create my crypto STIX objects to represent the data.

And we did.

crypto2stix allows you to enter a Bitcoin wallet or transaction hash, and it will create STIX objects using information obtained from the blockchain.

For example, here I use the transaction hash above to generate a STIX bundle that models the transaction object, and all wallets linked to the transaction;

python3 crypto2stix.py \
    --transaction c462e87a9db8a543102d39a482cd12ce0d5d726a935e444c628f12acb57c4155

Here is what that looks like on a graph;

If you click on the transaction object, you can see all the input and output wallets listed (which are also shown on the graph).

At this point the initial use-case is achieved – I can identify the transactions and amount of Bitcoins sent to a wallet using its hash.

Ransomware specific intelligence

However, at this point a wallet hash has no attribution to ransomware.

The examples used above are wallets and transactions used by Conti.

If you’re unfamiliar, there is a whole industry of blockchain analysis. The most prolific, and arguably the best, being Chainalysis. Other notable names include; TRM Labs, Elliptic, CipherTrace, AnChain, and SlowMist.

There are also a free/freemium set of tools, including; Arkham Intelligence, MetaSleuth, and Wallet Explorer, doing similar analysis. I’ve found Arkham Intelligence the best for tagging wallets related to ransomware.

For example if I enter the wallet hash 37x8uirHH9iQxGFQb7TUNU7tw1ub3pg6Gr into Arkham Intelligence, it tells me that it is associated to Conti.

Arkham Intelligence Conti Wallet ID

I can also do the reverse and start with Conti to ask for all the wallets and transactions associated with it.

Arkham Intelligence Conti

Sadly they are yet to respond to my request for API access so I couldn’t automate these lookups.

At this point I started to research open-databases tracking wallet hashes used in ransom requests so that I could then start searching the blockchain for inbound transactions to said wallet.

Then I discovered Ransomwhe.re.

Ransomwhere is the open, crowdsourced ransomware payment tracker. Browse and download ransomware payment data or help build our dataset by reporting ransomware demands you have received.

Ransomwhere also has an free and open API to access the data!

Using the API we built a tool, ransomwhere2stix, that converts the data into STIX objects.

ransomwhere2stix produces a STIX bundle you can see an example of this data here.

Here’s an example of the transaction data in ransomwhe.re for BlackCat;

Using stix2arango I can import this into ArangoDB as follows (making sure to replace path/to/ransomwhere-bundle.json with the correct path);

python3 stix2arango.py \
  --file path/to/ransomwhere-bundle.json \
  --database blog_demo \
  --collection ransomwhere

I can now start to explore the objects.

This search will return all the non-relationship objects in the bundle.

FOR doc IN ransomwhere_vertex_collection
    FILTER doc._stix2arango_note != "automatically imported on collection creation"
    LET keys = ATTRIBUTES(doc)
    LET filteredKeys = keys[* FILTER !STARTS_WITH(CURRENT, "_")]
        RETURN KEEP(doc, filteredKeys)

You can filter by each ransomware type to get the Indicator detections;

FOR doc IN ransomwhere_vertex_collection
    FILTER doc._stix2arango_note != "automatically imported on collection creation"
    AND doc.type == "indicator"
    LET keys = ATTRIBUTES(doc)
    LET filteredKeys = keys[* FILTER !STARTS_WITH(CURRENT, "_")]
        RETURN KEEP(doc, filteredKeys)

Currently returns 105 objects, here is the first in the response (for REvil)…

  {
    "created": "2020-01-09T17:36:50.000Z",
    "created_by_ref": "identity--904ac99b-7539-5de7-9ffa-23186f0e07b6",
    "description": "Known Cryptocurrency Wallets associated with REvil / Sodinokibi",
    "id": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "indicator_types": [
      "malicious-activity"
    ],
    "modified": "2020-01-09T17:36:50.000Z",
    "name": "REvil / Sodinokibi Cryptocurrency Wallets",
    "object_marking_refs": [
      "marking-definition--904ac99b-7539-5de7-9ffa-23186f0e07b6",
      "marking-definition--94868c89-83c2-464b-929b-a1a8aa3c8487"
    ],
    "pattern": "[cryptocurrency-wallet:address = '3BeQ9H5tByJK9CeeZftDsBFhgt1i5Q7AQK' OR cryptocurrency-wallet:address = '3L7ECcRBCypxrS5U9Kw9WexcsHmX4wKYz6' OR cryptocurrency-wallet:address = '34mMCqo83wc8GeLWjSPeQE8QiY9LKnkNuj' OR cryptocurrency-wallet:address = '3JYLAk26kZPw62W6UD2Jyk5i9jhCAPJjg4' OR cryptocurrency-wallet:address = '3Jxwt3fmXhUwDNDQ4sWYCgahLGDVjy1SQm' OR cryptocurrency-wallet:address = '3HTHHMm2YwNdwEDkGc6dRyxxKvByymeVqV' OR cryptocurrency-wallet:address = '3E9F7gE3upQ8rgsPjwiKH7ugfdneypPjqj']",
    "pattern_type": "stix",
    "pattern_version": "2.1",
    "spec_version": "2.1",
    "type": "indicator",
    "valid_from": "2020-01-09T17:36:50Z"
  }

If you want to search for a ransomware name you could use a search like:

FOR doc IN ransomwhere_vertex_collection
    FILTER doc._stix2arango_note != "automatically imported on collection creation"
    AND doc.name LIKE "%WannaCry%"
    LET keys = ATTRIBUTES(doc)
    LET filteredKeys = keys[* FILTER !STARTS_WITH(CURRENT, "_")]
    RETURN KEEP(doc, filteredKeys)
[
  {
    "created": "2020-11-14T07:08:23.000Z",
    "created_by_ref": "identity--904ac99b-7539-5de7-9ffa-23186f0e07b6",
    "id": "malware--4ac72b77-fc6d-5aba-b37c-39d3dd27b3ff",
    "is_family": true,
    "malware_types": [
      "ransomware"
    ],
    "modified": "2020-11-14T07:08:23.000Z",
    "name": "WannaCry",
    "object_marking_refs": [
      "marking-definition--904ac99b-7539-5de7-9ffa-23186f0e07b6",
      "marking-definition--94868c89-83c2-464b-929b-a1a8aa3c8487"
    ],
    "spec_version": "2.1",
    "type": "malware"
  },
  {
    "created": "2020-11-14T07:08:23.000Z",
    "created_by_ref": "identity--904ac99b-7539-5de7-9ffa-23186f0e07b6",
    "description": "Known Cryptocurrency Wallets associated with WannaCry",
    "id": "indicator--4ac72b77-fc6d-5aba-b37c-39d3dd27b3ff",
    "indicator_types": [
      "malicious-activity"
    ],
    "modified": "2020-11-14T07:08:23.000Z",
    "name": "WannaCry Cryptocurrency Wallets",
    "object_marking_refs": [
      "marking-definition--904ac99b-7539-5de7-9ffa-23186f0e07b6",
      "marking-definition--94868c89-83c2-464b-929b-a1a8aa3c8487"
    ],
    "pattern": "[cryptocurrency-wallet:address = '115p7UMMngoj1pMvkpHijcRdfJNXj6LrLn' OR cryptocurrency-wallet:address = '13AM4VW2dhxYgXeQepoHkHSQuy6NgaEb94' OR cryptocurrency-wallet:address = '12t9YDPgwueZ9NyMgw519p7AA8isjr6SMw' OR cryptocurrency-wallet:address = '1QAc9S5EmycqjzzWDc1yiWzr9jJLC8sLiY' OR cryptocurrency-wallet:address = '15zGqZCTcys6eCjDkE3DypCjXi6QWRV6V1']",
    "pattern_type": "stix",
    "pattern_version": "2.1",
    "spec_version": "2.1",
    "type": "indicator",
    "valid_from": "2020-11-14T07:08:23Z"
  }
]

stix2arango creates a graph of objects using the STIX relationship objects and embedded relationships inside STIX objects.

That means I can traverse the graph to find out what objects are related to what others. I’ll use the REvil / Sodinokibi Cryptocurrency Wallets Indicator…

// First, get all the ids from ransomwhere_vertex_collection that match the criteria
LET vertex_ids = (
    FOR doc IN ransomwhere_vertex_collection
        FILTER doc._stix2arango_note != "automatically imported on collection creation"
        AND doc.type == "indicator"
        AND doc.id == "indicator--7186c016-334c-5955-89d2-658e4a4d3756"
        LET keys = ATTRIBUTES(doc)
        LET filteredKeys = keys[* FILTER !STARTS_WITH(CURRENT, "_")]
        RETURN doc.id
)

// Next, find all relationships that have source_ref or target_ref in the list of vertex_ids
FOR rel IN ransomwhere_edge_collection
    FILTER rel.source_ref IN vertex_ids OR rel.target_ref IN vertex_ids
    RETURN {
        relationship_id: rel.id,
        source_ref: rel.source_ref,
        target_ref: rel.target_ref,
        relationship_type: rel.relationship_type
    }

Gives the Malware object the Indicator belongs to, and all the Cryptocurrency Wallets listed inside the Indicator pattern.

[
  {
    "relationship_id": "relationship--7e1b770d-c8b6-5691-872c-9f6cbef5a3bf",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "malware--7186c016-334c-5955-89d2-658e4a4d3756",
    "relationship_type": "indicates"
  },
  {
    "relationship_id": "relationship--c56fe058-6cdb-57a9-bb23-8f56efba4ce8",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "cryptocurrency-wallet--40b020eb-43a5-55a8-acd1-62a3aae761bc",
    "relationship_type": "pattern-contains"
  },
  {
    "relationship_id": "relationship--2da4674c-bcef-5095-8336-15ebec6b63ad",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "cryptocurrency-wallet--8e4221d4-48b9-5fd7-be9b-45fba3c545b6",
    "relationship_type": "pattern-contains"
  },
  {
    "relationship_id": "relationship--2f54948e-cdee-5844-8df2-a2584822e5f7",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "cryptocurrency-wallet--c5bb4016-b780-5d8b-8503-2fa8e1b5460b",
    "relationship_type": "pattern-contains"
  },
  {
    "relationship_id": "relationship--77dfe8ea-bcd1-577f-829f-c43e51a06290",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "cryptocurrency-wallet--2418b59a-20a7-5c75-801b-449c3dc8c00b",
    "relationship_type": "pattern-contains"
  },
  {
    "relationship_id": "relationship--12fc35d2-e8c8-5e92-98e6-840851118d5c",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "cryptocurrency-wallet--4619d607-041b-5c79-a98d-2fef07cac3da",
    "relationship_type": "pattern-contains"
  },
  {
    "relationship_id": "relationship--b85386a3-19c1-5d6b-8781-6176a49ccd2f",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "cryptocurrency-wallet--131063b3-04c4-5c14-8669-6db0ac11314f",
    "relationship_type": "pattern-contains"
  },
  {
    "relationship_id": "relationship--e841fe7d-6b46-5442-a7da-9100389e5c33",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "cryptocurrency-wallet--aecb8fb0-57a3-5310-beb7-d7af80dd2968",
    "relationship_type": "pattern-contains"
  },
  {
    "relationship_id": "relationship--e5e4b608-e2ad-5bd0-a0af-5391f4d574d8",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "identity--904ac99b-7539-5de7-9ffa-23186f0e07b6",
    "relationship_type": "created-by"
  },
  {
    "relationship_id": "relationship--4ab7e4fe-ab39-535f-a3fc-1dd941884cb6",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "marking-definition--904ac99b-7539-5de7-9ffa-23186f0e07b6",
    "relationship_type": "object-marking"
  },
  {
    "relationship_id": "relationship--08576437-b2e1-586f-802a-dbb84c964145",
    "source_ref": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "target_ref": "marking-definition--94868c89-83c2-464b-929b-a1a8aa3c8487",
    "relationship_type": "object-marking"
  }
]

Often you will want to know how much a malware string has netted for the actors using it to extort organisations.

// Step 1: Get all cryptocurrency-wallet ids related to the indicator
LET wallet_ids = (
    LET vertex_ids = (
        FOR doc IN ransomwhere_vertex_collection
            FILTER doc._stix2arango_note != "automatically imported on collection creation"
            AND doc.type == "indicator"
            AND doc.id == "indicator--7186c016-334c-5955-89d2-658e4a4d3756"
            LET keys = ATTRIBUTES(doc)
            LET filteredKeys = keys[* FILTER !STARTS_WITH(CURRENT, "_")]
            RETURN doc.id
    )
    FOR rel IN ransomwhere_edge_collection
        FILTER rel.source_ref IN vertex_ids OR rel.target_ref IN vertex_ids
        FILTER rel.relationship_type == "pattern-contains"
        RETURN DISTINCT rel.target_ref
)

// Step 2: Get all cryptocurrency-transactions related to the cryptocurrency-wallets
LET transactions = (
    FOR rel IN ransomwhere_edge_collection
        FILTER rel.relationship_type == "address"
        AND rel.target_ref IN wallet_ids
        RETURN rel.source_ref
)

// Step 3: Sum all the amounts linked to the indicator id and divide by 100000000
LET total_amount = (
    FOR transaction IN ransomwhere_vertex_collection
        FILTER transaction.type == "cryptocurrency-transaction"
        AND transaction.id IN transactions
        FOR output IN transaction.output
            FILTER output.address_ref IN wallet_ids
            COLLECT AGGREGATE total = SUM(output.amount)
        RETURN total / 100000000
)

RETURN { 
    indicator_id: "indicator--7186c016-334c-5955-89d2-658e4a4d3756", 
    total_amount: total_amount[0] 
}
[
  {
    "indicator_id": "indicator--7186c016-334c-5955-89d2-658e4a4d3756",
    "total_amount": 327.41854732
  }
]

Note, the total_amount shows total Bitcoins received by all wallets linked to this malware.

To work out an accurate USD amount you need to convert each transaction amount into USD first using the exchange rate on the execution_time datetime reported in the transaction.

Here’s an example of doing that for one transaction…

FOR doc IN ransomwhere_vertex_collection
    FILTER doc.id == "cryptocurrency-transaction--2b8233e1-7ec8-50bc-a567-0a17da8427b9"
    LET keys = ATTRIBUTES(doc)
    LET filteredKeys = keys[* FILTER !STARTS_WITH(CURRENT, "_")]
    RETURN KEEP(doc, filteredKeys)
[
  {
    "execution_time": "2020-04-04T15:44:42Z",
    "extensions": {
      "extension-definition--151d042d-4dcf-5e44-843f-1024440318e5": {
        "extension_type": "new-sco"
      }
    },
    "hash": "7377f834e1697c44fb3a39aca71548f36911ac3bd05f2f4c4b5f86c59cb9b860",
    "id": "cryptocurrency-transaction--2b8233e1-7ec8-50bc-a567-0a17da8427b9",
    "object_marking_refs": [
      "marking-definition--904ac99b-7539-5de7-9ffa-23186f0e07b6",
      "marking-definition--27557362-b745-4161-96e8-ccd62ce4cb26",
      "marking-definition--94868c89-83c2-464b-929b-a1a8aa3c8487"
    ],
    "output": [
      {
        "address_ref": "cryptocurrency-wallet--6b5c9429-9ca0-5f75-9fad-73fa07e719bc",
        "amount": 74110745
      }
    ],
    "spec_version": "2.1",
    "symbol": "BTC",
    "type": "cryptocurrency-transaction"
  }
]

On April 4, 2020, the closing price of Bitcoin was approximately $6,789.36 USD. Thus this transaction was worth (0.74110745BTC x 6,789.36) 5,032.34USD.

It is important to note that ransomwhe.re might not be the most accurate source for complete transaction data related to wallets (I’m not sure how often it is updated). You could also use the blockchain to lookup other transactions for a wallet hash known to be linked to ransomware that might not be covered in ransomwhe.re.

Often you’ll start from the bottom up, that is asking; is this transaction linked to a malware family?

Here I start with cryptocurrency-transaction--f437c493-b651-5cbb-845a-3dd231a39ec6;

// Step 1: Find cryptocurrency-wallets associated with the given cryptocurrency-transaction
LET transaction_id = "cryptocurrency-transaction--f437c493-b651-5cbb-845a-3dd231a39ec6"

LET wallet_ids = (
    FOR rel IN ransomwhere_edge_collection
        FILTER rel.relationship_type == "address"
        AND rel.source_ref == transaction_id
        RETURN rel.target_ref
)

// Step 2: Find indicators associated with these cryptocurrency-wallets
LET indicator_ids = (
    FOR rel IN ransomwhere_edge_collection
        FILTER rel.relationship_type == "pattern-contains"
        AND rel.target_ref IN wallet_ids
        RETURN DISTINCT rel.source_ref
)

// Step 3: Find malware associated with these indicators
LET malware_ids = (
    FOR rel IN ransomwhere_edge_collection
        FILTER rel.relationship_type == "indicates"
        AND rel.source_ref IN indicator_ids
        RETURN DISTINCT rel.target_ref
)

// Step 4: Retrieve malware details
FOR malware IN ransomwhere_vertex_collection
    FILTER malware.id IN malware_ids
    RETURN malware.name
[
  "Netwalker (Mailto)"
]

One of the big problems I came across with ransomwhe.re data was ransomware naming.

Netwalker (Mailto), is a good example. For example, in MITRE ATT&CK this ransomware is named Netwalker (S0457). The names are very similar, but make it a little trickier to join ransomwhe.re data to other sources.

In the outputted objects, I also map the MITRE ATT&CK Software ID, should one exist. For example for Netwalker (Mailto);

        },
        {
            "type": "malware",
            "spec_version": "2.1",
            "id": "malware--7269b4f4-e767-5381-af44-98f57a2fda31",
            "created_by_ref": "identity--a1cb37d2-3bd3-5b23-8526-47a22694b7e0",
            "created": "2020-04-25T18:24:13.000Z",
            "modified": "2020-04-25T18:24:13.000Z",
            "name": "Netwalker (Mailto)",
            "malware_types": [
                "ransomware"
            ],
            "is_family": true,
            "external_references": [
                {
                    "source_name": "mitre-attack",
                    "url": "https://attack.mitre.org/software/S0457",
                    "external_id": "S0457"
                }
            ],
            "object_marking_refs": [
                "marking-definition--a1cb37d2-3bd3-5b23-8526-47a22694b7e0",
                "marking-definition--94868c89-83c2-464b-929b-a1a8aa3c8487"
            ]
        }

This is a bigger problem though.

To try an address this, I’ve started work on a project I’m calling Ransomware KB.

This project is heavily inspired by MITRE ATT&CK, aiming to fill the gap in MITRE ATT&CK for ransomware specific content.

Ultimately the aim is to provide a single source of ransomware names, with alias and attribution. My next step is to normalise ransomwhe.re names to those in Ransomware KB.

In summary

It is important to stress here that this work should be considered a proof-of-concept to show how STIX crypto objects can be used to model transactions related to ransomware (or any crime really).

Once you have a cryptocurrency transaction or wallet represented as STIX objects you can use them, across your research. Link them to new malware strings, actors, reports, etc. as you continue your research to build a graph of intelligence.


Obstracts

The RSS reader for threat intelligence teams. Turn any blog into machine readable STIX 2.1 data ready for use with your security stack.

Obstracts. The RSS reader for threat intelligence teams.

Stixify

Your automated threat intelligence analyst. Extract machine readable STIX 2.1 data ready for use with your security stack.

Stixify. Your automated threat intelligence analyst.

Discuss this post

Head on over to the DOGESEC community to discuss this post.

DOGESEC community

Posted by:

David Greenwood

David Greenwood, Do Only Good Everyday



Never miss an update


Sign up to receive new articles in your inbox as they published.

Your subscription could not be saved. Please try again.
Your subscription has been successful.