Skip to content

A collection of tools for cryptominers analysis. Part of the Cryptominers Anatomy blog series.

License

Notifications You must be signed in to change notification settings

akamai/CryptominersAnalysisTools

Repository files navigation

CryptominersAnalysisTools

A collection of tools for analyzing cryptominer activity. This repository is part of the Cryptominers' Anatomy blog series by Maor Dahan. The tools were published in conjunction with the blog posts, with usage instructions and real-world use cases provided directly from the research.

Using these tools, you can adopt the attacker's mindset to track down cryptominer campaigns. In addition to hunting campaigns, you can use the tools to detect malicious mining activities within your systems and ultimately fight back to mitigate the attacker's grip on your resources.

Candidate Table (Part I)

This component generates a candidate table that lists the most relevant cryptocurrencies an attacker might target for cryptomining malware. The coins are ranked by profitability using a formula that considers both the reward per mined block and the network hashrate, predicting the chances of winning the block mining race relative to the botnet's size and computing power.

Example output where the most relevant coins are privacy-focused and ASIC resistant:

Name                     | Algorithm    | Privacy Coin | ASIC resistant | KHR Profit
------------------------------------------------------------------------------------
SUMO                     | CryptoNightR |      No      |      Yes       | 0.00029647
FNNC                     | YescryptR16  |      No      |      Yes       | 0.00021125
Yerbas                   | GhostRider   |      No      |      Yes       | 0.00020158
RTM                      | GhostRider   |     Yes      |      Yes       | 0.00013138
FSC                      | GhostRider   |      No      |      Yes       | 0.00009290
Kylacoin                 | Flex         |      No      |      Yes       | 0.00007729
LCN                      | Flex         |      No      |      Yes       | 0.00007559
SPRX                     | YesPoWer     |      No      |      Yes       | 0.00004781
Monero                   | RandomX      |     Yes      |      Yes       | 0.00003965
Quantum Resistant Ledger | RandomX      |      No      |      Yes       | 0.00002945
Pascal                   | RandomHash2  |      No      |      Yes       | 0.00001978
XMC                      | CryptoNight  |      No      |       No       | 0.00001740
XELIS                    | XelisHash    |      No      |      Yes       | 0.00000289
Babacoin                 | GhostRider   |      No      |      Yes       | 0.00000096
GBX                      | NeoScrypt    |     Yes      |      Yes       | 0.00000091
Litecoin                 | Scrypt       |     Yes      |       No       | 0.00000076
GSPC                     | GhostRider   |      No      |      Yes       | 0.00000069
Frog Coin                | BMW512       |      No      |      Yes       | 0.00000044
PLUS1                    | HMQ1725      |      No      |      Yes       | 0.00000038
ZOC                      | NeoScrypt    |      No      |      Yes       | 0.00000015
VGC                      | X16Rv2       |      No      |      Yes       | 0.00000012
Zephyr                   | RandomX      |     Yes      |      Yes       | 0.00000003
Avian                    | X16RT        |      No      |      Yes       | 0.00000002
DIME                     | Quark        |      No      |       No       | 0.00000002
Actinium                 | Lyra2z       |      No      |      Yes       | 0.00000001
Verus                    | VerusHash    |     Yes      |      Yes       | 0.00000001
EFL                      | Scrypt       |      No      |       No       | 0.00000000
Dogecoin                 | Scrypt       |      No      |       No       | 0.00000000
Arion                    | X11          |      No      |       No       | 0.00000000
Dash                     | X11          |     Yes      |       No       | 0.00000000
BOLI                     | X11          |      No      |       No       | 0.00000000
BTB                      | Scrypt       |      No      |       No       | 0.00000000
HAL                      | NeoScrypt    |      No      |      Yes       | 0.00000000
NOVO                     | SHA256DT     |      No      |       No       | 0.00000000
Sumokoin - exchanges: 1 - markets: 1
Raptoreum - exchanges: 6 - markets: 9
Monero - exchanges: 31 - markets: 59
XMC - exchanges: 3 - markets: 4
Zephyr - exchanges: 9 - markets: 15
Verus - exchanges: 3 - markets: 6

Blockchain Network Crawler (Part II)

This tool crawls blockchain networks based on the Cryptonote P2P protocol, with a focus on Monero-like blockchains. It can, however, be generalized to any decentralized proof-of-work blockchain. The crawler outputs a list of known node peers (publicly accessible nodes) and generates a world map displaying these nodes with geolocation and heat map overlays.

Quick Usage

There is a preset configuration for Monero and Safex. You may also use the custom option for other Cryptonote-based networks.

Run the following command to get started:

python ./blockchain_network_crawler/main.py monero

Command-line help output:

