ONNX
security
malware-detection
File size: 12,961 Bytes
d2507b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
package bundle

import "sort"

// This file is the host-agnostic CAPABILITY / RISK surfacing layer that AUGMENTS
// (never replaces) the existing malicious/benign bundle label. It is a pure
// taxonomy + presentation layer over res.Findings already extracted by the
// analyzers: NO AST, NO new detection, NO Severity changes.
//
// The core robustness property: capabilities are driven by HOST-AGNOSTIC
// findings (what a script CAN DO), not by the corpus's own defanged exfil host.
// exfil-host-reference (which matches attacker.example literally) is demoted to
// evidence_only NETWORK_EGRESS support and never acts as a capability driver, so
// swapping attacker.example for a novel host does NOT silence the exfil-capable
// combo (which fires via the host-agnostic exfil-env-to-network source+sink
// co-occurrence).

// CapabilityName identifies one host-agnostic thing a bundled script can do.
type CapabilityName string

const (
	CapSecretAccess     CapabilityName = "SECRET_ACCESS"
	CapNetworkEgress    CapabilityName = "NETWORK_EGRESS"
	CapOpaqueExecution  CapabilityName = "OPAQUE_EXECUTION"
	CapDynamicFetchExec CapabilityName = "DYNAMIC_FETCH_EXEC"
	CapCodeExecution    CapabilityName = "CODE_EXECUTION"
	CapRegistryTamper   CapabilityName = "REGISTRY_TAMPER"
	CapFSDestructive    CapabilityName = "FS_DESTRUCTIVE"
	CapObfuscation      CapabilityName = "OBFUSCATION"
	CapAntiAnalysis     CapabilityName = "ANTI_ANALYSIS"
	CapPersistence      CapabilityName = "PERSISTENCE"
)

// RiskTier is the combos-only risk ranking surfaced alongside the back-compat
// label. REVIEW is pinned to the existing escalation decision; ELEVATED/INFO/
// CLEAN rank the non-escalating remainder by capability power.
type RiskTier string

const (
	TierClean    RiskTier = "CLEAN"
	TierInfo     RiskTier = "INFO"
	TierElevated RiskTier = "ELEVATED"
	TierReview   RiskTier = "REVIEW"
)

// Evidence is a single finding-derived datum supporting a capability. It carries
// the originating sibling/member, line, signal, and human-readable detail so the
// capability card can show operators exactly where the behavior was observed.
type Evidence struct {
	File   string `json:"file"`
	Line   int    `json:"line"`
	Signal string `json:"signal"`
	Detail string `json:"detail"`
}

// Capability is one host-agnostic capability present in a bundle, with its
// power class ("high"|"low") and the evidence that produced it.
type Capability struct {
	Name     string     `json:"name"`
	Power    string     `json:"power"`
	Evidence []Evidence `json:"evidence"`
}

// BigNasty is one big-nasty capability COMBINATION that fired, with a
// plain-English explanation of why it is dangerous. The big_nasties list is
// always computed and reported: it explains WHY a REVIEW verdict fired.
type BigNasty struct {
	Name string `json:"name"`
	Why  string `json:"why"`
}

// CapabilityProfile is the full host-agnostic risk surface for a bundle: the
// capabilities present, the big-nasty combos that fired, and the derived tier.
type CapabilityProfile struct {
	Capabilities []Capability `json:"capabilities,omitempty"`
	BigNasties   []BigNasty   `json:"big_nasties,omitempty"`
	Tier         RiskTier     `json:"risk_tier"`
}

// signalCapabilityMap maps each emitted Finding.Signal to the host-agnostic
// capabilities it drives. exfil-host-reference is deliberately NOT here as a
// driver β€” it is host-specific (matches attacker.example) and lives in
// evidenceOnlySignals/evidenceDecorates, only decorating NETWORK_EGRESS.
var signalCapabilityMap = map[string][]CapabilityName{
	"env-credential-access":            {CapSecretAccess},
	"exfil-env-to-network":             {CapSecretAccess, CapNetworkEgress},
	"remote-code-execution":            {CapDynamicFetchExec},
	"reverse-shell":                    {CapNetworkEgress, CapCodeExecution},
	"scheduled-network-callback":       {CapNetworkEgress, CapPersistence},
	"persistence":                      {CapPersistence},
	"eval-decoded-payload":             {CapOpaqueExecution, CapObfuscation},
	"ships-opaque-executable":          {CapOpaqueExecution},
	"opaque-bytecode":                  {CapOpaqueExecution},
	"ships-compiled-bytecode":          {CapOpaqueExecution},
	"suspicious-native-symbols":        {CapOpaqueExecution},
	"opaque-archive":                   {CapOpaqueExecution},
	"opaque-archive-member":            {CapOpaqueExecution},
	"opaque-image":                     {CapOpaqueExecution},
	"opaque-notebook":                  {CapOpaqueExecution},
	"compiled-source-mismatch":         {CapOpaqueExecution},
	"compiled-without-matching-source": {CapOpaqueExecution},
	"archive-contains-executable":      {CapOpaqueExecution, CapAntiAnalysis},
	"delegates-to-bundled-script":      {CapCodeExecution},
	"delegates-to-data":                {CapCodeExecution},
	"delegates-to-image":               {CapCodeExecution},
	"symlinked-executable-reference":   {CapCodeExecution},
	"allowed-tools-nl-directive":       {CapCodeExecution},
	"registry-rewrite":                 {CapRegistryTamper},
	"destructive-command":              {CapFSDestructive},
	"embedded-encoded-blob":            {CapObfuscation},
	"image-embedded-blob":              {CapObfuscation},
	"data-embedded-directive":          {CapObfuscation},
	"image-embedded-directive":         {CapObfuscation},
	"padding-evasion":                  {CapObfuscation},
	"archive-path-traversal":           {CapAntiAnalysis},
	"archive-bomb-guard":               {CapAntiAnalysis},
	"archive-too-deep":                 {CapAntiAnalysis},
	"archive-member-limit":             {CapAntiAnalysis},
}

// evidenceOnlySignals contribute evidence (or scan-coverage notes) but never act
// as a capability driver. exfil-host-reference is host-specific and is treated
// as supporting evidence for NETWORK_EGRESS only; references-unscanned-filetype
// and analyzer-error are scan-limitation notes (no capability at all).
var evidenceOnlySignals = map[string]bool{
	"exfil-host-reference":          true, // host-specific (attacker.example); decorates NETWORK_EGRESS only
	"references-unscanned-filetype": true, // scan-limitation note
	"analyzer-error":                true, // scan-limitation note
}

// evidenceDecorates maps a host-specific evidence_only signal to the capability
// its evidence attaches to. It NEVER creates the capability β€” the entry only
// appends evidence when that capability is already (or also) present-driven, or
// surfaces the host-specific marker under that capability's evidence list. The
// capability's PRESENCE for combo purposes is still governed by host-agnostic
// drivers in signalCapabilityMap.
var evidenceDecorates = map[string]CapabilityName{
	"exfil-host-reference": CapNetworkEgress,
}

// highPowerCaps marks the capabilities whose mere presence (without a combo)
// warrants ELEVATED. Low-power capabilities (CODE_EXECUTION-of-readable-script,
// OBFUSCATION, ANTI_ANALYSIS) only reach INFO on their own.
var highPowerCaps = map[CapabilityName]bool{
	CapSecretAccess:     true,
	CapNetworkEgress:    true,
	CapOpaqueExecution:  true,
	CapDynamicFetchExec: true,
	CapRegistryTamper:   true,
	CapFSDestructive:    true,
}

// BuildCapabilityProfile is a PURE function over the already-extracted findings.
// It does NO new detection and NO AST: it folds each Finding through the
// host-agnostic signal->capability map, attaches host-specific evidence to the
// capabilities it decorates, classifies power, and computes the big-nasty
// combos. The returned profile's Tier is left zero-valued (CLEAN's zero value is
// the empty string, not TierClean) and MUST be set by ComputeTier at the call
// site, which pins REVIEW to the existing escalation decision.
func BuildCapabilityProfile(findings []Finding) CapabilityProfile {
	present := map[CapabilityName][]Evidence{}
	signals := map[string]bool{}
	for _, f := range findings {
		signals[f.Signal] = true
		ev := Evidence{File: f.File, Line: f.Line, Signal: f.Signal, Detail: f.Detail}
		for _, c := range signalCapabilityMap[f.Signal] {
			present[c] = append(present[c], ev)
		}
		// Host-specific evidence decorates an existing/also-driven capability but
		// never drives it on its own β€” append only when the capability is already
		// present (driven by a host-agnostic finding). This guarantees no
		// capability can be conjured purely by the host-specific match.
		if dec, ok := evidenceDecorates[f.Signal]; ok {
			if _, driven := present[dec]; driven {
				present[dec] = append(present[dec], ev)
			}
		}
	}

	caps := make([]Capability, 0, len(present))
	for name, evs := range present {
		power := "low"
		if highPowerCaps[name] {
			power = "high"
		}
		caps = append(caps, Capability{Name: string(name), Power: power, Evidence: evs})
	}
	sortCaps(caps)

	return CapabilityProfile{
		Capabilities: caps,
		BigNasties:   detectBigNasties(present, signals),
	}
}

