-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better handling of overrides after all fr.Behaviors are added #487
Conversation
Signed-off-by: egibs <[email protected]>
pkg/report/report.go
Outdated
if originalExists && overrideExists { | ||
for _, b := range fr.Behaviors { | ||
if b.RuleName == override.RuleName { | ||
b.RuleAuthor = original.RuleAuthor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was getting incorrect results if I did b = original
, so I was more prescriptive with the fields here. I think it adds helpful context anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be reversed: preserve the original rule, but apply the necessary override (priority) to it: otherwise we're going to just keep on adding fields to this code as new ones come up.
One thing I just realized is that we probably shouldn't modify the rules description directly: otherwise it gets messy if multiple overrides apply to a rule. Instead we can add the rule name to a list of overrides that apply to the behavior (for example: Behavior.Overrides), and modify the description at the presentation layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flipped the logic in a4c70f4
(#487).
Signed-off-by: egibs <[email protected]>
Currently working through a discrepancy between |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make it so that overridden rules use the original name rather than the override name?
Signed-off-by: egibs <[email protected]>
rules/override/shellcode.yara
Outdated
meta: | ||
malware_shellcode_hash = "high" | ||
|
||
strings: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tstromberg -- can you think of a less redundant way to handle this?
This is a new override rule for another third-party false positive we saw with Pulumi, but it feels gross to duplicate the strings/conditions. Using something less restrictive will allow other override rules to match, though (e.g., condition: true
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should invert how this override functions: it should simply detect the appropriate Pulumi binary rather than repeat the logic of the offending rule.
Here's what I would do in your shoes:
- Rename this file to "pulumi.yara"
- Rename the rule to the binary you are overriding, like
pulumictl
(if it's not obviously pulumi, maybe "pulumi_". - Add 1-2 long strings you expect to show up in the binary, such as "PULUMI_HOME" or "How did Pulumi come up with such a weird name?"
- Ditch the rest of the rules: we're only trying to detect that this binary is probably pulumi, not the rules it is overriding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Your Bash example comes to mind and that's a good way to go about it.
Signed-off-by: egibs <[email protected]>
Updated in |
rules/override/shellcode.yara
Outdated
meta: | ||
malware_shellcode_hash = "high" | ||
|
||
strings: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should invert how this override functions: it should simply detect the appropriate Pulumi binary rather than repeat the logic of the offending rule.
Here's what I would do in your shoes:
- Rename this file to "pulumi.yara"
- Rename the rule to the binary you are overriding, like
pulumictl
(if it's not obviously pulumi, maybe "pulumi_". - Add 1-2 long strings you expect to show up in the binary, such as "PULUMI_HOME" or "How did Pulumi come up with such a weird name?"
- Ditch the rest of the rules: we're only trying to detect that this binary is probably pulumi, not the rules it is overriding.
Ideal: instead of a generic I figure that overrides that add severity should probably live elsewhere? |
Signed-off-by: egibs <[email protected]>
Signed-off-by: egibs <[email protected]>
Signed-off-by: egibs <[email protected]>
@@ -521,6 +517,72 @@ func Generate(ctx context.Context, path string, mrs yara.MatchRules, c malconten | |||
// TODO: If we match multiple rules within a single namespace, merge matchstrings | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can some of the override handling code move to a new function? This one is getting overly large and complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to a new function in 660024a
(#487).
pkg/report/report.go
Outdated
// If this is the override rule, delete it from the behavior slice | ||
// Otherwise, treat the rule as a possible original (vo.Original makes this an O(1) lookup) | ||
if isOverride { | ||
fr.Behaviors = slices.Delete(fr.Behaviors, i, i+1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of deleting overrides from Behaviors, would the logic be simpler if overrides were stored elsewhere instead? For example, fr.Overrides
or a different variable altogether.
By the time fr.Behaviors
is populated, we already know if the rule has an override
tag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added two new fields for overrides in 660024a
(#487).
pkg/report/report.go
Outdated
switch c.Scan { | ||
case true: | ||
switch { | ||
case newRisk != overallRiskScore && newRisk >= 3: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this use a const of some sort? I don't remember if "3" is "HIGH" or "CRITICAL". I'm not sure why either needs to be hardcoded here to be honest, but I think it'd improve readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in 660024a
(#487).
pkg/report/report.go
Outdated
// Regardless of running an `analyze` or `scan`, adjust the overall risk if we deviated from overallRiskScore | ||
// Scans will still need to drop medium or lower results | ||
newRisk := highestBehaviorRisk(fr) | ||
switch c.Scan { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the nested switches are helping readability. Maybe something like:
if newRisk != overallRiskScore {
overallRiskScore = newRisk
}
if c.Scan && newRisk < HIGH:
return malcontent.FileReport{}, nil
}
Is this just checking if the command is "scan" vs "analyze"? If so, I don't think this function should need to know: all scan should need to do in main
is set a default minRisk of "HIGH" and everything else should just work out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleaned this up in 660024a
(#487).
Signed-off-by: egibs <[email protected]>
} | ||
|
||
for _, o := range fr.Overrides { | ||
if b, exists := behaviorMap[o.Override]; exists { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested this out with a critical false positive and it works.
Test override rule:
rule pulumi_shellcode : override {
meta:
malware_shellcode_hash = "low"
description = "Ignore shellcode findings in the pulumi binary"
strings:
$pulumi = { 50 75 6C 75 6D 69 }
$pulumi2 = { 70 75 6C 75 6D 69 }
condition:
any of ($pulumi*)
}
Signed-off-by: egibs <[email protected]>
Signed-off-by: egibs <[email protected]>
Signed-off-by: egibs <[email protected]>
Signed-off-by: egibs <[email protected]>
@@ -1,4 +1,4 @@ | |||
rule go_dns_refs { | |||
rule go_dns_refs_local { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were two go_dns_refs
rules and there was a chance one would clobber the other and result in different report results:
Lines 1 to 11 in 0acb2e0
rule go_dns_refs { meta: description = "Uses DNS (Domain Name Service)" strings: $dnsmessage = "dnsmessage" $edns = "SetEDNS0" $cname = "CNAMEResource" $nodejs = /require\(['"]dns['"]\)/ condition: any of them } malcontent/rules/net/dns-servers.yara
Lines 1 to 10 in 0acb2e0
rule go_dns_refs { meta: description = "Examines local DNS servers" strings: $resolv = "resolv.conf" fullword $dns_getservers = "dns.getServers" $cname = "CNAMEResource" condition: any of them }
Closes #486
We hit the edge case where an override rule was being evaluated before the original rule, so we never found it in the slice of behaviors.
This PR handles overrides as a post-processing step, rather than during the
mrs
loop.This PR also introduces our first Critical -> High override for a third-party (!) rule:
Because of this rule, I was able to see that the original override logic was insufficient. The new approach is more resilient and we should successfully apply all overrides now.