EtherHiding: JADESNOW Loader Pulling Payloads from Public Blockchains

WindowsmacOS

TTP Hunt medium Contagious InterviewEtherHidingInvisibleFerret

A script interpreter on a developer's machine reading blockchain explorer APIs to pull a smart-contract payload, then beaconing on TCP 3306: the on-chain loader Google Threat Intelligence Group attributes to UNC5342, the group behind Contagious Interview.

What this hunt looks for

In October 2025 Google Threat Intelligence Group reported UNC5342, the group behind Contagious Interview, adopting EtherHiding. A JavaScript downloader it names JADESNOW reads a payload from a BNB Smart Chain contract with gas-free read-only calls and runs the next stage in memory, which fetches a JavaScript INVISIBLEFERRET from Ethereum transaction data and beacons on TCP 3306.

The chain-reading step is the observable one. JADESNOW goes through public explorers that GTIG lists: Binplorer, Blockchair, BlockCypher, Ethplorer (with the free apiKey=freekey parameter), BscScan and Etherscan. A browser reaching them is ordinary; a node or Python process doing so from a developer's profile shortly after an interview assignment is not.

This hunt pairs that network view with GTIG's YARA rule for the downloader, the port 3306 beacon, and the contract, wallet, transaction and file-hash identifiers, which this site's EtherHiding tracker also records.

Technique
Ingress tool transfer through a web service the defender cannot block (MITRE ATT&CK T1105, T1102), dead-drop resolution via public blockchain (T1102.001), and non-application-layer command and control on a database port (T1571).
Data sources
EDR network telemetry, Proxy logs, File scanning, File hashes
Last updated
2026-10-04

Indicators

The atomic values this hunt matches on, listed separately from the queries so they can be lifted into tooling that speaks none of the query languages below. Every value here is reported by one of the sources cited at the foot of this page.

TypeValueNote
Smart contract address 0x8eac3198dd72f3e07108c4c7cff43108ad48a71c BNB Smart Chain contract hosting the second-stage JADESNOW payload; GTIG observed it updated more than twenty times over four months.
Wallet address 0x9bc1355344b54dedf3e44296916ed15653844509 Attacker-controlled owner address of the contract (GTIG).
Transaction hash 0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a Ethereum transaction carrying the INVISIBLEFERRET.JAVASCRIPT payload (GTIG).
Transaction hash 0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f Ethereum transaction carrying a split INVISIBLEFERRET.JAVASCRIPT payload (GTIG).
Transaction hash 0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41 Ethereum transaction carrying the credential-stealer component (GTIG).
Web service Binplorer, Blockchair, BlockCypher, Ethplorer (apiKey=freekey), BscScan, Etherscan Public explorer APIs JADESNOW reads the chain through (GTIG). The queries below match on the service names as they appear in host names.
Port 3306 INVISIBLEFERRET.JAVASCRIPT beacon and command channel (GTIG).
SHA256 970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628 ZIP archive containing the initial downloader (GTIG).
SHA256 01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7 JADESNOW initial JavaScript downloader (GTIG).

Logic

Every query below implements these steps, so the same hunt returns the same result in each product.

  1. Collect: network, process and file events on Windows and macOS endpoints, plus proxy logs
  2. Match: node or python reaches a blockchain explorer (Etherscan, BscScan, Binplorer, Blockchair, BlockCypher, Ethplorer)
  3. Match: or node or python reaches a public address on TCP 3306, or a reported contract, wallet or transaction ID is on a command line or URL
  4. Match: or, lower fidelity: a file hash is one of the two reported JADESNOW hashes, or a proxy log shows an explorer request from any client
  5. Report: devices with the signal kinds hit, most kinds first

Queries

Defender / Sentinel KQL

Written for this site, from indicators reported by Google Threat Intelligence Group

A script interpreter reading an explorer or beaconing on 3306 is the loader at work, and a chain identifier on a command line confirms it.

union
    // optional: lower fidelity, the two hashes are single builds and a rewritten downloader changes them; delete this line and the branch below it
    (DeviceFileEvents
        | where Timestamp > ago(90d)
        | where SHA256 in ("970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628", "01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7")
        | project Timestamp, DeviceId, DeviceName, Signal = "reported hash", Detail = strcat(FolderPath, "/", FileName)),
    (DeviceNetworkEvents
        | where Timestamp > ago(30d)
        | where InitiatingProcessFileName in~ ("node.exe", "node", "python.exe", "pythonw.exe", "python", "python3")
        | where RemoteUrl has_any ("etherscan", "bscscan", "binplorer", "blockchair", "blockcypher", "ethplorer") or (ActionType == "ConnectionSuccess" and RemotePort == 3306 and not(ipv4_is_private(RemoteIP)))
        | project Timestamp, DeviceId, DeviceName, Signal = "interpreter network", Detail = strcat(InitiatingProcessFileName, " -> ", RemoteUrl, " ", RemoteIP, ":", RemotePort)),
    (DeviceNetworkEvents
        | where Timestamp > ago(90d)
        | where RemoteUrl has_any ("0x8eac3198dd72f3e07108c4c7cff43108ad48a71c", "0x9bc1355344b54dedf3e44296916ed15653844509", "0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a", "0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f", "0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41")
        | project Timestamp, DeviceId, DeviceName, Signal = "chain identifier", Detail = RemoteUrl),
    (DeviceProcessEvents
        | where Timestamp > ago(90d)
        | where ProcessCommandLine has_any ("0x8eac3198dd72f3e07108c4c7cff43108ad48a71c", "0x9bc1355344b54dedf3e44296916ed15653844509", "0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a", "0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f", "0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41")
        | project Timestamp, DeviceId, DeviceName, Signal = "chain identifier", Detail = ProcessCommandLine)
| summarize Hits = count(), Signals = make_set(Signal), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Details = make_set(Detail, 5) by DeviceId, DeviceName
| order by array_length(Signals) desc, LastSeen desc
Lower-fidelity parts (1)
  • the two reported SHA256 hashes — each is a single build and a rewritten downloader changes it. Remove: delete the // optional: comment line and the (DeviceFileEvents ...) branch beneath it

Splunk SPL

Written for this site, from indicators reported by Google Threat Intelligence Group

The same interpreter, identifier and hash signals, with a proxy-side explorer branch for sites without endpoint network telemetry.

```Placeholders: index=network holds EDR or flow records with process_name, dest_ip, dest_port and url; index=proxy is the proxy log; index=endpoint holds process and file events with process and file_hash.```
index=network earliest=-30d process_name IN ("node.exe", "node", "python.exe", "pythonw.exe", "python", "python3") (dest_port=3306 OR url="*etherscan*" OR url="*bscscan*" OR url="*binplorer*" OR url="*blockchair*" OR url="*blockcypher*" OR url="*ethplorer*")
| where dest_port!=3306 OR NOT (cidrmatch("10.0.0.0/8", dest_ip) OR cidrmatch("172.16.0.0/12", dest_ip) OR cidrmatch("192.168.0.0/16", dest_ip))
| stats count max(_time) AS _time values(url) AS details BY dest, process_name
| eval host=dest, signal="interpreter network"
| append
    [search (index=proxy OR index=endpoint) earliest=-90d ("0x8eac3198dd72f3e07108c4c7cff43108ad48a71c" OR "0x9bc1355344b54dedf3e44296916ed15653844509" OR "0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a" OR "0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f" OR "0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41")
     | stats count max(_time) AS _time values(index) AS details BY host
     | eval signal="chain identifier"]
```optional: lower fidelity, proxy logs carry no process, so browsers and wallet applications also match; delete this line and the append block below it```
| append
    [search index=proxy earliest=-30d (url="*ethplorer.io*apiKey=freekey*" OR url="*binplorer.com*" OR url="*api.etherscan.io*" OR url="*api.bscscan.com*" OR url="*api.blockchair.com*" OR url="*api.blockcypher.com*")
     | stats count max(_time) AS _time values(url) AS details BY src
     | eval host=src, signal="explorer via proxy"]
```optional: lower fidelity, the two hashes are single builds and a rewritten downloader changes them; delete this line and the append block below it```
| append
    [search index=endpoint earliest=-90d file_hash IN ("970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628", "01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7")
     | stats count max(_time) AS _time values(file_path) AS details BY host
     | eval signal="reported hash"]
| stats sum(count) AS hits min(_time) AS first_seen max(_time) AS last_seen values(signal) AS signals values(details) AS details BY host
| eval signal_count=mvcount(signals)
| convert ctime(first_seen) ctime(last_seen)
| sort - signal_count, - last_seen
Lower-fidelity parts (2)
  • the proxy-log explorer branch — proxy logs carry no process, so browsers and wallet applications also match. Remove: delete the optional comment line and the append block beneath it
  • the two reported SHA256 hashes — each is a single build and a rewritten downloader changes it. Remove: delete the optional comment line and the append block beneath it

Sigma

Written for this site, from indicators reported by Google Threat Intelligence Group

Three rules that apply to Windows and macOS DNS, network and process telemetry.

title: Script Interpreter Reaching a Blockchain Explorer or TCP 3306
id: b2e1d0c3-5c8f-4d4b-8a72-3e9f6c8b2d02
status: experimental
description: >
  Detects node or python resolving a public blockchain explorer, the
  on-chain read JADESNOW performs, or connecting to TCP 3306 on a public
  address, the beacon channel of the JavaScript INVISIBLEFERRET variant, as
  Google Threat Intelligence Group documents for EtherHiding.
references:
  - https://cloud.google.com/blog/topics/threat-intelligence/dprk-adopts-etherhiding
logsource:
  category: dns_query
detection:
  selection:
    Image|endswith:
      - '\node.exe'
      - '\python.exe'
      - '\pythonw.exe'
      - '/node'
      - '/python'
      - '/python3'
    QueryName|contains:
      - 'etherscan'
      - 'bscscan'
      - 'binplorer'
      - 'blockchair'
      - 'blockcypher'
      - 'ethplorer'
  condition: selection
falsepositives:
  - Cryptocurrency developers calling explorer APIs from a script
level: medium
---
title: Script Interpreter Connecting to TCP 3306 on a Public Address
id: b2e1d0c3-5c8f-4d4b-8a72-3e9f6c8b2d03
status: experimental
description: >
  Detects node or python opening an outbound connection to TCP 3306 on a
  non-private address, the beacon channel GTIG documents for the JavaScript
  variant of INVISIBLEFERRET delivered through EtherHiding.
references:
  - https://cloud.google.com/blog/topics/threat-intelligence/dprk-adopts-etherhiding
logsource:
  category: network_connection
detection:
  selection:
    Image|endswith:
      - '\node.exe'
      - '\python.exe'
      - '\pythonw.exe'
      - '/node'
      - '/python'
      - '/python3'
    DestinationPort: 3306
  filter_private:
    DestinationIp|cidr:
      - '10.0.0.0/8'
      - '172.16.0.0/12'
      - '192.168.0.0/16'
      - '127.0.0.0/8'
  condition: selection and not filter_private
falsepositives:
  - Developers connecting to a hosted MySQL instance from a script
level: medium
---
title: EtherHiding Chain Identifier on a Command Line
id: b2e1d0c3-5c8f-4d4b-8a72-3e9f6c8b2d04
status: experimental
description: >
  Detects the contract, wallet or transaction identifier GTIG reports for
  the JADESNOW and INVISIBLEFERRET on-chain loader in a command line.
references:
  - https://cloud.google.com/blog/topics/threat-intelligence/dprk-adopts-etherhiding
logsource:
  category: process_creation
detection:
  selection:
    CommandLine|contains:
      - '0x8eac3198dd72f3e07108c4c7cff43108ad48a71c'
      - '0x9bc1355344b54dedf3e44296916ed15653844509'
      - '0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a'
      - '0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f'
      - '0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41'
  # optional: lower fidelity, the two hashes are single builds and a rewritten downloader changes them; delete this line and the selection below it
  selection_hash:
    Hashes|contains:
      - 'SHA256=970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628'
      - 'SHA256=01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7'
  condition: 1 of selection*
level: high
Lower-fidelity parts (1)
  • the two reported SHA256 hashes — each is a single build and a rewritten downloader changes it. Remove: delete the # optional: comment line and the selection_hash block beneath it

CrowdStrike LogScale

Written for this site, from indicators reported by Google Threat Intelligence Group

The identifier, explorer and hash signals over Falcon events; the 3306 beacon is omitted because Falcon network-connect events carry no process name.

(#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ CommandLine=/(0x8eac3198dd72f3e07108c4c7cff43108ad48a71c|0x9bc1355344b54dedf3e44296916ed15653844509|0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a|0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f|0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41)/i)
or (#event_simpleName=DnsRequest ContextBaseFileName=/^(node|python|python3|pythonw)(\.exe)?$/i DomainName=/(etherscan|bscscan|binplorer|blockchair|blockcypher|ethplorer)/i)
    // optional: lower fidelity, the two hashes are single builds and a rewritten downloader changes them; delete this line and the next
    or (#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ in(SHA256HashData, values=["970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628", "01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7"]))
| groupBy([aid, ComputerName], function=[count(as=Hits), count(#event_simpleName, distinct=true, as=SignalKinds), collect([#event_simpleName]), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)])
| sort(SignalKinds, order=desc)
Lower-fidelity parts (1)
  • the two reported SHA256 hashes — each is a single build and a rewritten downloader changes it. Remove: delete the // optional: comment line and the line beneath it

Elastic

Written for this site, from indicators reported by Google Threat Intelligence Group

The same interpreter, identifier and hash signals over Elastic Defend events, counted per host.

FROM logs-endpoint.events.*
| EVAL cmd = TO_LOWER(process.command_line), name = TO_LOWER(COALESCE(dns.question.name, destination.domain))
| WHERE (TO_LOWER(process.name) IN ("node.exe", "node", "python.exe", "pythonw.exe", "python", "python3") AND (name RLIKE ".*(etherscan|bscscan|binplorer|blockchair|blockcypher|ethplorer).*" OR (destination.port == 3306 AND NOT CIDR_MATCH(destination.ip, "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"))))
    OR cmd RLIKE ".*(0x8eac3198dd72f3e07108c4c7cff43108ad48a71c|0x9bc1355344b54dedf3e44296916ed15653844509|0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a|0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f|0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41).*"
    OR file.hash.sha256 IN ("970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628", "01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7")
    OR process.hash.sha256 IN ("970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628", "01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7")
| EVAL signal = CASE(file.hash.sha256 IS NOT NULL, "reported hash", process.command_line IS NOT NULL, "chain identifier", "interpreter network")
| STATS hits = COUNT(*), signal_kinds = COUNT_DISTINCT(signal), signals = VALUES(signal), last_seen = MAX(@timestamp) BY host.name
| SORT signal_kinds DESC, last_seen DESC
Lower-fidelity parts (1)
  • the two reported SHA256 hashes — each is a single build and a rewritten downloader changes it. Remove: delete the two OR lines ending in file.hash.sha256 IN and process.hash.sha256 IN

Cortex XQL

Written for this site, from indicators reported by Google Threat Intelligence Group

The same interpreter, identifier and hash signals over Cortex events.

dataset = xdr_data
| alter host = lowercase(coalesce(dns_query_name, action_external_hostname, ""))
| filter (event_type = ENUM.NETWORK and lowercase(actor_process_image_name) in ("node.exe", "node", "python.exe", "pythonw.exe", "python", "python3") and (host ~= "(etherscan|bscscan|binplorer|blockchair|blockcypher|ethplorer)" or (action_remote_port = 3306 and incidr(action_remote_ip, "10.0.0.0/8") = false and incidr(action_remote_ip, "172.16.0.0/12") = false and incidr(action_remote_ip, "192.168.0.0/16") = false)))
    or (event_type = ENUM.PROCESS and lowercase(action_process_image_command_line) ~= "(0x8eac3198dd72f3e07108c4c7cff43108ad48a71c|0x9bc1355344b54dedf3e44296916ed15653844509|0x86d1a21fd151e344ccc0778fd018c281db9d40b6ccd4bdd3588cb40fade1a33a|0xc2da361c40279a4f2f84448791377652f2bf41f06d18f19941a96c720228cd0f|0xf9d432745ea15dbc00ff319417af3763f72fcf8a4debedbfceeef4246847ce41)")
        // optional: lower fidelity, the two hashes are single builds and a rewritten downloader changes them; delete this line and the next
    or action_file_sha256 in ("970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628", "01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7") or action_process_image_sha256 in ("970307708071c01d32ef542a49099571852846a980d6e8eb164d2578147a1628", "01fd153bfb4be440dd46cea7bebe8eb61b1897596523f6f6d1a507a708b17cc7")
| comp count() as hits, count_distinct(event_type) as signal_kinds, max(_time) as last_seen by agent_hostname
| sort desc signal_kinds, desc last_seen
Lower-fidelity parts (1)
  • the two reported SHA256 hashes — each is a single build and a rewritten downloader changes it. Remove: delete the // optional: comment line and the line beneath it

osquery

Written for this site, from indicators reported by Google Threat Intelligence Group

Lists node and Python processes with a live socket to a public address on TCP 3306, so it catches the beacon only while connected.

SELECT p.pid, p.name, p.path, p.cmdline, s.remote_address, s.remote_port
FROM process_open_sockets s
JOIN processes p USING (pid)
WHERE s.remote_port = 3306
  AND lower(p.name) IN ('node', 'node.exe', 'python', 'python3', 'python.exe', 'pythonw.exe')
  AND s.remote_address NOT LIKE '10.%'
  AND s.remote_address NOT LIKE '192.168.%'
  AND s.remote_address NOT LIKE '172.16.%'
  AND s.remote_address NOT LIKE '127.%'
  AND s.remote_address != '';

GTIG's rule for the downloader, reproduced as published, keying on its small size and characteristic global-variable and string-handling idioms rather than any host name.

rule G_Downloader_JADESNOW_1 {
	meta:
		author = "Google Threat Intelligence Group (GTIG)"
	strings:
		$s1 = "global['_V']"
		$s2 = "global['r']"
		$s3 = "umP"
		$s4 = "mergeConfig"
		$s5 = "charAt" nocase
	condition:
		uint16(0) != 0x5A4D and filesize < 10KB and #s3 > 2 and #s5 == 1 and all of them
}

False positives and tuning

Cryptocurrency developers call explorer APIs constantly; in a Web3 shop scope the query to machines outside those teams, or to the free-tier Ethplorer key a funded project rarely uses.

The port 3306 queries catch any scripted MySQL client to a hosted database, so read the command line before escalating.

The YARA rule is GTIG's and will not find a rewritten downloader.

The contract, wallet and transaction identifiers are permanent but are visible only where a proxy log, command line or captured script shows the address being read.

Sources

Tuning required before use This hunt is a starting point. Adapt the queries to your schema, validate them against your baseline and tune them for the false positives noted above. Indicators go stale as operators rotate hosts.