Single-page SOC-facing summary. Full analysis in the main report .

Rule packs

PackFileCoverage
YARA๐Ÿ“ฅ watcher-netai.yar5 rules: scanner, packed loader (SHA-pinned), unpacked loader (custom gopclntab), launcher script, systemd unit
Sigma๐Ÿ“ฅ linux_file_watcher_netai_user_systemd.ymlfile_event - user-systemd unit drop
Sigma๐Ÿ“ฅ linux_process_watcher_netai_astats.ymlprocess_creation - astats argv
Sigma๐Ÿ“ฅ linux_network_watcher_netai_loader_callback.ymlnetwork - Stage-1 loader C2
Sigma๐Ÿ“ฅ linux_network_watcher_netai_skn_c2.ymlnetwork - Stage-2 scanner C2
IOCs (CSV)๐Ÿ“ฅ watcher-netai.csv34 IOCs - defanged, with evidence_class and confidence columns

Top IOCs to load into your stack today

Network

IndicatorNotes
64[.]89[.]161[.]144Stage-1 loader-delivery C2. AS205759 Ghosty Networks LLC. URLhaus 3793559.
64[.]89[.]161[.]144:28816Stage-1 delivery port (plaintext HTTP). Per-session URL path /[A-Za-z0-9_]{32} stable in operator commands across 60+ days.
194[.]5[.]97[.]46Stage-2 scanner-operational C2 (current resolution). AS149020 WebHorizon, Netherlands.
connexionlost[.]net, connexionlost[.]zipStage-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)

IndicatorTierNotes
~/<user>/.config/systemd/user/watcher-netai.servicehighNon-root persistence, unique filename. Hunt first.
~/<user>/.config/systemd/user/default.target.wants/watcher-netai.servicehighEnablement symlink.
/dev/shm/astatshighScanner binary on victim.
/dev/shm/s.lock or /var/run/s.lockhighjuju/fslock single-instance lockfile.
/{dev/shm,tmp,var/run,mnt,root,}/w.sh (size 800-3000 bytes)highLauncher 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"highThe 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)mediumLoader file recovered empty post-deployment.

Hashes

SHA256Role
712737cdde50fcce895f263ea18ed7f8e425dcb72f8ec63101ea0361c6602140Loader (UPX-packed)
7a71a17bde3611a1b147f8946106d6da4d859c0e322fb2bc437cc6c3eddb5390Loader (unpacked, custom .gopclntab magic)
a1297637c8c4300618bf407de15682e26c142ba2028338f101e045ec91f1073dScanner 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:

  1. Bash history of the compromised account - the seven-step recon sequence (uname -s -v -n -r -m โ†’ nproc โ†’ … โ†’ lspci VGA) plus the cat > w.sh and cat > watcher-netai.service writes will be there if the operator’s session has not yet wiped history. A useful disambiguator: a lspci | grep VGA | grep Radeon | wc -l | head -c 1 line - the AMD-aware variant - appears in roughly a third of the captured sessions. If history -c && history -w ran (the operator does this on cleanup), the recon will be lost - the recovered launcher and systemd unit on disk are enough on their own.
  2. Outbound flow to 194[.]5[.]97[.]46:443 in NetFlow/Zeek - the scanner’s operational C2 traffic is the longer-lived network artefact and the easiest to fingerprint once persistence has landed.
  3. /etc/shadow last-changed timestamp on the compromised account - if it changed during the SSH session and the legitimate user did not authenticate the change, the changePasswordSmart cascade 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] .