// sortCaps orders capabilities deterministically by name so JSON/SARIF output
// and test assertions are stable across runs (map iteration is unordered).
func sortCaps(caps []Capability) {
	sort.Slice(caps, func(i, j int) bool {
		return caps[i].Name < caps[j].Name
	})
}

// detectBigNasties computes the host-agnostic capability COMBINATIONS that drive
// the "why" behind a REVIEW. The combos are a documented SUPERSET of
// bundleEscalates's behavioral triggers so the plain-English explanation never
// contradicts the verdict. Critically, exfil-capable fires on the fused
// exfil-env-to-network finding (SECRET_ACCESS+NETWORK_EGRESS) OR on
// env-credential-access co-occurring with any NETWORK_EGRESS-driving sink β€” it
// NEVER requires exfil-host-reference, so it survives a host swap.
func detectBigNasties(present map[CapabilityName][]Evidence, signals map[string]bool) []BigNasty {
	has := func(c CapabilityName) bool { _, ok := present[c]; return ok }
	var out []BigNasty

	if signals["reverse-shell"] {
		out = append(out, BigNasty{
			Name: "remote-shell",
			Why:  "Opens a reverse/bind shell β€” hands an attacker an interactive shell on the host, independent of any destination string.",
		})
	}
	if signals["scheduled-network-callback"] {
		out = append(out, BigNasty{
			Name: "scheduled-phone-home",
			Why:  "Installs a persistent/scheduled job that performs network I/O β€” a recurring callout that survives restarts.",
		})
	}

	if has(CapSecretAccess) && has(CapNetworkEgress) {
		out = append(out, BigNasty{
			Name: "exfil-capable",
			Why:  "Reads a credential AND opens a network sink β€” can steal secrets and ship them off-box, regardless of destination host.",
		})
	}
	if has(CapOpaqueExecution) {
		out = append(out, BigNasty{
			Name: "runs-uninspectable-code",
			Why:  "Executes code that cannot be read (compiled/opaque artifact or source-mismatched binary, the xz pattern).",
		})
	}
	if has(CapDynamicFetchExec) {
		out = append(out, BigNasty{
			Name: "fetch-and-run",
			Why:  "Downloads remote code and runs it immediately β€” payload is whatever the server returns at runtime.",
		})
	}
	if has(CapObfuscation) && (has(CapNetworkEgress) || has(CapCodeExecution) || has(CapOpaqueExecution)) {
		out = append(out, BigNasty{
			Name: "hidden-and-active",
			Why:  "Hidden/encoded content combined with an active execution or egress capability β€” concealing live behavior.",
		})
	}
	if has(CapRegistryTamper) {
		out = append(out, BigNasty{
			Name: "external-registry-redirect",
			Why:  "Rewrites package/registry config to redirect installs to an external endpoint β€” supply-chain redirect.",
		})
	}
	if has(CapFSDestructive) {
		out = append(out, BigNasty{
			Name: "data-destruction",
			Why:  "Issues mass-destructive filesystem commands capable of wiping user data.",
		})
	}

	return out
}

// ComputeTier ranks a bundle into a RiskTier. REVIEW is PINNED to the existing
// label decision (escalatedOrMalicious := scorerMalicious || bundleEscalates) so
// the derived tier β€” and therefore the derived label β€” can NEVER diverge from
// today's bundle label on the corpus. The combo taxonomy does NOT gate REVIEW;
// it only ranks the NON-escalating remainder into ELEVATED/INFO/CLEAN by the
// power of the host-agnostic capabilities present.
func ComputeTier(escalatedOrMalicious bool, prof CapabilityProfile) RiskTier {
	if escalatedOrMalicious {
		return TierReview
	}
	high := false
	low := false
	for _, c := range prof.Capabilities {
		if c.Power == "high" {
			high = true
		} else {
			low = true
		}
	}
	switch {
	case high:
		return TierElevated
	case low:
		return TierInfo
	default:
		return TierClean
	}
}

// DeriveLabel keeps the back-compat bundle label byte-identical to today's:
// malicious iff REVIEW. Since REVIEW is pinned to (scorerMalicious ||
// bundleEscalates), this equals the exact OR that MakeBundleVerdict uses to set
// the label today, guaranteeing ZERO regression on the 240-bundle eval.
func DeriveLabel(tier RiskTier) string {
	if tier == TierReview {
		return "malicious"
	}
	return "benign"
}