| package bundle |
|
|
| import "sort" |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| 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" |
| ) |
|
|
| |
| |
| |
| type RiskTier string |
|
|
| const ( |
| TierClean RiskTier = "CLEAN" |
| TierInfo RiskTier = "INFO" |
| TierElevated RiskTier = "ELEVATED" |
| TierReview RiskTier = "REVIEW" |
| ) |
|
|
| |
| |
| |
| type Evidence struct { |
| File string `json:"file"` |
| Line int `json:"line"` |
| Signal string `json:"signal"` |
| Detail string `json:"detail"` |
| } |
|
|
| |
| |
| type Capability struct { |
| Name string `json:"name"` |
| Power string `json:"power"` |
| Evidence []Evidence `json:"evidence"` |
| } |
|
|
| |
| |
| |
| type BigNasty struct { |
| Name string `json:"name"` |
| Why string `json:"why"` |
| } |
|
|
| |
| |
| type CapabilityProfile struct { |
| Capabilities []Capability `json:"capabilities,omitempty"` |
| BigNasties []BigNasty `json:"big_nasties,omitempty"` |
| Tier RiskTier `json:"risk_tier"` |
| } |
|
|
| |
| |
| |
| |
| 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}, |
| } |
|
|
| |
| |
| |
| |
| var evidenceOnlySignals = map[string]bool{ |
| "exfil-host-reference": true, |
| "references-unscanned-filetype": true, |
| "analyzer-error": true, |
| } |
|
|
| |
| |
| |
| |
| |
| |
| var evidenceDecorates = map[string]CapabilityName{ |
| "exfil-host-reference": CapNetworkEgress, |
| } |
|
|
| |
| |
| |
| var highPowerCaps = map[CapabilityName]bool{ |
| CapSecretAccess: true, |
| CapNetworkEgress: true, |
| CapOpaqueExecution: true, |
| CapDynamicFetchExec: true, |
| CapRegistryTamper: true, |
| CapFSDestructive: true, |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| 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) |
| } |
| |
| |
| |
| |
| 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), |
| } |
| } |
|
|
| |
| |
| func sortCaps(caps []Capability) { |
| sort.Slice(caps, func(i, j int) bool { |
| return caps[i].Name < caps[j].Name |
| }) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| 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 |
| } |
|
|
| |
| |
| |
| |
| |
| |
| 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 |
| } |
| } |
|
|
| |
| |
| |
| |
| func DeriveLabel(tier RiskTier) string { |
| if tier == TierReview { |
| return "malicious" |
| } |
| return "benign" |
| } |
|
|