123 lines
3.7 KiB
Go
123 lines
3.7 KiB
Go
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package events
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/model"
|
|
)
|
|
|
|
const (
|
|
protWrite = 0x2
|
|
protExec = 0x4
|
|
ptraceTrace = 0
|
|
ptraceAttach = 16
|
|
ptraceSeize = 0x4206
|
|
prSetSeccomp = 22
|
|
)
|
|
|
|
type syscallPredicate func(SyscallEvent) bool
|
|
|
|
type SyscallRule struct {
|
|
SID int
|
|
Message string
|
|
Severity string
|
|
Classtype string
|
|
Syscalls map[string]bool
|
|
Predicate syscallPredicate
|
|
}
|
|
|
|
func (r SyscallRule) Matches(event SyscallEvent) bool {
|
|
return r.Syscalls[event.Syscall] && r.Predicate(event)
|
|
}
|
|
|
|
func (r SyscallRule) Alert(event SyscallEvent) model.Alert {
|
|
detail := fmt.Sprintf(
|
|
"sid=%d %s — pid=%d ppid=%d comm=%s syscall=%s args=[%#x, %#x, %#x, %#x]",
|
|
r.SID, r.Message, event.PID, event.PPID, event.Comm, event.Syscall,
|
|
event.Args[0], event.Args[1], event.Args[2], event.Args[3])
|
|
if event.Text != "" {
|
|
detail += " text=[" + truncateRunes(event.Text, 80) + "]"
|
|
}
|
|
return model.Alert{
|
|
SID: r.SID, Severity: r.Severity,
|
|
Signature: "syscall_rule." + r.Classtype, Classtype: r.Classtype,
|
|
Key: fmt.Sprintf("syscall:%d:%d:%s:%x:%x",
|
|
r.SID, event.PID, event.Syscall, event.Args[0], event.Args[2]),
|
|
Detail: detail, PIDs: []int{event.PID},
|
|
}
|
|
}
|
|
|
|
type SyscallRuleEngine struct {
|
|
Rules []SyscallRule
|
|
}
|
|
|
|
func DefaultSyscallRuleEngine() SyscallRuleEngine {
|
|
return SyscallRuleEngine{Rules: []SyscallRule{
|
|
{
|
|
SID: 100060, Message: "mprotect made memory writable and executable",
|
|
Severity: "CRITICAL", Classtype: "memory-obfuscation",
|
|
Syscalls: stringSet([]string{"mprotect"}),
|
|
Predicate: func(event SyscallEvent) bool { return protectedWX(event.Args[2]) },
|
|
},
|
|
{
|
|
SID: 100061, Message: "mmap requested writable and executable memory",
|
|
Severity: "CRITICAL", Classtype: "memory-obfuscation",
|
|
Syscalls: stringSet([]string{"mmap"}),
|
|
Predicate: func(event SyscallEvent) bool { return protectedWX(event.Args[2]) },
|
|
},
|
|
{
|
|
SID: 100062, Message: "memfd_create used for anonymous in-memory file staging",
|
|
Severity: "MEDIUM", Classtype: "fileless-execution",
|
|
Syscalls: stringSet([]string{"memfd_create"}), Predicate: alwaysSyscall,
|
|
},
|
|
{
|
|
SID: 100063, Message: "ptrace anti-debug or attach operation observed",
|
|
Severity: "HIGH", Classtype: "anti-analysis",
|
|
Syscalls: stringSet([]string{"ptrace"}),
|
|
Predicate: func(event SyscallEvent) bool {
|
|
request := event.Args[0]
|
|
return request == ptraceTrace || request == ptraceAttach || request == ptraceSeize
|
|
},
|
|
},
|
|
{
|
|
SID: 100064,
|
|
Message: "seccomp sandboxing call observed (possible anti-analysis hardening)",
|
|
Severity: "MEDIUM", Classtype: "anti-analysis",
|
|
Syscalls: stringSet([]string{"prctl", "seccomp"}),
|
|
Predicate: func(event SyscallEvent) bool {
|
|
return event.Syscall == "seccomp" || event.Args[0] == prSetSeccomp
|
|
},
|
|
},
|
|
{
|
|
SID: 100065, Message: "cross-process memory read/write syscall observed",
|
|
Severity: "HIGH", Classtype: "credential-access",
|
|
Syscalls: stringSet([]string{"process_vm_readv", "process_vm_writev"}),
|
|
Predicate: alwaysSyscall,
|
|
},
|
|
{
|
|
SID: 100066,
|
|
Message: "memory locking syscall observed (possible protected in-memory payload)",
|
|
Severity: "MEDIUM", Classtype: "memory-obfuscation",
|
|
Syscalls: stringSet([]string{"mlock", "mlock2", "mlockall"}),
|
|
Predicate: alwaysSyscall,
|
|
},
|
|
}}
|
|
}
|
|
|
|
func (e SyscallRuleEngine) Match(event SyscallEvent) []model.Alert {
|
|
alerts := make([]model.Alert, 0)
|
|
for _, rule := range e.Rules {
|
|
if rule.Matches(event) {
|
|
alerts = append(alerts, rule.Alert(event))
|
|
}
|
|
}
|
|
return alerts
|
|
}
|
|
|
|
func protectedWX(protection uint64) bool {
|
|
return protection&protWrite != 0 && protection&protExec != 0
|
|
}
|
|
|
|
func alwaysSyscall(SyscallEvent) bool { return true }
|