@gendigital/sage-opencode 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -74
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +230 -96
- package/dist/mcp-server.cjs.map +4 -4
- package/dist/plugin-discovery.d.ts +1 -0
- package/dist/plugin-discovery.d.ts.map +1 -1
- package/dist/plugin-discovery.js +27 -1
- package/dist/plugin-discovery.js.map +1 -1
- package/dist/startup-scan.d.ts +11 -3
- package/dist/startup-scan.d.ts.map +1 -1
- package/dist/startup-scan.js +59 -21
- package/dist/startup-scan.js.map +1 -1
- package/dist/tool-handler.d.ts +2 -1
- package/dist/tool-handler.d.ts.map +1 -1
- package/dist/tool-handler.js +17 -4
- package/dist/tool-handler.js.map +1 -1
- package/package.json +3 -3
- package/resources/threats/_macros.yaml +193 -0
- package/resources/threats/agent-layer.yaml +19 -1
- package/resources/threats/commands.yaml +165 -31
- package/resources/threats/credentials.yaml +24 -4
- package/resources/threats/dummy.yaml +52 -17
- package/resources/threats/files.yaml +38 -0
- package/resources/threats/mac-commands.yaml +118 -27
- package/resources/threats/mac-credentials.yaml +34 -8
- package/resources/threats/mac-files.yaml +20 -0
- package/resources/threats/mac-obfuscation.yaml +28 -6
- package/resources/threats/mac-persistence.yaml +30 -4
- package/resources/threats/mac-supply-chain.yaml +12 -3
- package/resources/threats/mitre.yaml +325 -62
- package/resources/threats/obfuscation.yaml +33 -8
- package/resources/threats/persistence.yaml +40 -5
- package/resources/threats/prompt-injection.yaml +58 -0
- package/resources/threats/self-defense.yaml +15 -2
- package/resources/threats/supply_chain.yaml +6 -1
- package/resources/threats/urls.yaml +10 -0
- package/resources/threats/win-commands.yaml +294 -60
- package/resources/threats/win-credentials.yaml +68 -9
- package/resources/threats/win-files.yaml +20 -0
- package/resources/threats/win-obfuscation.yaml +56 -8
- package/resources/threats/win-persistence.yaml +45 -7
- package/resources/threats/win-supply-chain.yaml +5 -1
|
@@ -5,20 +5,30 @@
|
|
|
5
5
|
|
|
6
6
|
# --- Pipe to shell (Remote Code Execution) ---
|
|
7
7
|
- id: "CLT-CMD-001"
|
|
8
|
+
version: 1
|
|
9
|
+
detection_name: "CMD:SageCommand-A [Heur]"
|
|
8
10
|
category: tool
|
|
9
11
|
severity: critical
|
|
10
12
|
confidence: 0.95
|
|
11
|
-
|
|
13
|
+
# {{CMD_POS}} + {{NOT_FILENAME}} on both sides of the pipe, same shape as
|
|
14
|
+
# CLT-CMD-009. Had no boundary at all. curl admits an optional .exe suffix
|
|
15
|
+
# -- it ships as a real Windows binary (built into Windows 10+).
|
|
16
|
+
pattern: "{{CMD_POS}}curl(?:\\.exe)?{{NOT_FILENAME}}\\s[^|]*\\|\\s*(?:{{SHELLS}}){{NOT_FILENAME}}"
|
|
12
17
|
match_on: command
|
|
13
18
|
title: "Remote code execution via curl pipe to shell"
|
|
14
19
|
expires_at: null
|
|
15
20
|
revoked: false
|
|
16
21
|
|
|
17
22
|
- id: "CLT-CMD-002"
|
|
23
|
+
version: 1
|
|
24
|
+
detection_name: "CMD:SageCommand-B [Heur]"
|
|
18
25
|
category: tool
|
|
19
26
|
severity: critical
|
|
20
27
|
confidence: 0.95
|
|
21
|
-
|
|
28
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-001. wget admits an
|
|
29
|
+
# optional .exe suffix -- real Windows builds exist (GnuWin32, Git for
|
|
30
|
+
# Windows).
|
|
31
|
+
pattern: "{{CMD_POS}}wget(?:\\.exe)?{{NOT_FILENAME}}\\s[^|]*\\|\\s*(?:{{SHELLS}}){{NOT_FILENAME}}"
|
|
22
32
|
match_on: command
|
|
23
33
|
title: "Remote code execution via wget pipe to shell"
|
|
24
34
|
expires_at: null
|
|
@@ -26,30 +36,41 @@
|
|
|
26
36
|
|
|
27
37
|
# --- Reverse shells ---
|
|
28
38
|
- id: "CLT-CMD-003"
|
|
39
|
+
version: 1
|
|
40
|
+
detection_name: "CMD:SageCommand-C [Heur]"
|
|
29
41
|
category: tool
|
|
30
42
|
severity: critical
|
|
31
43
|
confidence: 0.90
|
|
32
|
-
|
|
44
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009. Bare `\b` let
|
|
45
|
+
# the shell name match in quoted prose.
|
|
46
|
+
pattern: "{{CMD_POS}}(?:{{SHELLS}}){{NOT_FILENAME}}\\s+-i\\b[\\s\\S]{0,160}/dev/tcp/"
|
|
33
47
|
match_on: command
|
|
34
48
|
title: "Reverse shell via /dev/tcp"
|
|
35
49
|
expires_at: null
|
|
36
50
|
revoked: false
|
|
37
51
|
|
|
38
52
|
- id: "CLT-CMD-004"
|
|
53
|
+
version: 1
|
|
54
|
+
detection_name: "CMD:SageCommand-D [Heur]"
|
|
39
55
|
category: tool
|
|
40
56
|
severity: critical
|
|
41
57
|
confidence: 0.90
|
|
42
|
-
|
|
58
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
59
|
+
pattern: "{{CMD_POS}}nc{{NOT_FILENAME}}.*\\s-e\\s"
|
|
43
60
|
match_on: command
|
|
44
61
|
title: "Reverse shell via netcat -e"
|
|
45
62
|
expires_at: null
|
|
46
63
|
revoked: false
|
|
47
64
|
|
|
48
65
|
- id: "CLT-CMD-005"
|
|
66
|
+
version: 1
|
|
67
|
+
detection_name: "CMD:SageCommand-E [Heur]"
|
|
49
68
|
category: tool
|
|
50
69
|
severity: critical
|
|
51
70
|
confidence: 0.85
|
|
52
|
-
|
|
71
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009. Had no
|
|
72
|
+
# boundary at all.
|
|
73
|
+
pattern: "{{CMD_POS}}bash{{NOT_FILENAME}}\\s+-i\\s+>\\s*&\\s*/dev/"
|
|
53
74
|
match_on: command
|
|
54
75
|
title: "Interactive reverse shell via bash"
|
|
55
76
|
expires_at: null
|
|
@@ -57,40 +78,64 @@
|
|
|
57
78
|
|
|
58
79
|
# --- Destructive operations ---
|
|
59
80
|
- id: "CLT-CMD-006"
|
|
81
|
+
version: 1
|
|
82
|
+
detection_name: "CMD:SageCommand-F [Heur]"
|
|
60
83
|
category: tool
|
|
61
84
|
severity: critical
|
|
62
85
|
confidence: 0.95
|
|
63
|
-
|
|
86
|
+
# {{CMD_POS}} + {{NOT_FILENAME}} on each alternative's `rm`, same shape as
|
|
87
|
+
# CLT-CMD-009.
|
|
88
|
+
pattern: "{{CMD_POS}}rm{{NOT_FILENAME}}\\s+(-[a-zA-Z]*f[a-zA-Z]*\\s+)?(-[a-zA-Z]*r[a-zA-Z]*\\s+)?/(\\s|$|[;&|*])|{{CMD_POS}}rm{{NOT_FILENAME}}\\s+(-[a-zA-Z]*r[a-zA-Z]*\\s+)?(-[a-zA-Z]*f[a-zA-Z]*\\s+)?/(\\s|$|[;&|*])|{{CMD_POS}}rm{{NOT_FILENAME}}\\s+-[a-zA-Z]*rf[a-zA-Z]*\\s+/(\\s|$|[;&|*])"
|
|
64
89
|
match_on: command
|
|
65
90
|
title: "Recursive forced deletion from root"
|
|
66
91
|
expires_at: null
|
|
67
92
|
revoked: false
|
|
68
93
|
|
|
69
94
|
- id: "CLT-CMD-007"
|
|
95
|
+
version: 1
|
|
96
|
+
detection_name: "CMD:SageCommand-G [Heur]"
|
|
70
97
|
category: tool
|
|
71
98
|
severity: critical
|
|
72
99
|
confidence: 0.95
|
|
73
|
-
|
|
100
|
+
# {{CMD_POS}} + {{HAS_ARG}}, same shape as CLT-CMD-009. The fstype suffix
|
|
101
|
+
# is allowlisted ahead of {{NOT_FILENAME}}'s trailing-dot guard, since
|
|
102
|
+
# mkfs.<fstype> is the real invocation, not a filename, and the old
|
|
103
|
+
# `\bmkfs\b` matched it too. Suffixes are the actual mkfs.* binary names
|
|
104
|
+
# shipped by e2fsprogs, xfsprogs, btrfs-progs, dosfstools, exfatprogs,
|
|
105
|
+
# ntfs-3g, hfsprogs, udftools, util-linux, f2fs-tools, jfsutils,
|
|
106
|
+
# reiserfsprogs, nilfs-utils — not guessed shapes; a fabricated suffix like
|
|
107
|
+
# `fat32` is not a real binary and must not be added on spec alone.
|
|
108
|
+
pattern: "{{CMD_POS}}mkfs(?:\\.(?:ext[234]|xfs|btrfs|vfat|fat|msdos|exfat|ntfs|hfsplus|udf|minix|bfs|cramfs|f2fs|jfs|reiserfs|nilfs2))?{{NOT_FILENAME}}{{HAS_ARG}}"
|
|
74
109
|
match_on: command
|
|
75
110
|
title: "Filesystem format command"
|
|
76
111
|
expires_at: null
|
|
77
112
|
revoked: false
|
|
78
113
|
|
|
79
114
|
- id: "CLT-CMD-008"
|
|
115
|
+
version: 1
|
|
116
|
+
detection_name: "CMD:SageCommand-H [Heur]"
|
|
80
117
|
category: tool
|
|
81
118
|
severity: warning
|
|
82
119
|
confidence: 0.85
|
|
83
|
-
|
|
120
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
121
|
+
pattern: "{{CMD_POS}}dd{{NOT_FILENAME}}\\s+if=.*of=/dev/"
|
|
84
122
|
match_on: command
|
|
85
123
|
title: "Direct disk write via dd"
|
|
86
124
|
expires_at: null
|
|
87
125
|
revoked: false
|
|
88
126
|
|
|
89
127
|
- id: "CLT-CMD-009"
|
|
128
|
+
version: 1
|
|
129
|
+
detection_name: "CMD:SageCommand-I [Heur]"
|
|
90
130
|
category: tool
|
|
91
131
|
severity: critical
|
|
92
132
|
confidence: 0.90
|
|
93
|
-
|
|
133
|
+
# Requires the binary to actually be executed: a bare word boundary matched
|
|
134
|
+
# filenames (`shred.py`) and path segments (`shred-latency-probe`), and a
|
|
135
|
+
# whitespace boundary also accepted argument position (`man shred | head`).
|
|
136
|
+
# `{{CMD_POS}}` is the shared vocabulary for this — see threats/_macros.yaml
|
|
137
|
+
# and "Writing command-position patterns" in docs/developer-guide.md.
|
|
138
|
+
pattern: "{{CMD_POS}}shred{{NOT_FILENAME}}{{HAS_ARG}}"
|
|
94
139
|
match_on: command
|
|
95
140
|
title: "Secure file destruction via shred"
|
|
96
141
|
expires_at: null
|
|
@@ -98,10 +143,16 @@
|
|
|
98
143
|
|
|
99
144
|
# --- Download + execute chains ---
|
|
100
145
|
- id: "CLT-CMD-010"
|
|
146
|
+
version: 1
|
|
147
|
+
detection_name: "CMD:SageCommand-J [Heur]"
|
|
101
148
|
category: tool
|
|
102
149
|
severity: critical
|
|
103
150
|
confidence: 0.90
|
|
104
|
-
|
|
151
|
+
# {{CMD_POS}} + {{NOT_FILENAME}} on the fetcher, same shape as CLT-CMD-009,
|
|
152
|
+
# with an optional .exe suffix on each (real Windows binaries). `chmod`
|
|
153
|
+
# itself needs no boundary fix -- it's already anchored to a real
|
|
154
|
+
# separator (`&&`/`;`) literally in the pattern, not a bare mention.
|
|
155
|
+
pattern: "{{CMD_POS}}(curl|wget)(?:\\.exe)?{{NOT_FILENAME}}\\s[\\s\\S]*?(&&|;)\\s*chmod\\s+\\+x"
|
|
105
156
|
match_on: command
|
|
106
157
|
title: "Download and execute chain"
|
|
107
158
|
expires_at: null
|
|
@@ -109,16 +160,21 @@
|
|
|
109
160
|
|
|
110
161
|
# --- Privilege escalation ---
|
|
111
162
|
- id: "CLT-CMD-011"
|
|
163
|
+
version: 1
|
|
164
|
+
detection_name: "CMD:SageCommand-K [Heur]"
|
|
112
165
|
category: tool
|
|
113
166
|
severity: warning
|
|
114
167
|
confidence: 0.60
|
|
115
|
-
|
|
168
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
169
|
+
pattern: "{{CMD_POS}}chmod{{NOT_FILENAME}}\\s+777\\b"
|
|
116
170
|
match_on: command
|
|
117
171
|
title: "Overly permissive file permissions"
|
|
118
172
|
expires_at: null
|
|
119
173
|
revoked: false
|
|
120
174
|
|
|
121
175
|
- id: "CLT-CMD-012"
|
|
176
|
+
version: 1
|
|
177
|
+
detection_name: "CMD:SageCommand-L [Heur]"
|
|
122
178
|
category: tool
|
|
123
179
|
severity: warning
|
|
124
180
|
confidence: 0.60
|
|
@@ -130,10 +186,14 @@
|
|
|
130
186
|
|
|
131
187
|
# --- Data exfiltration ---
|
|
132
188
|
- id: "CLT-CMD-013"
|
|
189
|
+
version: 1
|
|
190
|
+
detection_name: "CMD:SageCommand-M [Heur]"
|
|
133
191
|
category: network_egress
|
|
134
192
|
severity: warning
|
|
135
193
|
confidence: 0.60
|
|
136
|
-
|
|
194
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009. Had no
|
|
195
|
+
# boundary at all. curl admits an optional .exe suffix, same as CLT-CMD-001.
|
|
196
|
+
pattern: "{{CMD_POS}}curl(?:\\.exe)?{{NOT_FILENAME}}\\s[^|;]*(-d|--data|--data-binary)\\s\\S*(/etc/passwd|/etc/shadow|\\.ssh/|id_rsa|credentials|secrets)"
|
|
137
197
|
match_on: command
|
|
138
198
|
title: "Possible data exfiltration of sensitive files"
|
|
139
199
|
expires_at: null
|
|
@@ -141,10 +201,18 @@
|
|
|
141
201
|
|
|
142
202
|
# --- Privileged pipe to shell ---
|
|
143
203
|
- id: "CLT-CMD-014"
|
|
204
|
+
version: 1
|
|
205
|
+
detection_name: "CMD:SageCommand-N [Heur]"
|
|
144
206
|
category: tool
|
|
145
207
|
severity: critical
|
|
146
208
|
confidence: 0.95
|
|
147
|
-
|
|
209
|
+
# {{CMD_POS}} + {{NOT_FILENAME}} on `sudo` itself, not on curl/wget --
|
|
210
|
+
# this rule means "privileged", so it must keep requiring the literal
|
|
211
|
+
# `sudo` wrapper rather than folding into {{CMD_POS}}'s generic wrapper
|
|
212
|
+
# allowance (which would also admit the unprivileged case CLT-CMD-001
|
|
213
|
+
# already covers). curl/wget admit an optional .exe suffix, same as
|
|
214
|
+
# CLT-CMD-001/002.
|
|
215
|
+
pattern: "{{CMD_POS}}sudo{{NOT_FILENAME}}\\s+(curl|wget)(?:\\.exe)?{{NOT_FILENAME}}\\s[^|]*\\|\\s*(?:{{SHELLS}}){{NOT_FILENAME}}"
|
|
148
216
|
match_on: command
|
|
149
217
|
title: "Privileged pipe-to-shell (sudo curl/wget piped to shell)"
|
|
150
218
|
expires_at: null
|
|
@@ -156,10 +224,14 @@
|
|
|
156
224
|
# Uses `[\s\S]` to span newlines (loader does not set the `s` flag) and a
|
|
157
225
|
# bounded non-greedy quantifier to avoid catastrophic backtracking.
|
|
158
226
|
- id: "CLT-CMD-015"
|
|
227
|
+
version: 1
|
|
228
|
+
detection_name: "CMD:SageCommand-O [Heur]"
|
|
159
229
|
category: tool
|
|
160
230
|
severity: warning
|
|
161
231
|
confidence: 0.60
|
|
162
|
-
|
|
232
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009. Had no
|
|
233
|
+
# boundary at all.
|
|
234
|
+
pattern: "{{CMD_POS}}python[3]?{{NOT_FILENAME}}\\s+-c\\s+[\"'][\\s\\S]{0,2048}?(?:os\\.(?:system|popen|exec[lv]?[ep]?e?)|subprocess\\.(?:run|call|Popen|check_output|check_call|getoutput|getstatusoutput)|__import__)\\s*\\("
|
|
163
235
|
match_on: command
|
|
164
236
|
title: "Python one-liner invoking shell/exec/subprocess call"
|
|
165
237
|
expires_at: null
|
|
@@ -167,43 +239,68 @@
|
|
|
167
239
|
|
|
168
240
|
# --- Indirect execution patterns ---
|
|
169
241
|
- id: "CLT-CMD-016"
|
|
242
|
+
version: 1
|
|
243
|
+
detection_name: "CMD:SageCommand-P [Heur]"
|
|
170
244
|
category: tool
|
|
171
245
|
severity: critical
|
|
172
246
|
confidence: 0.90
|
|
173
|
-
|
|
247
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
248
|
+
pattern: "{{CMD_POS}}bash{{NOT_FILENAME}}\\s+<\\(\\s*(curl|wget)\\b"
|
|
174
249
|
match_on: command
|
|
175
250
|
title: "Process substitution executing remote content"
|
|
176
251
|
expires_at: null
|
|
177
252
|
revoked: false
|
|
178
253
|
|
|
179
254
|
- id: "CLT-CMD-017"
|
|
255
|
+
version: 1
|
|
256
|
+
detection_name: "CMD:SageCommand-Q [Heur]"
|
|
180
257
|
category: tool
|
|
181
258
|
severity: warning
|
|
182
259
|
confidence: 0.85
|
|
183
|
-
|
|
260
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
261
|
+
pattern: "{{CMD_POS}}eval{{NOT_FILENAME}}[^;|]*\\b(curl|wget)\\b"
|
|
184
262
|
match_on: command
|
|
185
263
|
title: "Eval executing remote download command"
|
|
186
264
|
expires_at: null
|
|
187
265
|
revoked: false
|
|
188
266
|
|
|
189
267
|
- id: "CLT-CMD-018"
|
|
268
|
+
version: 1
|
|
269
|
+
detection_name: "CMD:SageCommand-R [Heur]"
|
|
190
270
|
category: tool
|
|
191
271
|
severity: warning
|
|
192
272
|
confidence: 0.85
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
|
|
273
|
+
# `xargs` is itself anchored with {{CMD_POS}} rather than a bare `\bxargs\b`
|
|
274
|
+
# (which fired on prose merely mentioning xargs). `{{NOT_COMPOUND_LEFT}}`
|
|
275
|
+
# around the dispatched shell/curl name was also a misapplication of that
|
|
276
|
+
# macro: it exists for identifiers that are argument *values* (a WMI class
|
|
277
|
+
# name), never program names, whereas the shell/curl here really is being
|
|
278
|
+
# run. `xargs` is already one of {{CMD_PREFIX}}'s wrapper tokens, so a
|
|
279
|
+
# second `{{CMD_PREFIX}}` application right after the anchored `xargs`
|
|
280
|
+
# naturally skips its own flags (`-I{}`, `-n1`, `-P4`, an intervening
|
|
281
|
+
# `sudo`, ...) before the dispatched program, the same way {{CMD_POS}}
|
|
282
|
+
# skips wrapper tokens before any other program name. Anchoring `xargs`
|
|
283
|
+
# itself (rather than folding it into a generic {{CMD_POS}} on the
|
|
284
|
+
# shell/curl name) keeps this rule scoped to xargs's `{}`-substitution
|
|
285
|
+
# danger specifically — `sudo bash -c "echo {}"` with no xargs anywhere is
|
|
286
|
+
# inert literal text, not a real invocation, and must not fire here (that's
|
|
287
|
+
# CLT-CMD-009/017's territory, not this rule's). curl/wget admit an
|
|
288
|
+
# optional .exe suffix, same as CLT-CMD-001/002; {{SHELLS}} does too for
|
|
289
|
+
# powershell/pwsh via the shared macro.
|
|
290
|
+
pattern: "{{CMD_POS}}xargs{{NOT_FILENAME}}\\s+{{CMD_PREFIX}}(?:(?:{{SHELLS}}){{NOT_FILENAME}}(?:\\s+-[\\w]*c\\b\\s+(?:\"(?!\\s*(?:head|cat|wc)\\b)[^\"\\r\\n]{0,240}(?:\\{\\}|\\$[@*]|\\$\\d)[^\"\\r\\n]{0,240}\"|'(?!\\s*(?:head|cat|wc)\\b)[^'\\r\\n]{0,240}(?:\\{\\}|\\$[@*]|\\$\\d)[^'\\r\\n]{0,240}')|(?!\\s+-[\\w]*c\\b))|(curl|wget)(?:\\.exe)?{{NOT_FILENAME}}[^|;\\r\\n]{0,400}(?:-d|--data|--data-binary|--upload-file|-T|--post-file|--post-data|--body-file)\\b)"
|
|
197
291
|
match_on: command
|
|
198
292
|
title: "xargs dispatching download or shell execution"
|
|
199
293
|
expires_at: null
|
|
200
294
|
revoked: false
|
|
201
295
|
|
|
202
296
|
- id: "CLT-CMD-019"
|
|
297
|
+
version: 1
|
|
298
|
+
detection_name: "CMD:SageCommand-S [Heur]"
|
|
203
299
|
category: tool
|
|
204
300
|
severity: warning
|
|
205
301
|
confidence: 0.85
|
|
206
|
-
|
|
302
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
303
|
+
pattern: "{{CMD_POS}}find{{NOT_FILENAME}}[^|;]*-exec\\s+(?:{{SHELLS}}|curl|wget)\\b"
|
|
207
304
|
match_on: command
|
|
208
305
|
title: "find -exec executing shell or download commands"
|
|
209
306
|
expires_at: null
|
|
@@ -211,20 +308,26 @@
|
|
|
211
308
|
|
|
212
309
|
# --- DNS exfiltration ---
|
|
213
310
|
- id: "CLT-CMD-020"
|
|
311
|
+
version: 1
|
|
312
|
+
detection_name: "CMD:SageCommand-T [Heur]"
|
|
214
313
|
category: network_egress
|
|
215
314
|
severity: warning
|
|
216
315
|
confidence: 0.80
|
|
217
|
-
|
|
316
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
317
|
+
pattern: "{{CMD_POS}}(nslookup|dig|host){{NOT_FILENAME}}[^|;]*\\$\\([^)]+\\)\\.[a-zA-Z]"
|
|
218
318
|
match_on: command
|
|
219
319
|
title: "DNS exfiltration via command substitution as subdomain label"
|
|
220
320
|
expires_at: null
|
|
221
321
|
revoked: false
|
|
222
322
|
|
|
223
323
|
- id: "CLT-CMD-021"
|
|
324
|
+
version: 1
|
|
325
|
+
detection_name: "CMD:SageCommand-U [Heur]"
|
|
224
326
|
category: network_egress
|
|
225
327
|
severity: warning
|
|
226
328
|
confidence: 0.80
|
|
227
|
-
|
|
329
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-020.
|
|
330
|
+
pattern: "{{CMD_POS}}(nslookup|dig|host){{NOT_FILENAME}}[^|;]*`[^`]+`\\.[a-zA-Z]"
|
|
228
331
|
match_on: command
|
|
229
332
|
title: "DNS exfiltration via backtick substitution as subdomain label"
|
|
230
333
|
expires_at: null
|
|
@@ -232,10 +335,18 @@
|
|
|
232
335
|
|
|
233
336
|
# --- Loop-based indirect execution ---
|
|
234
337
|
- id: "CLT-CMD-022"
|
|
338
|
+
version: 1
|
|
339
|
+
detection_name: "CMD:SageCommand-V [Heur]"
|
|
235
340
|
category: tool
|
|
236
341
|
severity: warning
|
|
237
342
|
confidence: 0.80
|
|
238
|
-
|
|
343
|
+
# {{CMD_POS}} anchors the loop keyword itself so prose merely describing
|
|
344
|
+
# the shape (`echo "for url in $URLS; do curl $url | sh; done ..."`) no
|
|
345
|
+
# longer fires. The bare `\b` on the inner shell/curl/wget mentions is left
|
|
346
|
+
# as-is: they're only reachable once a real `for`/`while ...; do` has
|
|
347
|
+
# already matched at command position, so they aren't an independent
|
|
348
|
+
# prose-mention surface the way the outer keyword was.
|
|
349
|
+
pattern: "{{CMD_POS}}(while|for){{NOT_FILENAME}}[^;]*;\\s*do[^;]*(\\b(?:{{SHELLS}})\\s+-c\\b|\\b(curl|wget)\\b[^;]*\\|\\s*(?:sudo\\s+)?(?:env\\s+)?(?:[^\\s\"']*[\\\\/])?(?:{{SHELLS}})\\b|\\b(curl|wget)\\b[^;]*(?:(-d|--data|--data-binary)\\s+['\\\"]?@\\S+|(--upload-file|-T)\\s+['\\\"]?\\S+))"
|
|
239
350
|
match_on: command
|
|
240
351
|
title: "Loop executing shell or download commands"
|
|
241
352
|
expires_at: null
|
|
@@ -245,10 +356,14 @@
|
|
|
245
356
|
|
|
246
357
|
# Python socket reverse shell
|
|
247
358
|
- id: "CLT-CMD-023"
|
|
359
|
+
version: 1
|
|
360
|
+
detection_name: "CMD:SageCommand-W [Heur]"
|
|
248
361
|
category: tool
|
|
249
362
|
severity: critical
|
|
250
363
|
confidence: 0.90
|
|
251
|
-
|
|
364
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-015. Had no
|
|
365
|
+
# boundary at all.
|
|
366
|
+
pattern: "{{CMD_POS}}python[23]?{{NOT_FILENAME}}\\s+-c.*socket.*connect.*(\\/bin\\/zsh|\\/bin\\/sh|\\/bin\\/bash)"
|
|
252
367
|
match_on: command
|
|
253
368
|
title: "Python reverse shell via socket"
|
|
254
369
|
expires_at: null
|
|
@@ -256,10 +371,14 @@
|
|
|
256
371
|
|
|
257
372
|
# Ruby TCPSocket reverse shell
|
|
258
373
|
- id: "CLT-CMD-024"
|
|
374
|
+
version: 1
|
|
375
|
+
detection_name: "CMD:SageCommand-X [Heur]"
|
|
259
376
|
category: tool
|
|
260
377
|
severity: critical
|
|
261
378
|
confidence: 0.90
|
|
262
|
-
|
|
379
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009. Had no
|
|
380
|
+
# boundary at all.
|
|
381
|
+
pattern: "{{CMD_POS}}ruby{{NOT_FILENAME}}\\s+-e.*TCPSocket.*exec.*(\\/bin\\/zsh|\\/bin\\/sh)"
|
|
263
382
|
match_on: command
|
|
264
383
|
title: "Ruby reverse shell via TCPSocket"
|
|
265
384
|
expires_at: null
|
|
@@ -267,10 +386,13 @@
|
|
|
267
386
|
|
|
268
387
|
# zsh -c reverse shell pattern
|
|
269
388
|
- id: "CLT-CMD-025"
|
|
389
|
+
version: 1
|
|
390
|
+
detection_name: "CMD:SageCommand-Y [Heur]"
|
|
270
391
|
category: tool
|
|
271
392
|
severity: critical
|
|
272
393
|
confidence: 0.90
|
|
273
|
-
|
|
394
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
395
|
+
pattern: "{{CMD_POS}}zsh{{NOT_FILENAME}}\\s+-c\\b.*\\b(exec|socket|/dev/tcp)\\b"
|
|
274
396
|
match_on: command
|
|
275
397
|
title: "Zsh reverse shell pattern"
|
|
276
398
|
expires_at: null
|
|
@@ -278,10 +400,13 @@
|
|
|
278
400
|
|
|
279
401
|
# --- Recursive deletion of critical system directories ---
|
|
280
402
|
- id: "CLT-CMD-026"
|
|
403
|
+
version: 1
|
|
404
|
+
detection_name: "CMD:SageCommand-Z [Heur]"
|
|
281
405
|
category: tool
|
|
282
406
|
severity: critical
|
|
283
407
|
confidence: 0.95
|
|
284
|
-
|
|
408
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-006.
|
|
409
|
+
pattern: "{{CMD_POS}}rm{{NOT_FILENAME}}\\s+(-[a-zA-Z]+\\s+)*-[a-zA-Z]*[rR][a-zA-Z]*(\\s+-[a-zA-Z]+)*\\s+/(home|Users|etc|usr|var|boot|bin|sbin|lib|lib64|opt|root|System|Applications|Library|Volumes|private|mnt)\\b/?(\\s|$|[;&|])"
|
|
285
410
|
match_on: command
|
|
286
411
|
title: "Recursive deletion of critical system directory"
|
|
287
412
|
expires_at: null
|
|
@@ -289,10 +414,13 @@
|
|
|
289
414
|
|
|
290
415
|
# --- Deletion of .env files ---
|
|
291
416
|
- id: "CLT-CMD-027"
|
|
417
|
+
version: 1
|
|
418
|
+
detection_name: "CMD:SageCommand-AA [Heur]"
|
|
292
419
|
category: tool
|
|
293
420
|
severity: warning
|
|
294
421
|
confidence: 0.60
|
|
295
|
-
|
|
422
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-006.
|
|
423
|
+
pattern: "{{CMD_POS}}rm{{NOT_FILENAME}}\\s+(-{1,2}[\\w-]+(=\\S+)?\\s+)*(\\S+[\\\\/])?\\.env(?!\\S*\\.(example|sample|template|dist)\\b)(\\.(local|production|prod|staging|stage|development|dev|test))?\\b"
|
|
296
424
|
match_on: command
|
|
297
425
|
title: "Deletion of .env file"
|
|
298
426
|
expires_at: null
|
|
@@ -300,10 +428,13 @@
|
|
|
300
428
|
|
|
301
429
|
# --- Deletion of database files ---
|
|
302
430
|
- id: "CLT-CMD-028"
|
|
431
|
+
version: 1
|
|
432
|
+
detection_name: "CMD:SageCommand-AB [Heur]"
|
|
303
433
|
category: tool
|
|
304
434
|
severity: warning
|
|
305
435
|
confidence: 0.60
|
|
306
|
-
|
|
436
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-006.
|
|
437
|
+
pattern: "{{CMD_POS}}rm{{NOT_FILENAME}}\\s+(-{1,2}[\\w-]+(=\\S+)?\\s+)*\\S+\\.(db|sqlite|sqlite3)\\b"
|
|
307
438
|
match_on: command
|
|
308
439
|
title: "Deletion of database file"
|
|
309
440
|
expires_at: null
|
|
@@ -311,10 +442,13 @@
|
|
|
311
442
|
|
|
312
443
|
# --- Deletion of .git directory ---
|
|
313
444
|
- id: "CLT-CMD-029"
|
|
445
|
+
version: 1
|
|
446
|
+
detection_name: "CMD:SageCommand-AC [Heur]"
|
|
314
447
|
category: tool
|
|
315
448
|
severity: warning
|
|
316
449
|
confidence: 0.60
|
|
317
|
-
|
|
450
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-SELF-001.
|
|
451
|
+
pattern: "{{CMD_POS}}(rm|del|rmdir|rd|Remove-Item|unlink){{NOT_FILENAME}}.*(\\s|[\\\\/])\\.git(\\s|[\\\\/]|$)"
|
|
318
452
|
case_insensitive: true
|
|
319
453
|
match_on: command
|
|
320
454
|
title: "Deletion of .git directory"
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
# --- Known API key prefixes ---
|
|
6
6
|
- id: "CLT-CRED-001"
|
|
7
|
+
version: 1
|
|
8
|
+
detection_name: "CMD:SageCredential-A [Heur]"
|
|
7
9
|
category: secrets
|
|
8
10
|
severity: critical
|
|
9
11
|
confidence: 0.80
|
|
@@ -15,10 +17,13 @@
|
|
|
15
17
|
|
|
16
18
|
# --- Export of credential env vars ---
|
|
17
19
|
- id: "CLT-CRED-002"
|
|
20
|
+
version: 1
|
|
21
|
+
detection_name: "CMD:SageCredential-B [Heur]"
|
|
18
22
|
category: secrets
|
|
19
23
|
severity: warning
|
|
20
24
|
confidence: 0.60
|
|
21
|
-
|
|
25
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
26
|
+
pattern: "{{CMD_POS}}export{{NOT_FILENAME}}\\s+[A-Z_]*(API_KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL|AUTH)[A-Z_]*="
|
|
22
27
|
match_on: command
|
|
23
28
|
title: "Export of environment variable with credential keyword"
|
|
24
29
|
expires_at: null
|
|
@@ -26,6 +31,8 @@
|
|
|
26
31
|
|
|
27
32
|
# --- .env file writes ---
|
|
28
33
|
- id: "CLT-CRED-003"
|
|
34
|
+
version: 1
|
|
35
|
+
detection_name: "CMD:SageCredential-C [Heur]"
|
|
29
36
|
category: secrets
|
|
30
37
|
severity: warning
|
|
31
38
|
confidence: 0.60
|
|
@@ -37,10 +44,13 @@
|
|
|
37
44
|
|
|
38
45
|
# --- Reading credential files ---
|
|
39
46
|
- id: "CLT-CRED-004"
|
|
47
|
+
version: 1
|
|
48
|
+
detection_name: "CMD:SageCredential-D [Heur]"
|
|
40
49
|
category: secrets
|
|
41
50
|
severity: warning
|
|
42
51
|
confidence: 0.60
|
|
43
|
-
|
|
52
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
53
|
+
pattern: "{{CMD_POS}}(cat|less|more|head|tail){{NOT_FILENAME}}\\s+.*(\\.(env(?!\\S*\\.(example|sample|template|dist)\\b)(\\.(local|production|prod|staging|stage|development|dev|test))?)|credentials|\\.aws/credentials|\\.ssh/id_rsa|\\.ssh/id_ed25519|\\.netrc|\\.pgpass)"
|
|
44
54
|
match_on: command
|
|
45
55
|
title: "Reading credential or secret file"
|
|
46
56
|
expires_at: null
|
|
@@ -48,6 +58,8 @@
|
|
|
48
58
|
|
|
49
59
|
# --- Plaintext credential assignment ---
|
|
50
60
|
- id: "CLT-CRED-005"
|
|
61
|
+
version: 1
|
|
62
|
+
detection_name: "CMD:SageCredential-E [Heur]"
|
|
51
63
|
category: secrets
|
|
52
64
|
severity: warning
|
|
53
65
|
confidence: 0.60
|
|
@@ -59,6 +71,8 @@
|
|
|
59
71
|
|
|
60
72
|
# --- Long-form API key patterns ---
|
|
61
73
|
- id: "CLT-CRED-006"
|
|
74
|
+
version: 1
|
|
75
|
+
detection_name: "CMD:SageCredential-F [Heur]"
|
|
62
76
|
category: secrets
|
|
63
77
|
severity: critical
|
|
64
78
|
confidence: 0.92
|
|
@@ -70,13 +84,16 @@
|
|
|
70
84
|
|
|
71
85
|
# --- Copy/move/link secret files ---
|
|
72
86
|
- id: "CLT-CRED-007"
|
|
87
|
+
version: 1
|
|
88
|
+
detection_name: "CMD:SageCredential-G [Heur]"
|
|
73
89
|
category: secrets
|
|
74
90
|
severity: warning
|
|
75
91
|
confidence: 0.60
|
|
76
92
|
# Protected files: .env*, .aws/credentials, .ssh/(id_rsa|id_ed25519|id_ecdsa|id_dsa),
|
|
77
93
|
# .netrc, .pgpass, .npmrc, .pypirc, key.properties, .docker/config.json,
|
|
78
94
|
# kubeconfig, .kube/config, master.key, credentials.yml
|
|
79
|
-
|
|
95
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
96
|
+
pattern: "{{CMD_POS}}(cp|mv|ln|rsync|install|ditto){{NOT_FILENAME}}\\s+.*(?<=[\\\\/\\s])(\\.env(?!\\S*\\.(example|sample|template|dist)\\b)(\\.\\w+)?(?=[\\s\\\\/]|$)|\\.aws/credentials|\\.ssh/(id_rsa|id_ed25519|id_ecdsa|id_dsa)|\\.netrc|\\.pgpass|\\.npmrc|\\.pypirc|key\\.properties|\\.docker/config\\.json|kubeconfig|\\.kube/config|master\\.key|credentials\\.yml)"
|
|
80
97
|
match_on: command
|
|
81
98
|
title: "Copy/move/link of credential or secret file"
|
|
82
99
|
expires_at: null
|
|
@@ -84,13 +101,16 @@
|
|
|
84
101
|
|
|
85
102
|
# --- Archive secret files ---
|
|
86
103
|
- id: "CLT-CRED-008"
|
|
104
|
+
version: 1
|
|
105
|
+
detection_name: "CMD:SageCredential-H [Heur]"
|
|
87
106
|
category: secrets
|
|
88
107
|
severity: warning
|
|
89
108
|
confidence: 0.60
|
|
90
109
|
# Protected files: .env*, .aws/credentials, .ssh/(id_rsa|id_ed25519|id_ecdsa|id_dsa),
|
|
91
110
|
# .netrc, .pgpass, .npmrc, .pypirc, key.properties, .docker/config.json,
|
|
92
111
|
# kubeconfig, .kube/config, master.key, credentials.yml
|
|
93
|
-
|
|
112
|
+
# {{CMD_POS}} + {{NOT_FILENAME}}, same shape as CLT-CMD-009.
|
|
113
|
+
pattern: "{{CMD_POS}}(tar|zip|7z|rar|gzip){{NOT_FILENAME}}\\s+.*(?<=[\\\\/\\s])(\\.env(?!\\S*\\.(example|sample|template|dist)\\b)(\\.\\w+)?(?=[\\s\\\\/]|$)|\\.aws/credentials|\\.ssh/(id_rsa|id_ed25519|id_ecdsa|id_dsa)|\\.netrc|\\.pgpass|\\.npmrc|\\.pypirc|key\\.properties|\\.docker/config\\.json|kubeconfig|\\.kube/config|master\\.key|credentials\\.yml)"
|
|
94
114
|
match_on: command
|
|
95
115
|
title: "Archiving credential or secret file"
|
|
96
116
|
expires_at: null
|