Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 0 additions & 57 deletions pkg/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@
package compile

import (
"bytes"
"context"
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"

"github.com/chainguard-dev/clog"
Expand All @@ -20,11 +17,6 @@ import (
yarax "github.com/VirusTotal/yara-x/go"
)

const (
globalInclude = `include "rules/global/global.yara"`
globalPath = "rules/global/global.yara"
)

var FS = rules.FS

// badRules are noisy 3rd party rules to silently disable.
Expand Down Expand Up @@ -167,47 +159,6 @@ func removeRules(data []byte, rulesToRemove []string) []byte {
return newlinePattern.ReplaceAll(modified, []byte("\n\n"))
}

// findRoot locates the packages's root directory on the fly.
func findRoot() (string, error) {
_, here, _, ok := runtime.Caller(0)
if !ok {
return "", fmt.Errorf("failed to get current file path")
}

dir := filepath.Dir(here)
current := dir
for {
rulesPath := filepath.Join(current, "rules")
if fi, err := os.Stat(rulesPath); err == nil && fi.IsDir() {
return current, nil
}

parent := filepath.Dir(current)
if parent == current {
break
}
current = parent
}

rulesPath := filepath.Join(filepath.Dir(dir), "rules")
if fi, err := os.Stat(rulesPath); err == nil && fi.IsDir() {
return filepath.Dir(dir), nil
}

return "", fmt.Errorf("could not find rules directory from %s", dir)
}

// replaceGlobal updates the include string to reference the absolute path of rules/global/global.yara
// by default, the relative path is valid for local compilations and builds done from the root of the repository,
// but this is not valid for test files located in various directories.
func replaceGlobal(data []byte, path string) []byte {
modified := data
if bytes.Contains(data, []byte(globalInclude)) {
modified = bytes.Replace(data, []byte(globalInclude), fmt.Appendf(nil, `include "%s"`, path), 1)
}
return modified
}

func Recursive(ctx context.Context, fss []fs.FS) (*yarax.Rules, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
Expand All @@ -218,11 +169,6 @@ func Recursive(ctx context.Context, fss []fs.FS) (*yarax.Rules, error) {
return nil, fmt.Errorf("yarax compiler: %w", err)
}

rootPath, err := findRoot()
if err != nil {
return nil, err
}

rulesToRemove := getRulesToRemove()

for _, root := range fss {
Expand All @@ -243,9 +189,6 @@ func Recursive(ctx context.Context, fss []fs.FS) (*yarax.Rules, error) {

bs = removeRules(bs, rulesToRemove)

globalAbs := filepath.Join(rootPath, globalPath)
bs = replaceGlobal(bs, globalAbs)

yxc.NewNamespace(path)
if err := yxc.AddSource(string(bs), yarax.WithOrigin(path)); err != nil {
return fmt.Errorf("failed to parse %s: %v", path, err)
Expand Down
19 changes: 17 additions & 2 deletions rules/anti-behavior/random_behavior.yara
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import "math"

include "rules/global/global.yara"
private rule random_behavior_pythonSetup {
strings:
$if_distutils = /from distutils.core import .{0,32}setup/
$if_setuptools = /from setuptools import .{0,32}setup/
$i_setuptools = "import setuptools"
$setup = "setup("

$not_setup_example = ">>> setup("
$not_setup_todict = "setup(**config.todict()"
$not_import_quoted = "\"from setuptools import setup"
$not_setup_quoted = "\"setup(name="
$not_distutils = "from distutils.errors import"

condition:
filesize < 128KB and $setup and any of ($i*) and none of ($not*)
}

rule setuptools_random: critical {
meta:
Expand All @@ -12,7 +27,7 @@ rule setuptools_random: critical {
$not_easy_install = "pid = random.randint(0, sys.maxsize)"

condition:
global_python_setup and $ref and none of ($not*)
random_behavior_pythonSetup and $ref and none of ($not*)
}

rule java_random: low {
Expand Down
18 changes: 13 additions & 5 deletions rules/anti-static/elf/entropy.yara
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import "math"

include "rules/global/global.yara"
private rule normal_elf {
condition:
filesize < 64MB and uint32(0) == 1179403647
}

private rule small_elf {
condition:
filesize < 400KB and uint32(0) == 1179403647
}

rule higher_elf_entropy_68: medium {
meta:
description = "higher entropy ELF binary (>6.95)"
filetypes = "elf"

condition:
global_normal_elf and math.entropy(1, filesize) >= 6.95
normal_elf and math.entropy(1, filesize) >= 6.95
}

rule normal_elf_high_entropy_7_4: high {
Expand All @@ -21,7 +29,7 @@ rule normal_elf_high_entropy_7_4: high {
$not_bazel = "BazelLogHandler"

condition:
filesize < 30MB and global_normal_elf and math.entropy(1, filesize) >= 7.4 and none of ($not*)
filesize < 30MB and normal_elf and math.entropy(1, filesize) >= 7.4 and none of ($not*)
}

rule normal_elf_high_entropy_footer_7_4: high {
Expand All @@ -30,7 +38,7 @@ rule normal_elf_high_entropy_footer_7_4: high {
filetypes = "elf"

condition:
global_normal_elf and math.entropy(filesize - 8192, filesize) >= 7.4
normal_elf and math.entropy(filesize - 8192, filesize) >= 7.4
}

rule normal_elf_high_entropy_footer_7_4_rc4: high {
Expand All @@ -43,5 +51,5 @@ rule normal_elf_high_entropy_footer_7_4_rc4: high {
$cmp_r_x_256 = { 48 81 f? 00 01 00 00 } // cmp {rbx, rcx, …}, 256

condition:
filesize < 25MB and global_normal_elf and math.entropy(filesize - 8192, filesize) >= 7.4 and any of them
filesize < 25MB and normal_elf and math.entropy(filesize - 8192, filesize) >= 7.4 and any of them
}
9 changes: 6 additions & 3 deletions rules/anti-static/macho/entropy.yara
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import "math"

include "rules/global/global.yara"
private rule smaller_macho {
condition:
filesize < 64MB and (uint32(0) == 4277009102 or uint32(0) == 3472551422 or uint32(0) == 4277009103 or uint32(0) == 3489328638 or uint32(0) == 3405691582 or uint32(0) == 3199925962)
}

rule higher_entropy_6_9: medium {
meta:
description = "higher entropy binary (>6.9)"
filetypes = "macho"

condition:
global_small_macho and math.entropy(1, filesize) >= 6.9
smaller_macho and math.entropy(1, filesize) >= 6.9
}

rule high_entropy_7_2: high {
Expand All @@ -21,5 +24,5 @@ rule high_entropy_7_2: high {
$bin_java = "bin/java"

condition:
global_small_macho and math.entropy(1, filesize) >= 7.2 and not $bin_java
smaller_macho and math.entropy(1, filesize) >= 7.2 and not $bin_java
}
7 changes: 5 additions & 2 deletions rules/anti-static/macho/footer.yara
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import "math"

include "rules/global/global.yara"
private rule anti_static_macho {
condition:
(uint32(0) == 4277009102 or uint32(0) == 3472551422 or uint32(0) == 4277009103 or uint32(0) == 3489328638 or uint32(0) == 3405691582 or uint32(0) == 3199925962 or uint32(0) == 3405691583 or uint32(0) == 3216703178)
}

rule high_entropy_trailer: high {
meta:
Expand All @@ -12,5 +15,5 @@ rule high_entropy_trailer: high {
$page_zero = "_PAGEZERO"

condition:
filesize < 10MB and global_macho and $page_zero and math.entropy(filesize - 1024, filesize - 1) >= 4
filesize < 10MB and anti_static_macho and $page_zero and math.entropy(filesize - 1024, filesize - 1) >= 4
}
8 changes: 6 additions & 2 deletions rules/anti-static/packer/aes.yara
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import "math"

include "rules/global/global.yara"
private rule smallBinary {
condition:
// matches ELF or machO binary
filesize > 1MB and filesize < 8MB and (uint32(0) == 1179403647 or uint32(0) == 4277009102 or uint32(0) == 3472551422 or uint32(0) == 4277009103 or uint32(0) == 3489328638 or uint32(0) == 3405691582 or uint32(0) == 3199925962)
}

rule go_aes: high {
meta:
Expand All @@ -13,5 +17,5 @@ rule go_aes: high {
$decrypt = "NewCFBDecrypter"

condition:
global_small_binary and math.entropy(1, filesize) >= 7 and all of them
smallBinary and math.entropy(1, filesize) >= 7 and all of them
}
13 changes: 11 additions & 2 deletions rules/anti-static/unmarshal/marshal.yara
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import "math"

include "rules/global/global.yara"
private rule pySetup {
strings:
$i_distutils = "from distutils.core import setup"
$i_setuptools = "setuptools"
$setup = "setup("
$not_setuptools = "setuptools.command"

condition:
filesize < 2097152 and $setup and any of ($i*) and none of ($not*)
}

rule unmarshal_py_marshal: medium {
meta:
Expand All @@ -20,5 +29,5 @@ rule setuptools_py_marshal: suspicious {
filetypes = "py"

condition:
global_python_setup and unmarshal_py_marshal
pySetup and unmarshal_py_marshal
}
9 changes: 6 additions & 3 deletions rules/c2/addr/ip.yara
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
include "rules/global/global.yara"

rule hardcoded_ip: medium {
meta:
description = "hardcoded IP address"
Expand All @@ -21,6 +19,11 @@ rule hardcoded_ip: medium {
filesize < 200MB and 1 of ($sus_ip*) and none of ($not*)
}

private rule ip_elf_or_macho {
condition:
uint32(0) == 1179403647 or (uint32(0) == 4277009102 or uint32(0) == 3472551422 or uint32(0) == 4277009103 or uint32(0) == 3489328638 or uint32(0) == 3405691582 or uint32(0) == 3199925962 or uint32(0) == 3405691583 or uint32(0) == 3216703178)
}

rule bin_hardcoded_ip: high {
meta:
description = "ELF with hardcoded IP address"
Expand All @@ -45,7 +48,7 @@ rule bin_hardcoded_ip: high {
$not_2345 = "23.45.67.89"

condition:
filesize < 12MB and global_elf_or_macho and 1 of ($sus_ip*) and none of ($not*)
filesize < 12MB and ip_elf_or_macho and 1 of ($sus_ip*) and none of ($not*)
}

rule http_hardcoded_ip: high exfil {
Expand Down
9 changes: 6 additions & 3 deletions rules/c2/addr/url.yara
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import "math"

include "rules/global/global.yara"
private rule elf_or_macho {
condition:
uint32(0) == 1179403647 or (uint32(0) == 4277009102 or uint32(0) == 3472551422 or uint32(0) == 4277009103 or uint32(0) == 3489328638 or uint32(0) == 3405691582 or uint32(0) == 3199925962 or uint32(0) == 3405691583 or uint32(0) == 3216703178)
}

rule unusual_nodename: medium {
meta:
Expand Down Expand Up @@ -82,7 +85,7 @@ rule binary_with_url: low {
$ref = /https*:\/\/[\w\.\/]{8,160}[\/\w\=\&]{0,32}/

condition:
filesize < 150MB and global_elf_or_macho and $ref
filesize < 150MB and elf_or_macho and $ref
}

rule binary_url_with_question: high {
Expand All @@ -99,7 +102,7 @@ rule binary_url_with_question: high {
$not_mesibo = "https://api.mesibo.com/api.php?"

condition:
filesize < 150MB and global_elf_or_macho and $ref and none of ($not*)
filesize < 150MB and elf_or_macho and $ref and none of ($not*)
}

rule script_url_with_question: high {
Expand Down
10 changes: 7 additions & 3 deletions rules/c2/tool_transfer/download.yara
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
include "rules/global/global.yara"

rule download_sites: high {
meta:
ref = "https://github.com/ditekshen/detection/blob/e6579590779f62cbe7f5e14b5be7d77b2280f516/yara/indicator_high.yar#L1001"
Expand Down Expand Up @@ -115,6 +113,12 @@ rule http_archive_url: medium {
any of ($ref*) and none of ($not*)
}

private rule smallerBinary {
condition:
// matches ELF or machO binary
filesize < 10MB and (uint32(0) == 1179403647 or uint32(0) == 4277009102 or uint32(0) == 3472551422 or uint32(0) == 4277009103 or uint32(0) == 3489328638 or uint32(0) == 3405691582 or uint32(0) == 3199925962)
}

rule http_archive_url_higher: high {
meta:
description = "accesses hardcoded archive file endpoint"
Expand All @@ -125,5 +129,5 @@ rule http_archive_url_higher: high {
$not_foo_bar = "http://foo/bar.tar"

condition:
global_small_binary and any of ($ref*) and none of ($not*)
smallerBinary and any of ($ref*) and none of ($not*)
}
12 changes: 10 additions & 2 deletions rules/c2/tool_transfer/macos.yara
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
include "rules/global/global.yara"
private rule tool_transfer_macho {
strings:
$not_jar = "META-INF/"
$not_dwarf = "_DWARF"
$not_kext = "_.SYMDEF SORTED"

condition:
(uint32(0) == 4277009102 or uint32(0) == 3472551422 or uint32(0) == 4277009103 or uint32(0) == 3489328638 or uint32(0) == 3405691582 or uint32(0) == 3199925962 or uint32(0) == 3405691583 or uint32(0) == 3216703178) and none of ($not*)
}

rule macos_chflags_hidden: critical {
meta:
Expand Down Expand Up @@ -30,5 +38,5 @@ rule cocoa_bundle_dropper: critical {
$platform = "isPlatformOrVariantPlatformVersionAtLeast" fullword

condition:
global_specific_macho and $shared and 5 of them
tool_transfer_macho and $shared and 5 of them
}
Loading
Loading