usage: main.py [-h] [-n NODE] [-p PORT] [--network-id NETWORK_ID] [--cleanup] {monero,safex,custom}

A blockchain network crawler for networks based on the Cryptonote P2P protocol, such as Monero.

positional arguments:
  {monero,safex,custom}
                        Select the network you want to crawl.

options:
  -h, --help            Show this help message and exit.
  -n NODE, --node NODE  Specify the IPv4 address of a seed node.
  -p PORT, --port PORT  Specify the port number of the seed node.
  --network-id NETWORK_ID
                        Provide the hex string ID of the network.
  --cleanup             Clean up cached files.

Geo-Location Map of Nodes

The tool generates an HTML file that overlays blockchain nodes on a world map.

Example result: heat_map_monero_2025

XMRogue (Part III)

XMRogue is a minimal, pure python, crypto miner with the capability of:

  • Fingerprint pool endpoint directly or through XMRig proxy
  • Violate pool policy through abnormal connection to the pool
  • Execute BadShares technique to shutting down a proxy topology based mining campaigns

Quick Usage

You can run XMRogue out-of-the-box and provide targets using a command line or list file.

usage: main.py [-h] [--server-file SERVER_FILE] [--wallet WALLET] [--clients CLIENTS] [--threads THREADS] [--violation-type {multiple_login}] [--intervals INTERVALS] [--debug]
               {violation,bad_shares,pool_fingerprint} [server] [port]

XMRogue - Compromise cryptominer campaign

positional arguments:
  {violation,bad_shares,pool_fingerprint}
                        Operation mode
  server                Server hostname or IP
  port                  Server port

options:
  -h, --help            show this help message and exit
  --server-file SERVER_FILE
                        Path to file with list of server:port entries (one per line)
  --wallet WALLET       "multiple_login" and "pool_fingerprint" modes requires a wallet
  --clients CLIENTS     Number of clients
  --threads THREADS     Number of threads
  --violation-type {multiple_login}, --type {multiple_login}
                        Type of TOU violation
  --intervals INTERVALS
                        Interval between violation executions in seconds
  --debug               Enable debug logging

First choose the operation mode violation/bad_shares/pool_fingerprint with server-port pair or server list in a file.

The rest of the options are optional and depends on the usage. Like wallet, that is required in violation mode with violation-type=multiple_login in order to ban account using its wallet address.

Although the tool is harmless to legitimate crypto miners, we are asking you to use it responsibly and for research purposes.

Fingerprint pool through proxy

As we described in the blog, the proxy server.custompool.xyz:6202 was identified as part of malicious activity. It’s an XMRig proxy server and it’s fingerprint:

