Single-page SOC-facing summary. Full analysis in the main report .
Rule packs
| Pack | File | Coverage |
|---|---|---|
| YARA | ๐ฅ watcher-netai.yar | 5 rules: scanner, packed loader (SHA-pinned), unpacked loader (custom gopclntab), launcher script, systemd unit |
| Sigma | ๐ฅ linux_file_watcher_netai_user_systemd.yml | file_event - user-systemd unit drop |
| Sigma | ๐ฅ linux_process_watcher_netai_astats.yml | process_creation - astats argv |
| Sigma | ๐ฅ linux_network_watcher_netai_loader_callback.yml | network - Stage-1 loader C2 |
| Sigma | ๐ฅ linux_network_watcher_netai_skn_c2.yml | network - Stage-2 scanner C2 |
| IOCs (CSV) | ๐ฅ watcher-netai.csv | 34 IOCs - defanged, with evidence_class and confidence columns |
Top IOCs to load into your stack today
Network
| Indicator | Notes |
|---|---|
64[.]89[.]161[.]144 | Stage-1 loader-delivery C2. AS205759 Ghosty Networks LLC. URLhaus 3793559. |
64[.]89[.]161[.]144:28816 | Stage-1 delivery port (plaintext HTTP). Per-session URL path /[A-Za-z0-9_]{32} stable in operator commands across 60+ days. |
194[.]5[.]97[.]46 | Stage-2 scanner-operational C2 (current resolution). AS149020 WebHorizon, Netherlands. |
connexionlost[.]net, connexionlost[.]zip | Stage-2 C2 domains (current resolution 194[.]5[.]97[.]46). |
0a07365cc01fa9fc82608ba4019af499 (HASSH) | Brute-force fleet SSH client fingerprint. Caveat: also matches stock OpenSSH client - pair with destination context, e.g. SSH brute-force pattern toward non-root accounts, or post-event correlation with watcher-netai.service creation. |
Historical Stage-2 IPs (rotation cadence ~1-3 months) for retrospective hunts over the past 7 months:
88[.]214[.]57[.]157,5[.]249[.]160[.]243,103[.]179[.]44[.]146,194[.]5[.]97[.]15. Full details in the main report’s IOCs table .
Files (post-compromise)
| Indicator | Tier | Notes |
|---|---|---|
~/<user>/.config/systemd/user/watcher-netai.service | high | Non-root persistence, unique filename. Hunt first. |
~/<user>/.config/systemd/user/default.target.wants/watcher-netai.service | high | Enablement symlink. |
/dev/shm/astats | high | Scanner binary on victim. |
/dev/shm/s.lock or /var/run/s.lock | high | juju/fslock single-instance lockfile. |
/{dev/shm,tmp,var/run,mnt,root,}/w.sh (size 800-3000 bytes) | high | Launcher script. The operator’s cascade picks the first writable directory from /dev/shm โ /tmp โ /var/run โ /mnt โ /root โ /. |
Cron entries containing /tmp/w.sh "astats" "netai" "kstats" "ssh 2 ranges" | high | The operator writes a cron @reboot + hourly entry in cascade with the user-systemd unit, not as an alternative. Hunt both. |
~/<user>/.sysmonitor (0 bytes after the session ends) | medium | Loader file recovered empty post-deployment. |
Hashes
| SHA256 | Role |
|---|---|
712737cdde50fcce895f263ea18ed7f8e425dcb72f8ec63101ea0361c6602140 | Loader (UPX-packed) |
7a71a17bde3611a1b147f8946106d6da4d859c0e322fb2bc437cc6c3eddb5390 | Loader (unpacked, custom .gopclntab magic) |
a1297637c8c4300618bf407de15682e26c142ba2028338f101e045ec91f1073d | Scanner astats (operator-internal skn) |
Hunts to triage first
1. Non-root systemd-user persistence + cron cascade (file_event)
file.path : "*/.config/systemd/user/watcher-netai.service"
OR file.path : "*/.config/systemd/user/default.target.wants/watcher-netai.service"
Also pull current cron entries on hosts where you suspect compromise:
for u in $(cut -d: -f1 /etc/passwd); do
crontab -u "$u" -l 2>/dev/null \
| grep -F '/w.sh "astats" "netai" "kstats" "ssh 2 ranges"' \
&& echo "operator cron entry on user $u"
done
The exact filename is cluster-specific in our corpus and should be low-noise; validate against environments with custom user-systemd services. Triage: check creator process (should be bash or sh spawned from sshd), uid (should be โฅ 1000), and creation time (within last 24 h on a recent compromise). Note: filesystem * wildcards may be slow on Elastic indices without wildcard field type - anchor on the literal filename where possible.
2. Scanner process + launcher argv (process_creation)
Two AND-separated forms, more portable across SPL / Sigma backends than nested-quote escapes:
process.command_line : *astats* AND process.command_line : *netai*
AND process.command_line : *kstats* AND process.command_line : "*ssh 2 ranges*"
process.executable : "*/astats"
AND (process.command_line : *-watchdog* OR process.command_line : *-scan*)
AND process.command_line : "*ssh 2 ranges*"
The four-token launcher arg list is operator-defined and is not expected on benign hosts. The scanner runs as a long-lived process tree (watchdog parent + scan worker child).
3. Stage-1 loader-delivery C2 (network_connection / proxy logs)
destination.ip : "64.89.161.144" AND destination.port : 28816
OR url.original : /http:\/\/64\.89\.161\.144:28816\/[A-Za-z0-9_]{32}/
Plaintext HTTP on an unusual high port. The URL path is per-session random but the host:port pair is stable across the kit’s observed lifetime (URLhaus 3793559, plus operator-executed curl commands 60+ days later).
4. Stage-2 scanner-operational C2 (network_connection)
destination.ip : "194.5.97.46"
OR destination.domain : ("connexionlost.net" OR "connexionlost.zip")
OR (destination.port : 443
AND url.path : ("/update/scanfiles/*" OR "/update/pass/*"
OR "/harvest/sshsv" OR "/harvest/users" OR "/harvest/sshlist"))
Infrastructure matches (IP or domain) are high-confidence on their own. URL-path-only matches are gated by destination.port : 443 to keep noise low.
FP-testing status
YARA tested green. Zero false positives on a benign Linux Go corpus (kubectl, gh, jq, yq, hugo, caddy, helm, minikube, kind, fzf). True-positive match on each of the 5 intended targets. Validate against your own corpus before broad EDR rollout.
Sigma - pending. Field-developed signatures awaiting community validation.
Investigative pivot
If you confirm a hit on any tier-1 IOC, the most useful pivots are:
- Bash history of the compromised account - the seven-step recon sequence (
uname -s -v -n -r -mโnprocโ … โlspci VGA) plus thecat > w.shandcat > watcher-netai.servicewrites will be there if the operator’s session has not yet wiped history. A useful disambiguator: alspci | grep VGA | grep Radeon | wc -l | head -c 1line - the AMD-aware variant - appears in roughly a third of the captured sessions. Ifhistory -c && history -wran (the operator does this on cleanup), the recon will be lost - the recovered launcher and systemd unit on disk are enough on their own. - Outbound flow to
194[.]5[.]97[.]46:443in NetFlow/Zeek - the scanner’s operational C2 traffic is the longer-lived network artefact and the easiest to fingerprint once persistence has landed. /etc/shadowlast-changed timestamp on the compromised account - if it changed during the SSH session and the legitimate user did not authenticate the change, thechangePasswordSmartcascade has fired and the account is now operator-owned.
Controlled sharing
Verified CERTs, law-enforcement teams, hosting-provider abuse desks, and incident responders working a confirmed case may request extended C2 indicators, protocol notes, and the observed deployment timeline. Contact: research@ohiiho.com.
Full analysis, capability map, and threat-cluster context in the main report [Part 1/2] .