Add host correlation and assurance coverage
This commit is contained in:
parent
8194d13734
commit
b40ac4252c
36 changed files with 944 additions and 23 deletions
|
|
@ -19,6 +19,9 @@ class HostEvent:
|
|||
peer_port: int = 0
|
||||
local_ip: str = ""
|
||||
local_port: int = 0
|
||||
target_uid: int = -1
|
||||
target_gid: int = -1
|
||||
mode: int = 0
|
||||
|
||||
@property
|
||||
def argv_str(self) -> str:
|
||||
|
|
@ -38,4 +41,7 @@ class HostEvent:
|
|||
"peer_port": self.peer_port,
|
||||
"local_ip": self.local_ip,
|
||||
"local_port": self.local_port,
|
||||
"target_uid": self.target_uid,
|
||||
"target_gid": self.target_gid,
|
||||
"mode": self.mode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ _INTERPRETERS = frozenset(
|
|||
"node nodejs nc ncat netcat socat curl wget fetch".split()
|
||||
)
|
||||
_COMMON_PUBLIC_PORTS = frozenset({22, 53, 80, 123, 443, 853})
|
||||
_PERSISTENCE_PREFIXES = (
|
||||
"/etc/cron.d/", "/etc/crontab", "/etc/systemd/system/",
|
||||
"/etc/ld.so.preload", "/root/.ssh/authorized_keys",
|
||||
"/etc/sudoers", "/etc/sudoers.d/",
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -32,6 +37,9 @@ class HostRule:
|
|||
peer_port_exclude: frozenset[int] = frozenset()
|
||||
local_ports: frozenset[int] = frozenset()
|
||||
local_port_exclude: frozenset[int] = frozenset()
|
||||
path_prefixes: tuple[str, ...] = ()
|
||||
target_uids: frozenset[int] = frozenset()
|
||||
target_gids: frozenset[int] = frozenset()
|
||||
argv_regex: str | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
|
|
@ -40,7 +48,8 @@ class HostRule:
|
|||
if not any((
|
||||
self.comm, self.parent_comm, self.peer_public is not None,
|
||||
self.peer_ports, self.peer_port_exclude, self.local_ports,
|
||||
self.local_port_exclude, self.argv_regex,
|
||||
self.local_port_exclude, self.path_prefixes, self.target_uids,
|
||||
self.target_gids, self.argv_regex,
|
||||
)):
|
||||
raise ValueError(f"rule sid={self.sid} has no match conditions")
|
||||
if self.argv_regex is not None:
|
||||
|
|
@ -66,6 +75,15 @@ class HostRule:
|
|||
return False
|
||||
if self.local_port_exclude and ev.local_port in self.local_port_exclude:
|
||||
return False
|
||||
if self.path_prefixes and not any(
|
||||
ev.path == prefix or ev.path.startswith(prefix)
|
||||
for prefix in self.path_prefixes
|
||||
):
|
||||
return False
|
||||
if self.target_uids and ev.target_uid not in self.target_uids:
|
||||
return False
|
||||
if self.target_gids and ev.target_gid not in self.target_gids:
|
||||
return False
|
||||
if self._argv_re is not None and not self._argv_re.search(ev.argv_str):
|
||||
return False
|
||||
return True
|
||||
|
|
@ -74,11 +92,17 @@ class HostRule:
|
|||
return Alert(
|
||||
severity=self.severity,
|
||||
signature=f"host_rule.{self.classtype}",
|
||||
key=f"host:{self.sid}:{ev.event}:{ev.pid}:{ev.peer_ip}:{ev.peer_port}",
|
||||
key=(
|
||||
f"host:{self.sid}:{ev.event}:{ev.pid}:"
|
||||
f"{ev.peer_ip}:{ev.peer_port}:{ev.local_ip}:{ev.local_port}:"
|
||||
f"{ev.path}:{ev.target_uid}:{ev.target_gid}:{ev.mode}"
|
||||
),
|
||||
detail=(
|
||||
f"sid={self.sid} {self.msg} - pid={ev.pid} ppid={ev.ppid} "
|
||||
f"comm={ev.comm} event={ev.event} peer={ev.peer_ip}:{ev.peer_port} "
|
||||
f"local={ev.local_ip}:{ev.local_port}"
|
||||
f"local={ev.local_ip}:{ev.local_port} path={ev.path} "
|
||||
f"target_uid={ev.target_uid} target_gid={ev.target_gid} "
|
||||
f"mode={oct(ev.mode) if ev.mode else '-'}"
|
||||
),
|
||||
pids=(ev.pid,),
|
||||
sid=self.sid,
|
||||
|
|
@ -106,6 +130,41 @@ DEFAULT_HOST_RULES: tuple[HostRule, ...] = (
|
|||
comm=_INTERPRETERS,
|
||||
local_port_exclude=_COMMON_PUBLIC_PORTS,
|
||||
),
|
||||
HostRule(
|
||||
sid=100069,
|
||||
msg="Interpreter wrote to a persistence path",
|
||||
severity=Severity.HIGH,
|
||||
classtype="persistence-write",
|
||||
events=frozenset({"file_write"}),
|
||||
comm=_INTERPRETERS,
|
||||
path_prefixes=_PERSISTENCE_PREFIXES,
|
||||
),
|
||||
HostRule(
|
||||
sid=100070,
|
||||
msg="Permissions or ownership changed on a persistence path",
|
||||
severity=Severity.HIGH,
|
||||
classtype="persistence-permission-change",
|
||||
events=frozenset({"chmod", "chown"}),
|
||||
path_prefixes=_PERSISTENCE_PREFIXES,
|
||||
),
|
||||
HostRule(
|
||||
sid=100071,
|
||||
msg="Interpreter requested a root UID transition",
|
||||
severity=Severity.HIGH,
|
||||
classtype="privilege-transition",
|
||||
events=frozenset({"setuid"}),
|
||||
comm=_INTERPRETERS,
|
||||
target_uids=frozenset({0}),
|
||||
),
|
||||
HostRule(
|
||||
sid=100072,
|
||||
msg="Interpreter requested a root GID transition",
|
||||
severity=Severity.HIGH,
|
||||
classtype="privilege-transition",
|
||||
events=frozenset({"setgid"}),
|
||||
comm=_INTERPRETERS,
|
||||
target_gids=frozenset({0}),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue