Repository navigation
Expand file tree
/
Copy pathmalware-scan.sh
More file actions
114 lines (104 loc) · 5.26 KB
/
Copy pathmalware-scan.sh
File metadata and controls
114 lines (104 loc) · 5.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env bash
# SpeedGoat malware guard — dependency-free, cross-platform (macOS / Linux / Windows Git Bash).
#
# Detects the self-propagating JS worm's indicators:
# [dropper] the obfuscated global['!']= JS payload (config/font/any file)
# [gitignore] the worm's .gitignore markers (temp_auto_push / temp_interactive_push / branch_structure)
# [font] a .woff2/.woff that lacks its real magic header (JS disguised as a font)
# [wormfile] the worm's dropped working files on disk
# [autorun] a file that executes a font as a node script (VS Code tasks.json folderOpen, etc.)
#
# Modes:
# sh malware-scan.sh Sweep THIS repo / folder's working tree.
# sh malware-scan.sh --all [DIR] Sweep EVERY git repo under DIR (default: current dir).
# sh malware-scan.sh --staged Scan staged files only (used by the pre-commit hook).
#
# Exit code: 0 = clean, 1 = indicator(s) found.
# Dependencies: only tools bundled with Git (bash, grep, find, sed, head). No npm/python/etc.
set -u
SIG_JS="global['!']="
SIG_GI="temp_auto_push|temp_interactive_push|branch_structure"
WORM_FILES="temp_auto_push.bat temp_interactive_push.bat branch_structure.json"
SIG_EXEC='node[[:space:]][^[:space:]"]*[.]woff' # e.g. VS Code tasks.json running node on the fake font
# Scan a working-tree directory. Prints findings to stdout; returns 1 if any found.
detect_dir() {
d="$1"; f=0; tmp="$(mktemp)"
# 1. dropper signature (include binary-looking files; skip .git, node_modules, this scanner)
grep -rlF "$SIG_JS" "$d" --exclude-dir=.git --exclude-dir=node_modules 2>/dev/null \
| grep -v 'malware-scan\.sh' > "$tmp" || true
while IFS= read -r p; do [ -n "$p" ] && { printf " [dropper] %s\n" "$p"; f=1; }; done < "$tmp"
# 2. worm .gitignore markers
find "$d" -name .gitignore -not -path '*/.git/*' -not -path '*/node_modules/*' 2>/dev/null > "$tmp" || true
while IFS= read -r g; do
[ -n "$g" ] && grep -qE "$SIG_GI" "$g" 2>/dev/null && { printf " [gitignore] %s\n" "$g"; f=1; }
done < "$tmp"
# 3. disguised fonts (wrong magic header)
find "$d" \( -name '*.woff2' -o -name '*.woff' \) -not -path '*/.git/*' -not -path '*/node_modules/*' 2>/dev/null > "$tmp" || true
while IFS= read -r w; do
[ -n "$w" ] || continue
magic="$(head -c4 "$w" 2>/dev/null)"
case "$w" in
*.woff2) [ "$magic" = "wOF2" ] || { printf " [font] %s (bad magic)\n" "$w"; f=1; } ;;
*.woff) [ "$magic" = "wOFF" ] || { printf " [font] %s (bad magic)\n" "$w"; f=1; } ;;
esac
done < "$tmp"
# 4. dropped worm working files
for wf in $WORM_FILES; do
find "$d" -name "$wf" -not -path '*/.git/*' -not -path '*/node_modules/*' 2>/dev/null > "$tmp" || true
while IFS= read -r x; do [ -n "$x" ] && { printf " [wormfile] %s\n" "$x"; f=1; }; done < "$tmp"
done
# 5. auto-run trigger: any file that executes a font as a node script (the VS Code
# .vscode/tasks.json folderOpen task, npm scripts, shell profiles, ...)
grep -rlE "$SIG_EXEC" "$d" --exclude-dir=.git --exclude-dir=node_modules 2>/dev/null \
| grep -v 'malware-scan\.sh' > "$tmp" || true
while IFS= read -r a; do [ -n "$a" ] && { printf " [autorun] %s\n" "$a"; f=1; }; done < "$tmp"
rm -f "$tmp"
return $f
}
# Scan staged files (pre-commit). Prints findings; returns 1 if any found.
detect_staged() {
f=0; tmp="$(mktemp)"
git diff --cached --name-only --diff-filter=ACM > "$tmp" 2>/dev/null || true
while IFS= read -r file; do
[ -n "$file" ] || continue
case "$(basename "$file")" in malware-scan.sh) continue ;; esac
git show ":$file" 2>/dev/null | grep -qaF "$SIG_JS" && { printf " [dropper] %s\n" "$file"; f=1; }
[ "$(basename "$file")" = ".gitignore" ] && git show ":$file" 2>/dev/null | grep -qaE "$SIG_GI" \
&& { printf " [gitignore] %s\n" "$file"; f=1; }
case "$file" in
*.woff2) [ "$(git show ":$file" 2>/dev/null | head -c4)" = "wOF2" ] || { printf " [font] %s (bad magic)\n" "$file"; f=1; } ;;
*.woff) [ "$(git show ":$file" 2>/dev/null | head -c4)" = "wOFF" ] || { printf " [font] %s (bad magic)\n" "$file"; f=1; } ;;
esac
git show ":$file" 2>/dev/null | grep -qaE "$SIG_EXEC" && { printf " [autorun] %s\n" "$file"; f=1; }
done < "$tmp"
for wf in $WORM_FILES; do [ -e "$wf" ] && { printf " [wormfile] %s\n" "$wf"; f=1; }; done
rm -f "$tmp"
return $f
}
mode="${1:-worktree}"
case "$mode" in
--staged)
out="$(detect_staged)"; rc=$?
if [ "$rc" -ne 0 ]; then
printf 'MALWARE GUARD blocked this commit:\n%s\nRemove the indicators above, or (only if certain) bypass with: git commit --no-verify\n' "$out" >&2
exit 1
fi
exit 0 ;;
--all)
dir="${2:-.}"; any=0; tmp="$(mktemp)"
find "$dir" -type d -name .git -not -path '*/node_modules/*' -prune 2>/dev/null > "$tmp" || true
while IFS= read -r gd; do
repo="$(dirname "$gd")"
out="$(detect_dir "$repo")"; rc=$?
[ "$rc" -ne 0 ] && { printf '\n[X] %s\n%s\n' "$repo" "$out"; any=1; }
done < "$tmp"
rm -f "$tmp"
[ "$any" -eq 0 ] && printf 'All git repos under %s are clean.\n' "$dir"
exit "$any" ;;
-h|--help)
sed -n '2,20p' "$0"; exit 0 ;;
*)
out="$(detect_dir ".")"; rc=$?
if [ "$rc" -ne 0 ]; then printf 'Indicators found:\n%s\n' "$out"; exit 1; fi
printf 'malware-scan: clean\n'; exit 0 ;;
esac