From 8e17b85778b7d5e406e64be674897716c54a8036 Mon Sep 17 00:00:00 2001 From: Avioflagos Date: Sat, 29 Aug 2026 10:45:26 +0100 Subject: [PATCH] fix: Windows guard install, and lead every response path with rotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Windows user hit "could not create the scheduled task" with no further detail, having just found roughly fifteen infected repositories. guard install on Windows schtasks.exe is a Windows program and needs a Windows path for the executable it runs. It was being handed an MSYS path such as /usr/bin/bash, which cannot work. Worse, every error was discarded with 2>&1 >/dev/null, so the user saw a bare failure with nothing to act on and no way to report it usefully. It now converts the bash path with cygpath, sets MSYS_NO_PATHCONV so the /TN flags are not rewritten as paths, and PRINTS the actual schtasks error when it still fails. On failure it falls back to a Startup-folder entry, which needs no administrator rights, and if that is unavailable it explains how to run the guard by hand rather than leaving the user stuck. notify templates led with the wrong step Both the issue and the mail template said "delete your clone and re-clone" first and mentioned rotation second. Credentials are this family's objective, so rotation is first, npm write tokens ahead of everything else, and checking your own machine comes before cleaning any repository — cleaning first simply lets an infected machine re-inject. The mail template now spells out the full rotation list, including the clipboard, and warns about the Actions workflow that keeps exfiltrating secrets after the dropper is gone. docs: an incident-response walkthrough A new "If you're infected" section, placed before the command reference because that is the state someone arrives in. Rotate, clean the machine, then the repositories, then tell people — with the reasoning for the order rather than just the order. It also answers the question this user's output raised: `guard scan` reporting "clean" while repositories are infected is the expected result, not a contradiction. The loader runs when a build runs or an editor opens the folder, then exits. --- docs/_source.html | 89 +++++++++++++++++++++++++++++++++++++++++++++++ docs/index.html | 89 +++++++++++++++++++++++++++++++++++++++++++++++ lib/guard.sh | 68 ++++++++++++++++++++++++++++++------ lib/notify.sh | 73 ++++++++++++++++++++++++++++++++------ 4 files changed, 299 insertions(+), 20 deletions(-) diff --git a/docs/_source.html b/docs/_source.html index 2e90ea1..7c6f168 100644 --- a/docs/_source.html +++ b/docs/_source.html @@ -319,6 +319,7 @@ Reports Check your machine Install + If you're infected Commands Report a bug Install snare @@ -736,6 +737,94 @@

Pick your machine

+
+ Found something? +

What to do, in the order that matters

+

The instinct is to clean the repositories first. That is the wrong + order, and on an infected machine it is wasted work — this family injects into + commits on their way out, so it re-infects whatever you just cleaned.

+ +
+
    +
  1. 1 · Rotate your credentialsStealing them is the objective; the + file in your repository is delivery, not payload. Removing it does not un-steal + a token. npm write tokens first — a stolen one lets the worm publish + trojanised versions of your other packages under your name, which is how one + laptop becomes a supply-chain incident.
  2. +
  3. 2 · Clean the machine you push fromBefore touching any + repository. If several repositories are flagged, the common factor is almost + always the machine that pushed to them, not several separate accidents.
  4. +
  5. 3 · Then clean the repositoriessnare fix is a dry + run by default and always backs up first. Only add --push once you + have read what it would change.
  6. +
  7. 4 · Then tell your collaboratorsThey may be infected from the + same source, and if you rewrote history their clones will break with no + explanation.
  8. +
+ +

1 · Rotate

+
Do this first
+
snare rotate        # the ordered checklist, plus a GitHub audit
+

In order: npm tokens + (npmjs.com/settings/~/tokens — write and "bypass 2FA" first), then + GitHub PATs, OAuth apps and SSH keys, then cloud keys + (AWS — including Secrets Manager in every region you use — GCP, Azure), then + everything else the process could read: .env files in any project you + had open, kubeconfig, Vault tokens, database URLs, VPN, AI-service keys, wallet + keys and seed phrases. Finally your clipboard — this family has a + documented clipboard stealer, so a password pasted out of a manager while infected + should be treated as seen.

+ +

2 · Clean the machine

+
Before touching any repo
+
snare doctor          # machine, persistence spots, your GitHub account
+snare guard scan      # one-shot process check
+snare guard install   # kill the loader on sight, from login onwards
+

guard scan saying "clean" does not mean you + were never infected. It reports what is running right now. The + loader fires when a build runs or an editor opens the folder, does its work, and + exits. A clean process check alongside infected repositories is the expected + result, not a contradiction.

+ +

3 · Clean the repositories

+
Dry run first — always
+
snare fix owner/repo                        # dry run, changes nothing
+snare fix --all                             # dry run over everything flagged
+
+snare fix owner/repo --push                 # clean the branch tips
+snare fix owner/repo --purge-history --push # erase it from all history
+

--purge-history rewrites every commit, so every + SHA changes. Collaborators must delete their clone and re-clone; a + git pull will conflict or silently reintroduce old objects. Forks and + pull-request refs keep the old objects regardless — ask GitHub Support to + garbage-collect them if that matters to you.

+

Afterwards, verify rather than assume. snare scan github + only reads branch tips; run snare scan repo against a fresh clone to + walk full history.

+ +

4 · Tell the others

+
Contacts and templates
+
snare notify owner/repo             # list collaborators, preview the notice
+snare notify owner/repo --issue     # file a GitHub issue @-mentioning them
+snare notify owner/repo --mail      # open a pre-filled mail draft per person
+

Both templates lead with credential rotation and say plainly that + this is not an accusation: the malware injects from an already-infected machine, so + a collaborator whose commit carried it was almost certainly a victim rather than a + cause. Say that early, or you will not get the reply you need.

+ +

If a lot of repositories are flagged

+

Fifteen infected repositories is not fifteen accidents. It points at one machine + that pushed to all of them. Work out which machine that is before cleaning anything, + or you will clean the same repositories twice.

+

Worth knowing what you scaffold new projects with — a starter + template, a create-* command, a downloaded theme, an editor extension. + The delivery mechanism for this family is still unidentified, and + that is the single most useful thing anyone can contribute. If you find yours, + say so in the + discussion.

+
+
+
Reference

Every command, and what it will not do

diff --git a/docs/index.html b/docs/index.html index 9dcdb22..2524a6f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -356,6 +356,7 @@ Reports Check your machine Install + If you're infected Commands Report a bug Install snare @@ -773,6 +774,94 @@

Pick your machine

+
+ Found something? +

What to do, in the order that matters

+

The instinct is to clean the repositories first. That is the wrong + order, and on an infected machine it is wasted work — this family injects into + commits on their way out, so it re-infects whatever you just cleaned.

+ +
+
    +
  1. 1 · Rotate your credentialsStealing them is the objective; the + file in your repository is delivery, not payload. Removing it does not un-steal + a token. npm write tokens first — a stolen one lets the worm publish + trojanised versions of your other packages under your name, which is how one + laptop becomes a supply-chain incident.
  2. +
  3. 2 · Clean the machine you push fromBefore touching any + repository. If several repositories are flagged, the common factor is almost + always the machine that pushed to them, not several separate accidents.
  4. +
  5. 3 · Then clean the repositoriessnare fix is a dry + run by default and always backs up first. Only add --push once you + have read what it would change.
  6. +
  7. 4 · Then tell your collaboratorsThey may be infected from the + same source, and if you rewrote history their clones will break with no + explanation.
  8. +
+ +

1 · Rotate

+
Do this first
+
snare rotate        # the ordered checklist, plus a GitHub audit
+

In order: npm tokens + (npmjs.com/settings/~/tokens — write and "bypass 2FA" first), then + GitHub PATs, OAuth apps and SSH keys, then cloud keys + (AWS — including Secrets Manager in every region you use — GCP, Azure), then + everything else the process could read: .env files in any project you + had open, kubeconfig, Vault tokens, database URLs, VPN, AI-service keys, wallet + keys and seed phrases. Finally your clipboard — this family has a + documented clipboard stealer, so a password pasted out of a manager while infected + should be treated as seen.

+ +

2 · Clean the machine

+
Before touching any repo
+
snare doctor          # machine, persistence spots, your GitHub account
+snare guard scan      # one-shot process check
+snare guard install   # kill the loader on sight, from login onwards
+

guard scan saying "clean" does not mean you + were never infected. It reports what is running right now. The + loader fires when a build runs or an editor opens the folder, does its work, and + exits. A clean process check alongside infected repositories is the expected + result, not a contradiction.

+ +

3 · Clean the repositories

+
Dry run first — always
+
snare fix owner/repo                        # dry run, changes nothing
+snare fix --all                             # dry run over everything flagged
+
+snare fix owner/repo --push                 # clean the branch tips
+snare fix owner/repo --purge-history --push # erase it from all history
+

--purge-history rewrites every commit, so every + SHA changes. Collaborators must delete their clone and re-clone; a + git pull will conflict or silently reintroduce old objects. Forks and + pull-request refs keep the old objects regardless — ask GitHub Support to + garbage-collect them if that matters to you.

+

Afterwards, verify rather than assume. snare scan github + only reads branch tips; run snare scan repo against a fresh clone to + walk full history.

+ +

4 · Tell the others

+
Contacts and templates
+
snare notify owner/repo             # list collaborators, preview the notice
+snare notify owner/repo --issue     # file a GitHub issue @-mentioning them
+snare notify owner/repo --mail      # open a pre-filled mail draft per person
+

Both templates lead with credential rotation and say plainly that + this is not an accusation: the malware injects from an already-infected machine, so + a collaborator whose commit carried it was almost certainly a victim rather than a + cause. Say that early, or you will not get the reply you need.

+ +

If a lot of repositories are flagged

+

Fifteen infected repositories is not fifteen accidents. It points at one machine + that pushed to all of them. Work out which machine that is before cleaning anything, + or you will clean the same repositories twice.

+

Worth knowing what you scaffold new projects with — a starter + template, a create-* command, a downloaded theme, an editor extension. + The delivery mechanism for this family is still unidentified, and + that is the single most useful thing anyone can contribute. If you find yours, + say so in the + discussion.

+
+
+
Reference

Every command, and what it will not do

diff --git a/lib/guard.sh b/lib/guard.sh index 7a7f511..3142761 100644 --- a/lib/guard.sh +++ b/lib/guard.sh @@ -214,20 +214,68 @@ UNIT # ---- Windows (Git Bash): Scheduled Task ---------------------------------- guard_svc_schtasks(){ - local name="snare-guard" bash_exe + local name="snare-guard" bash_exe snare_sh err rc bash_exe="$(command -v bash)" + snare_sh="$SNARE_ROOT/bin/snare" + + # schtasks.exe is a Windows program: it needs a Windows path for the + # executable. Handing it an MSYS path like /usr/bin/bash fails, and the old + # code discarded stderr so the user only ever saw "could not create the + # scheduled task" with nothing to act on. + if command -v cygpath >/dev/null 2>&1; then + bash_exe="$(cygpath -w "$bash_exe" 2>/dev/null || command -v bash)" + fi + # MSYS_NO_PATHCONV stops MSYS rewriting /TN and friends into paths. + export MSYS_NO_PATHCONV=1 + case "$1" in install) - schtasks //Create //TN "$name" //SC ONLOGON //RL LIMITED //F \ - //TR "\"$bash_exe\" -lc 'snare guard run --interval 1'" >/dev/null 2>&1 \ - && grn "scheduled task '$name' created (runs at logon)" || red "could not create the scheduled task" - schtasks //Run //TN "$name" >/dev/null 2>&1 && grn "started" ;; - uninstall) schtasks //Delete //TN "$name" //F >/dev/null 2>&1 && ylw "guard uninstalled" ;; - start) schtasks //Run //TN "$name" >/dev/null 2>&1 && grn "started" ;; - stop) schtasks //End //TN "$name" >/dev/null 2>&1 && ylw "stopped" ;; + err="$(schtasks /Create /TN "$name" /SC ONLOGON /RL LIMITED /F \ + /TR "\"$bash_exe\" -c \"'$snare_sh' guard run --interval 1\"" 2>&1)"; rc=$? + if [ "$rc" = 0 ]; then + grn "scheduled task '$name' created (runs at logon)" + schtasks /Run /TN "$name" >/dev/null 2>&1 && grn "started" + return 0 + fi + red "could not create the scheduled task" + [ -n "$err" ] && echo "$err" | sed 's/^/ /' + echo + # A Startup-folder shortcut needs no privileges and no Task Scheduler. + ylw " Falling back to a Startup entry (no admin rights needed)." + local startup + startup="$(cygpath -u "$APPDATA" 2>/dev/null)/Microsoft/Windows/Start Menu/Programs/Startup" + if [ -d "$startup" ]; then + cat > "$startup/snare-guard.bat" </dev/null 2>&1 && ylw "guard uninstalled" + local startup + startup="$(cygpath -u "$APPDATA" 2>/dev/null)/Microsoft/Windows/Start Menu/Programs/Startup" + [ -f "$startup/snare-guard.bat" ] && { rm -f "$startup/snare-guard.bat"; ylw "startup entry removed"; } + return 0 ;; + start) schtasks /Run /TN "$name" >/dev/null 2>&1 && grn "started" || \ + dim "start it manually: snare guard run --interval 1 &" ;; + stop) schtasks /End /TN "$name" >/dev/null 2>&1 && ylw "stopped" ;; status) - if schtasks //Query //TN "$name" >/dev/null 2>&1; then grn "scheduled task '$name' registered" - else ylw "guard not registered — 'snare guard install'"; fi + if schtasks /Query /TN "$name" >/dev/null 2>&1; then grn "scheduled task '$name' registered" + else + local startup + startup="$(cygpath -u "$APPDATA" 2>/dev/null)/Microsoft/Windows/Start Menu/Programs/Startup" + if [ -f "$startup/snare-guard.bat" ]; then grn "startup entry registered" + else ylw "guard not registered — 'snare guard install'"; fi + fi _guard_common_status ;; esac } diff --git a/lib/notify.sh b/lib/notify.sh index 81f633d..875fa47 100644 --- a/lib/notify.sh +++ b/lib/notify.sh @@ -53,13 +53,41 @@ grep -rn "folderOpen" .vscode/tasks.json 2>/dev/null head -c 4 public/fonts/*.woff2 2>/dev/null # a real font prints wOF2 \`\`\` -### Then +### Then, in this order + +**1. Rotate your credentials first.** Stealing them is what this family is *for* — +the file in the repository is delivery, not payload. Removing it does not +un-steal a token. + +- **npm tokens first** — https://www.npmjs.com/settings/~/tokens — start with + write / "bypass 2FA". A stolen npm token lets the worm publish trojanised + versions of *your other packages* under your name. This is the step that + stops one machine becoming a supply-chain incident. +- GitHub PATs and OAuth apps — https://github.com/settings/tokens — and SSH + keys — https://github.com/settings/keys +- Cloud keys (AWS — check Secrets Manager per region — GCP, Azure) +- \`.env\` files in any project you had open, kubeconfig, Vault, database URLs, + VPN, AI-service keys, wallet keys and seed phrases +- **Your clipboard.** This family has a documented clipboard stealer; a password + pasted out of a manager while infected should be treated as seen. + +**2. Check your own machine before you clean anything.** This family injects +into commits *from an already-infected machine*, so cleaning a repository while +your machine is still infected just re-injects into it. -1. **Delete your local clone and re-clone.** If history was rewritten, a \`git pull\` - will conflict — you must re-clone. -2. **Rotate credentials**: GitHub tokens (github.com/settings/tokens), SSH keys, - npm tokens, \`.env\` values, and anything copied to your clipboard. -3. Check your GitHub account for repos, keys or OAuth apps you did not create. +\`\`\`bash +snare doctor # this machine, persistence spots, your account +snare guard scan # one-shot process check +snare rotate # the checklist above, plus a GitHub audit +\`\`\` + +**3. Delete your local clone and re-clone.** If history was rewritten a +\`git pull\` will conflict — you must re-clone, not pull. + +**4. Check your GitHub account** for repositories, keys, OAuth apps or Actions +workflows you did not create. This family leaves behind a workflow that +exfiltrates secrets on every push, and it survives long after the dropper is +removed. Scanned and cleaned with [snare](https://github.com/) — \`snare scan repo .\` to check your own clones. MD @@ -149,10 +177,35 @@ PLEASE CHECK: grep -rn "0xa322E5f3D311D3080e6f0121063e9aDC2490Ef1a" . head -c 4 public/fonts/*.woff2 -THEN: -- Delete old clones and re-clone (a pull may conflict if history was rewritten). -- Rotate GitHub tokens, SSH keys, npm tokens, .env values, and anything you - copied to your clipboard. Check your account for repos/keys you did not create. +THEN, IN THIS ORDER: + +1. ROTATE YOUR CREDENTIALS FIRST. Stealing them is what this family is for - + the file in the repo is delivery, not payload. Removing it does not + un-steal a token. + - npm tokens first (write / "bypass 2FA"): + https://www.npmjs.com/settings/~/tokens + A stolen npm token lets it publish trojanised versions of your other + packages under your name. + - GitHub PATs and OAuth apps: https://github.com/settings/tokens + - SSH keys: https://github.com/settings/keys + - Cloud keys (AWS - check Secrets Manager per region - GCP, Azure) + - .env files, kubeconfig, Vault, database URLs, VPN, AI-service keys, + crypto wallet keys and seed phrases + - Your clipboard: this family has a clipboard stealer, so anything you + copied while infected should be treated as seen. + +2. CHECK YOUR MACHINE before cleaning any repo - this family injects from an + already-infected machine, so cleaning first just lets it re-inject: + snare doctor + snare guard scan + snare rotate + +3. Delete old clones and re-clone (a pull will conflict if history was + rewritten - you must re-clone, not pull). + +4. Check your GitHub account for repos, keys, OAuth apps or Actions workflows + you did not create. This family leaves a workflow that exfiltrates secrets + on every push and survives the dropper being removed. Happy to walk you through it. """