Server server.custompool.xyz:6202 fingerprint:
{'_test': 'invalid_nonce', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Invalid nonce; is miner not compatible with NiceHash?'}}
{'_test': 'low_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Low difficulty share'}}
{'_test': 'high_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Wrong hash 56c793bb17a2c9886151710f8f04841121873def33de3bc897369d90ade4df27 0000000000000000000000000000000000000000000000001000000000000000'}}
{'_test': 'keepalive', 'id': 1, 'jsonrpc': '2.0', 'error': None, 'result': {'status': 'KEEPALIVED'}}
{'_test': 'unknown', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Invalid method'}}
{'_test': 'login', 'jsonrpc': '2.0', 'id': 1, 'error': None, 'result': {'id': 'd259a73a538cf877', 'job': {'blob': '10108ccbdcc2064ede4e20e0a2fc6da8e6af79b9db23bd8e45dca7e5b60e0d78ae1e7e492dbe88000000307654a8b5ccdd635451ecf1e36a5983b139cce0dc25c93c5dbf896132611bcaf609', 'job_id': '75933', 'target': 'f3220000', 'algo': 'rx/0', 'height': 3439112, 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112'}, 'extensions': ['algo', 'nicehash', 'connect', 'tls', 'keepalive'], 'status': 'OK'}}
{'_test': 'login_bad_wallet', 'jsonrpc': '2.0', 'id': 1, 'error': None, 'result': {'id': '5f690eddbca1f584', 'job': {'blob': '101088cbdcc2064ede4e20e0a2fc6da8e6af79b9db23bd8e45dca7e5b60e0d78ae1e7e492dbe88000000f3712b0c0d7b0eeca4fcc19372d8bb20f24985db8b8fc3f4846f4dcd1a242ffe9008', 'job_id': '409012', 'target': 'f3220000', 'algo': 'rx/0', 'height': 3439112, 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112'}, 'extensions': ['algo', 'nicehash', 'connect', 'tls', 'keepalive'], 'status': 'OK'}}

As we can see on the next example, it is match to be an XMRig proxy that connected to nanopool.

Top Monero public pools

Here is a fingerprinting of the top Monero public pools. It can be used to find which pool is behind a mining proxy by cross-reference tests results.

Server miner.ntminer.vip:10799 fingerprint:
{'_test': 'invalid_nonce', 'id': 1, 'jsonrpc': '2.0', 'error': None, 'result': {'status': 'Ok'}}
{'_test': 'low_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': None, 'result': {'status': 'Ok'}}
{'_test': 'high_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': None, 'result': {'status': 'Ok'}}
{'_test': 'keepalive', 'id': 1, 'jsonrpc': '2.0', 'error': None, 'result': {'status': 'KEEPALIVED'}}
{'_test': 'unknown', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Method not found'}}
{'_test': 'login', 'id': 1, 'jsonrpc': '2.0', 'error': None, 'result': {'id': 'Wn1D3rvWcQ8jmt3eCnOJaRmiiPY6P7vi', 'job': {'id': 'Wn1D3rvWcQ8jmt3eCnOJaRmiiPY6P7vi', 'job_id': '0', 'blob': '1010c1bedcc206fcb2c7855789d94b21a47e340ef1bda0f34febfdaba9cc21c8d2d82bf9e211de00000000dbaed5a8178004e653a9b9858bd3763abacee5d7d187faf126f5b1a6988d8b5f01', 'target': 'f3220000', 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112', 'next_seed_hash': '', 'algo': 'rx/0', 'height': 3439099}, 'status': 'OK'}}
{'_test': 'login_bad_wallet', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Invalid address'}}

Server pool.supportxmr.com:3333 fingerprint:
{'_test': 'invalid_nonce', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Low difficulty share'}}
{'_test': 'low_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Low difficulty share'}}
{'_test': 'high_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Low difficulty share'}}
{'_test': 'keepalive', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Unauthenticated'}}
{'_test': 'login', 'id': 1, 'jsonrpc': '2.0', 'error': None, 'result': {'id': '5468e575-53ea-46ca-bf35-bce409fdeff5', 'job': {'blob': '1010dabfdcc206fcb2c7855789d94b21a47e340ef1bda0f34febfdaba9cc21c8d2d82bf9e211de00000000a86defcf6ad348a6252a3d91b8e1fa9d9c9468bae6d82a077513e6a6653befc227', 'job_id': 'RH7lGoQ1mR60TROG9BCzoVucREEJ', 'target': 'b2df0000', 'id': '5468e575-53ea-46ca-bf35-bce409fdeff5', 'height': 3439099, 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112', 'blockHash': 'e97cfe922017cfe09328a6cf15fef7d8', 'algo': 'rx/0'}, 'status': 'OK'}}
{'_test': 'login_bad_wallet', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Invalid payment address provided'}}

Server pool.hashvault.pro:443 fingerprint:
{'_test': 'invalid_nonce', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Invalid share'}}
{'_test': 'low_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Invalid share'}}
{'_test': 'high_difficulty', 'id': 2, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': 'Invalid share'}}
{'_test': 'keepalive', 'id': 1, 'jsonrpc': '2.0', 'error': None, 'result': {'status': 'KEEPALIVED'}}
{'_test': 'login', 'id': 1, 'jsonrpc': '2.0', 'error': None, 'result': {'id': '1e376829-4f5a-492f-8bc9-f96e14edf78b', 'job': {'blob': '1010dbbfdcc206fcb2c7855789d94b21a47e340ef1bda0f34febfdaba9cc21c8d2d82bf9e211de00000000b53a2668046471d773ff8f7b00437854a8ce4576e07ad0076ea40b00fd43e7eb27', 'job_id': '0b0b86ef-bcfa-4380-a49a-9370b1b1f83d', 'target': '04e90000', 'id': '1e376829-4f5a-492f-8bc9-f96e14edf78b', 'timestamp': 1750540257412, 'height': 3439099, 'algo': 'rx/0', 'variant': 'rx/0', 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112', 'motd': '0D0A486173685661756C74206B6565707320796F757220726967206861736872617465207365637572652E205468616E6B7320666F72207472757374696E67207573210D0A'}, 'extensions': ['algo', 'motd', 'keepalive'], 'status': 'OK'}}
{'_test': 'login_bad_wallet', 'id': 1, 'jsonrpc': '2.0', 'error': {'code': -1, 'message': "Your address doesn't look like a valid known coin address. Please check your login details"}, 'result': None}

Server auto.c3pool.org:80 fingerprint:
{'_test': 'invalid_nonce', 'jsonrpc': '2.0', 'id': 1, 'error': {'code': -1, 'message': 'Low difficulty share'}}
{'_test': 'low_difficulty', 'jsonrpc': '2.0', 'id': 2, 'error': {'code': -1, 'message': 'Low difficulty share'}}
{'_test': 'high_difficulty', 'jsonrpc': '2.0', 'id': 2, 'error': {'code': -1, 'message': 'Low difficulty share'}}
{'_test': 'keepalive', 'jsonrpc': '2.0', 'id': 1, 'error': None, 'result': {'status': 'KEEPALIVED'}}
{'_test': 'login', 'jsonrpc': '2.0', 'id': 1, 'error': None, 'result': {'id': '56070955', 'job': {'blob': '1010dabfdcc206fcb2c7855789d94b21a47e340ef1bda0f34febfdaba9cc21c8d2d82bf9e211de0000000031e3587a63b2710cb902c37de1df6f5479732bceb6be727917eb96d0263397c127', 'algo': 'rx/0', 'height': 3439099, 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112', 'job_id': '56070956', 'target': '711b0d00', 'id': '56070955'}, 'status': 'OK'}}

Server xmr-eu1.nanopool.org:10300 fingerprint:
{'_test': 'invalid_nonce', 'id': 1, 'jsonrpc': '2.0', 'result': None, 'error': {'code': -1, 'message': 'Wrong hash c84a9b198e172c6c674e5cb4fb94e51d3da6dc807cf7450d0cd6d9369f490b05 0000000000000000000000000000000000000000000000001000000000000000'}}
{'_test': 'low_difficulty', 'id': 2, 'jsonrpc': '2.0', 'result': None, 'error': {'code': -1, 'message': 'Wrong hash 7632ec46e5285a3ee6f56a3575b975f3586338297ccd025f573ca9787b2b6fd4 10e7f3bf01bead15b5ea9156d02bb2b17906a5d37c97813a1000000000000001'}}
{'_test': 'high_difficulty', 'id': 2, 'jsonrpc': '2.0', 'result': None, 'error': {'code': -1, 'message': 'Wrong hash 17d82a96079d7eb48b48d315b9f8943dd0a42060d0da44d24e896674e231f77a 0000000000000000000000000000000000000000000000001000000000000000'}}
{'_test': 'keepalive', 'id': 1, 'jsonrpc': '2.0', 'result': {'status': 'KEEPALIVED'}, 'error': None}
{'_test': 'login', 'id': 1, 'jsonrpc': '2.0', 'result': {'id': '1', 'job': {'blob': '1010dbbfdcc206fcb2c7855789d94b21a47e340ef1bda0f34febfdaba9cc21c8d2d82bf9e211de000000000f4a73b4f7dd8f7dd5ec81c9629af0edb36c54258184e5298e676afe6795707827', 'job_id': '260094', 'target': 'f3220000', 'height': 3439099, 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112'}, 'status': 'OK'}, 'error': None}
{'_test': 'login_bad_wallet', 'id': 1, 'jsonrpc': '2.0', 'result': {'id': '1', 'job': {'blob': '1010d7bfdcc206fcb2c7855789d94b21a47e340ef1bda0f34febfdaba9cc21c8d2d82bf9e211de000000004ae29aa0acb57d5502f08e9db1d1251b75267c6d36e343f9dc5c8ffcf978bf5726', 'job_id': '408886', 'target': 'f3220000', 'height': 3439099, 'seed_hash': '082f7e2de113b48affa1c587429498c88641b4e6194aa76171994afb9a47b112'}, 'status': 'OK'}, 'error': None}

Bad shares attack

Bad shares is a novel technique to interfere with the attackers’ mining bot net. In the video below we are demonstrates how we dismantling a malicious cryptomining operation from within the attackers’ botnet, freeing victims’ resources and severely impacting the attackers’ revenue—significantly undermining their incentive to continue these attacks.

Cryptominers.Anatomy.POC.video.mp4

Bad shares require one point of failure like in mining proxy topology, where all the victims are connected to a proxy, and the proxy is connected to a pool. As designed, there is a pushback mechanism to reduce the impact of bad shares on the pool servers. This is how we exploit the design and enforce the pool to ban the attackers’ proxy—and practically freeing the victims resources.

python XMRogue/main.py bad_shares <proxy ip> <port>

You can review the results in the video and in the blog post.

Policy violation

This is another take-off which target the attackers’ accounts through their wallet addresses. When attacker spread a cryptominer with a direct connection to a pool, it will require to provide the wallet address with it. Since the anonymous nature those systems, we can easily login the pool dashboard using the wallet address alone. It will reveal the magnitude of the cryptomining campaign, and the impact of XMRogue on the mining bot net when succeeded.

python XMRogue/main.py violation <pool ip> <port> --wallet <wallet> --violation-type multiple_login 

Acknowledgments

Akamai data science department for insightful data.

License

Copyright 2025 Akamai Technologies Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

About

A collection of tools for cryptominers analysis. Part of the Cryptominers Anatomy blog series.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages