Introduction
As part of our ongoing malware research at Seqrite Labs, we analysed an infected Windows machine with three scheduled tasks: SysCheck2, SysCheckX and SysHeal. All three pointed to C:\Windows\SystemHealth\Update, and the machine was talking to datamons[.]org. When we opened the folder, we found a complete malware kit written in Python, shipped with its own portable copy of Python 3.11 so that it runs on any Windows machine.
Every malicious script in the kit was protected with the trial version of PyArmor, so there was nothing readable on disk. We decrypted all of the scripts and rebuilt the logic of the whole kit.
The kit turned out to be Armorsteal, a Python malware family that Zscaler ThreatLabz wrote recently on 22 October 2026. An older build was also described in June 2026 by an independent researcher, who found an XMRig miner bundled with it. The build we found is newer than both.Zscaler ThreatLabz
Infection Chain
We did not see the first step on this machine. Zscaler says victims search for tools like Chocolatey or the Microsoft Activation Scripts and land on fake pages. The page asks them to paste a PowerShell command. The command installs the real tool and quietly drops the kit into C:\Windows\SystemHealth\Update.
After that, three scheduled tasks start two processes. SysMon.py downloads the spy and stealer packages and then works as a backdoor. xm_handler.py installs and guards the miner. Each part has its own copy of the C2 list. If one part is killed, the others keep working.

Three scheduled tasks keep everything running. SysCheck2 starts the controller (SysMon.py) every time the user logs on, through conhost.exe –headless so no console window appears. SysCheckX starts the miner manager (xm_handler.py) as SYSTEM 45 seconds after every boot, in the build Zscaler described, this task started the controller instead. SysHeal is new. It ran the controller once as SYSTEM on 27 September 2026. SysCheck2 was registered by the user account, while the two SYSTEM tasks have no author, which suggests the malware added them itself after it got in.

Decrypting the PyArmor-protected scripts
Each script on disk is a few lines of PyArmor loader followed by an encrypted blob. The only readable part is the header, which at least tells us when the script was built:
| # Pyarmor 9.1.7 (trial), 000000, non-profits, 2026-10-28T12:56:58.193660
from pyarmor_runtime_000000 import __pyarmor__ __pyarmor__(__name__, __file__, b’PY000000\x00\x03\x0b\x00\xa7\r\r\n\x80… |
The attackers used the trial version, and in trial builds the decryption key ships inside the bundled runtime, pyarmor_runtime.pyd. That meant we could decrypt everything. We used the open-source tool Pyarmor-Static-Unpack-1shot, which gave us readable source, a full disassembly and a string dump for every script.

The controller: SysMon.py
SysMon.py is the heart of the kit. It first makes sure only one copy is running, then builds a victim ID from the motherboard serial number. If the serial is empty or a placeholder like “To be filled by OEM”, it makes up a random one. The ID goes into machine_id.txt, which every other part of the kit reads.

Next it looks for a working server. It tries 11 domains in order, asking each one for /downloadables/configf.json, and settles on the first that answers. After three failures it moves down the list, and it moves back up as soon as a better domain returns. datamons[.]org, the domain we saw on the machine, is only seventh on that list, so blocking it alone does very little.

| DOMAINS = [
(‘pymons.is’, ‘443’), (‘pur.st’, ‘443’), (‘workerstats.net’, ‘443’), (‘niceer.net’, ‘443’), (‘plaineagle.xyz’, ‘443’), (‘univalley.top’, ‘443’), (‘datamons.org’, ‘443’), (‘supertrap.net’, ‘443’), (‘icedhatch.top’, ‘443’), (‘icedhatch.xyz’, ‘443’), (‘icedout.top’, ‘443’)] |
Before doing anything visible, it checks where it is running. It asks ipinfo.io for the network owner and quits with a fake “Memory is insufficient” error if the answer contains Google, Amazon, Microsoft or Azure. Many automated sandboxes run on those clouds, so they never get to see the payloads.

If the machine passes, SysMon sends a host profile (hostname, IPs, MAC, OS, CPU, RAM and disks) and downloads the two packages. It checks each zip against a SHA-256 hash hard-coded in the script, so a tampered or sinkholed download is simply rejected. It also waits for the spy package to finish before fetching the stealer, so that the two large downloads don’t compete for the victim’s bandwidth.
A backdoor that only accepts signed code
Once the packages are running, SysMon becomes a backdoor. Every 60 seconds it asks all 11 domains for /global.txt, which is code meant for every victim, and asks for /get-onetime-code/<victim ID>, which is code for this machine only. It runs the code with exec() only if it carries a valid signature from the attacker’s RSA-2048 key, so nobody else can take over the botnet even if they seize a domain.

The miner manager: xm_handler.py
Its only job is to keep a crypto miner running without anyone noticing. It runs as SYSTEM and does not depend on the controller.
It downloads xmrig-v4-3333.zip from the same 11 domains, checks its hash and unpacks it into xmrig-v2\. The zip was gone when we looked, so we can’t see the coin or wallet. The older build mined Monero, so Monero is the most likely coin here too.

Every minute the script checks the Defender exclusions and, if its own are missing, adds C:\Windows as an excluded path and xmrig.exe as an excluded process. Excluding the whole Windows folder hides the entire kit from Defender scanning, and the exclusion stays behind after the malware is cleaned up.

And the most interesting trick is how it hides. Once a second, it checks whether any of 20 monitoring tools is open, including Task Manager, Resource Monitor, Process Explorer, Process Hacker, System Informer and Wireshark. If one is, it kills the miner straight away, so the user who opens Task Manager to find out why the fan is loud sees a quiet machine. Mining starts again about a second after the tool is closed. If a tool stays open for more than an hour, the script kills the tool instead.


The script also expects to be cleaned up. If xmrig.exe disappears, it waits longer before each reinstall, starting with 2 minutes, then 4, then 8, and so on up to 6 hours, so it doesn’t keep setting off alerts. That is exactly what we saw on the machine. The install marker was there and WinRing0x64.sys was still in the folder, but xmrig.exe itself was gone.

The spy module: basrq.py
It logs every key the user types, along with the window title, so a password can be matched to the site it was typed into. The log is uploaded every minute.
The key names are in five languages, mostly French, so the author probably built it on French Windows. That doesn’t limit the victims, though. Anyone who uses the fake download pages can be infected.
It also checks the clipboard twice a second. If a crypto address is copied, it swaps it for the attacker’s. The kit has nine addresses covering Bitcoin, Litecoin, Ethereum, Tron, XRP, Dogecoin, TON, Solana and Monero. Any other copied text, like a password, goes into the keylog.

The module also downloads a keyword list from the server every five minutes. When one of those words appears in a window title or in typed text, it takes a screenshot in memory and uploads it, so nothing is written to disk.
Three more scripts in the package show fake Windows notifications for Exodus, MetaMask and Phantom, with the real logos, saying something like “You received 18.2 SOL (~$2,911)“. The goal is to get the victim to open and unlock their wallet while the keylogger is running. Nothing in the kit starts them on its own, so the operator most likely triggers them through the backdoor.

The stealer: got.py
The Infame package runs got.py, which calls six helper modules in a fixed order. It starts by checking that the miner has already added the C:\Windows Defender exclusion, and waits 80 seconds if it hasn’t.

Discord tokens
The module, dt.py, steals Discord login tokens. A token is the saved login key that keeps a user signed in, and with it the attacker can log into the victim’s Discord account without the password or 2FA. It looks in the four Discord apps (Discord, Discord Canary, Discord PTB and Lightcord) and in 18 Chromium-based browsers, including Chrome, Edge, Brave, Opera, Opera GX, Vivaldi and Yandex. It reads each one’s Local Storage files, decrypts the tokens with the browser’s own key and sends them, with the victim’s public IP from api.ipify.org, to /infame/receive/dt.

Crypto wallets and seed phrases
The next module is wc.py, which copies the data files of about 20 wallet browser extensions, such as MetaMask, Phantom, Rabby, Ronin and TronLink, and 13 desktop wallets, such as Exodus, Electrum, Atomic and Coinomi. It closes the browsers first, because a running browser locks those files. The stealer doesn’t move any money itself. With the wallet files and the wallet password, which the keylogger may already have captured, the attacker can open the wallets on their own machine and move the funds.
| Wallet / Extension | Extension ID / Key |
| MetaMask | nkbihfbeogaeaoehlefnkodbefgpgknn |
| Phantom | bfnaelmomeimhlpmgjnjophhpkkoljpa |
| Ronin | fnjhmkhhmkbjkkabndcnnogagogbneec |
| Exodus | aholpfdialjgjfhomihkjbmgjidlcdno |
| Coin98 | aeachknmefphepccionboohckonoeemg |
| KardiaChain | pdadjkfkgcafgbceimcpbkalnfnepbnk |
| TerraStation | aiifbnbfobpmeekipheeijimdpnlpgpp |
| Wombat | amkmjjmmflddogmhpjloimipbofnfjih |
| Harmony | fnnegphlobjdpkhecapkijjdkgcjhkib |
| Nami | lpfcbjknijpeeillifnkikgncikgfhdo |
| MartianAptos | efbglgofoippbgcjepnhiblaibcnclgk |
| Braavos | jnlgamecbpmbajjfhmmmlhejkemejdma |
| XDEFI | hmeobnfnfcmdkdcmlblgagmfpfboieaf |
| Yoroi | ffnbelfdoeiohenkjibnmadjiehjhajb |
| TON | nphplpgoakhhjchkkhmiggakijnkhfnd |
| MetaMask_Edge | ejbalbakoplchlghecdalmeeeajnimhm |
| Tron | ibnejdfjmmkpcnlpebklmnkoeoihofec |
| Trust | egjidjbpglichdcondbcbdnbeeppgdph |
| Rabby | acmacodkjbdgmoleebolmdjonilkdbch |

The same module also starts a background search of every drive for seed phrases. It reads every .txt file and flags any line of 12, 15, 18, 21, 24 or 25 lowercase words that all come from the BIP-39 list, with at least 80% of them unique. Matching files are copied, zipped as seeds.zip and uploaded to /ws/<victim ID>. The word list it checks against is getthem\english.txt, which is the official BIP-39 English wordlist of exactly 2,048 words.

Browser passwords and cookies
For browser passwords, cookies and saved cards, the stealer doesn’t bother writing its own code. To get around Chrome’s App Bound Encryption, it asks the GitHub API for the latest release of ChromElevator, a public research tool, and falls back to a fixed v0.17.4 link if that fails.

It then gathers autofill data, SSH keys and a system and location profile (from ip-api.com), zips everything as <machine_id>.zip and uploads it to /infame/receive/zip.

Password managers
pms.py looks for password-manager vaults: KeePass (.kdbx, .kdb), 1Password (.opvault, .agilekeychain, 1password.sqlite), Bitwarden (data.json), Dashlane (dashlane.db), RoboForm (.rfo), PasswordSafe (.psafe3) and Enpass (vault.enpassdb). It checks each app’s own folder under AppData, and the user’s Desktop, Documents and Downloads, including the OneDrive copies. Every vault it finds is copied and uploaded in a second zip.

Infrastructure
When we checked on 31 October 2026, only two of the 11 domains still resolved: pymons[.]is at 186.2.175[.]158 and datamons[.]org at 186.2.163[.]193. Both are in the same ranges as the C2 servers in the Zscaler report, but at different addresses, so the operator has moved since then. Six of the 11 domains don’t appear in any public report we could find. Visiting datamons[.]org in a browser shows a bland decoy page for “SPC Data Services”, and asking for a C2 file directly just redirects to a login page.


wallet addresses

Conclusion
This build shows Armorsteal growing from a stealer into a full money-making kit. PyArmor hides the code, the cloud check keeps it away from sandboxes, the Defender exclusion keeps it away from scanning, and the miner disappears whenever someone looks. The 11-domain failover keeps it connected after any single domain is taken down.
Since every PyArmor rebuild changes the file hashes and the backdoor can change the kit’s behaviour without touching the disk, file hashes alone are a weak way to detect it. The behaviour around the files is much more stable: Python running from under C:\Windows, launched by scheduled tasks, adding Defender exclusions and starting a miner.
Indicators of compromise
| C2 Domains and IPs |
|---|
| Domains: pymons[.]is, pur[.]st, workerstats[.]net, niceer[.]net, plaineagle[.]xyz, univalley[.]top, datamons[.]org, supertrap[.]net, icedhatch[.]top, icedhatch[.]xyz, icedout[.]top |
| IPs: 186.2.175[.]158, 186.2.163[.]193 |
Host artifacts
| C:\Windows\SystemHealth\Update\
Scheduled tasks: SysCheck2, SysCheckX, SysHeal Defender exclusions: path C:\Windows, process xmrig.exe |
The wallet addresses and C2 URL paths are in the accompanying white paper.
MITRE ATT&CK
| Tactic | ID | Technique | Summary |
| Execution | T1059.006 | Python | Bundled portable Python runs obfuscated malware modules. |
| Execution | T1059.001 | PowerShell | Collects hardware data and modifies Defender settings. |
| Persistence / PrivEsc | T1053.005 | Scheduled Task | Maintains access via tasks (SysCheck2, SysCheckX, SysHeal). |
| Defense Evasion | T1027.013 | Encrypted Files | Uses PyArmor-encrypted script blocks. |
| Defense Evasion | T1036.004 | Masqueraded Tasks | Disguises malicious tasks as system health routines. |
| Defense Evasion | T1562.001 | Disable/Modify Tools | Adds Defender exclusions and kills monitoring utilities. |
| Defense Evasion | T1564.003 | Hidden Window | Runs background processes invisibly (CREATE_NO_WINDOW). |
| Defense Evasion | T1497.001 | Sandbox Evasion | Exits if the network belongs to Google, Amazon, Microsoft or Azure. |
| Defense Evasion | T1070.004 | File Deletion | Cleans up staging directories and temporary zip archives. |
| Credential Access | T1555.003 | Browser Credentials | Steals saved logins and autofill data from browsers. |
| Credential Access | T1555.005 | Password Managers | Targets vault files for KeePass, Bitwarden, 1Password, etc. |
| Credential Access | T1539 | Web Session Cookies | Extracts active browser cookies via remote debugging. |
| Credential Access | T1528 | Application Tokens | Exfiltrates Discord authentication tokens from local apps. |
| Credential Access | T1552.001 | Credentials in Files | Scans all local drives for crypto seed phrase text files. |
| Credential Access | T1552.004 | Private Keys | Harvests local SSH keys. |
| Credential / Collection | T1056.001 | Keylogging | Records keystrokes and active application titles. |
| Discovery | T1082 / T1016 | System / Network Info | Profiles hardware, OS version, MAC addresses, and local IPs. |
| Discovery | T1614 / T1057 | Location / Process Info | Checks public IP geolocation and scans for security tools. |
| Discovery | T1083 / T1518.001 | File / Security Discovery | Searches for wallets/configs and checks Defender status. |
| Collection | T1005 / T1115 | Local Data / Clipboard | Copies wallet files and monitors clipboard contents. |
| Collection | T1113 / T1560.002 | Screen Capture / Archive | Takes keyword-triggered screenshots and zips staged data. |
| Command & Control | T1071.001 / T1008 | Web Protocols / Fallback | Communicates over HTTPS with multiple failover domains. |
| Command & Control | T1105 | Ingress Tool Transfer | Downloads payloads like XMRig, ChromElevator, and zips. |
| Exfiltration | T1041 / T1029 | Exfil over C2 / Scheduled | Uploads collected archives and keylogs periodically. |
| Impact | T1496.001 | Compute Hijacking | Mines cryptocurrency in the background via XMRig. |
| Impact | T1565.002 | Data Manipulation | Swaps copied crypto wallet addresses in the clipboard. |
File Hashes (SHA-256)
| File | SHA-256 |
|---|---|
| SysMon.py | 972fe9edb13da76113047e6db39bcc44749aaeec1be107c975649f0c413a3067 |
| xm_handler.py | cb9db2b44476e160fdae0e840d62e7c5c0b07dc56d26d17acf82412e446274ca |
| nig\basrq.py | f1150680a69ca5e28f229e51c7b7bc9afa34f3e3a4ba424e3571b152009104b0 |
| nig\fakeex.py | 8619090392a4c483038f7de31ecfdc2d4689f61c305aaddbae8c89105cb5bd1a |
| nig\fakemet.py | 147852c1d363c57fedc8c89b2ee6713951dccfd2b06442b749e8e088261b72f2 |
| nig\fakeph.py | ae9d67418d0984629c4ff69293eac20391d23bc77b683670da2ae00c676f6b43 |
| getthem\got.py | 836e93a79361eb82a1c3758b79474ba5bb3546ea1234cc6002943b632e43ce5d |
| getthem\wc.py | 1d9f0eaa053cfdd38bfbafb922035d71f81c32bf1648f2668f7aa556b65da892 |
| getthem\dt.py | 73a00ffadce2b5edf80162e874335a772ed88dcc1fdba2612c7721094c210e38 |
| getthem\pms.py | 3b07d663b85dd21f0780bc0208eb44bb99c0cf2ef14f2b3afa6ab4f1ab23c14a |
| getthem\ac.py | 0c0349e3754e7b45690dfacab2c91c8727677e95d832eea1f4ff62a6203bb196 |
| getthem\sic.py | 5e113fea6eeeb888fec38feef5bfe2033ebe5fee49b03a582964e6424148a4ed |
| getthem\du.py | e2787ae4cd96b3884947ae7a5fe64f0f3224f212a5e7944b91cc4ec58ef883ec |
| getthem\getws.py | 17c125540d41a382946c3eaceaf29535ce67a52ef1775db4bb0a4289fec7f303 |
| getthem\newst.py | 84b436dede385f93b8bd0ebadd2b2dd3edf95f20da3e75b2f36bde7233ad0b85 |
| getthem\getst.py | 318677fd9169908af838f6ec338bf4b7230b8d3e0ddc61913a2ecbf6964b7bc6 |
| test20.py (both packages) | f959639d4b8fe0f40cec05cfdf32e7ffbbd8026104aee287db1340a85690dc59 |
| nig.zip | 19f1b7d054470801c4aaff25e73e231e6095b2c5e5b813f5af37f0a36bc66dd6 |
| getthem.zip | 96101754e96bc42949f3aefb9d0a3955b35d2c26ff604b604c29e98dc2aabb8a |
| xmrig-v4-3333.zip (pinned, not obtained) | db5e778414910445ace33cf3cc7cc9bc051a8bf8fb99990aa965d425e73d2e55 |
| xmrig-6.26-v2.zip (pinned, not obtained) | 253653f645b9c89e7e7e7e967926b0cf0f9adda98ac9d6ba4aa1eb354d80ab9d |
| chromelevator_x64.exe (public tool) | e857298fd2f8d1c7d48780769433f33e7b3ceaae5ea5a74c13ce8c10bcc7b690 |
| SysCheck2 (task XML) | 553883190491c4664e4701fc393c71bbfb9ca1c35e206612f1194d7b21e5d0df |
| SysCheckX (task XML) | 21811c2e54bb5595321f2feaf7e1075f317cd72039fb55294d3cb8ddcc6dfb7a |
| SysHeal (task XML) | 382b82f1bb1360f4df423b6e8fe90c20e5ce6c89065a439ac9c89c72a1b2f736 |
References
- Zscaler ThreatLabz, “SEO Poisoning Campaign Delivers New Armorsteal Malware”, 22 October 2026: https://www.zscaler.com/blogs/security-research/seo-poisoning-campaign-delivers-new-armorsteal-malware
- Luke Wilkinson (Blue Team Cool Team), “A Fake ‘SystemHealth’ Service, a Pyarmor Wall, and Three Ways to Get Paid”, 6 June 2026: https://blueteamcoolteam.pages.dev/posts/systemhealth-pyarmor-bundle/
- Pyarmor-Static-Unpack-1shot: https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot

