Expand monitoring for credential theft and covert protocols
This commit is contained in:
parent
3e5f8fc3f7
commit
cb334c0c94
17 changed files with 675 additions and 25 deletions
|
|
@ -18,6 +18,10 @@ from pathlib import Path
|
|||
|
||||
_INO_RE = re.compile(r"\bino:(\d+)")
|
||||
_USER_RE = re.compile(r'users:\(\("([^"]+)",pid=(\d+),fd=\d+')
|
||||
_SS_NETIDS = frozenset({
|
||||
"tcp", "udp", "raw", "sctp", "dccp", "mptcp",
|
||||
"p_raw", "p_dgr", "packet", "tipc", "xdp", "vsock",
|
||||
})
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -65,6 +69,23 @@ class Process:
|
|||
out[k] = v
|
||||
return out
|
||||
|
||||
@cached_property
|
||||
def fd_targets(self) -> dict[str, str]:
|
||||
"""Open file descriptor targets, keyed by fd number as a string."""
|
||||
out: dict[str, str] = {}
|
||||
fd_dir = f"/proc/{self.pid}/fd"
|
||||
try:
|
||||
names = sorted(os.listdir(fd_dir),
|
||||
key=lambda x: int(x) if x.isdigit() else 0)
|
||||
except OSError:
|
||||
return out
|
||||
for name in names:
|
||||
try:
|
||||
out[name] = os.readlink(os.path.join(fd_dir, name))
|
||||
except OSError:
|
||||
continue
|
||||
return out
|
||||
|
||||
@cached_property
|
||||
def status(self) -> dict[str, str]:
|
||||
out: dict[str, str] = {}
|
||||
|
|
@ -105,6 +126,7 @@ class Socket:
|
|||
inode: int | None
|
||||
comm: str
|
||||
pid: int | None
|
||||
kind: str = ""
|
||||
|
||||
|
||||
class SystemState:
|
||||
|
|
@ -166,8 +188,19 @@ class SystemState:
|
|||
if self._injected_socks is not None:
|
||||
return self._injected_socks
|
||||
out: list[Socket] = []
|
||||
for args in (["-tanep"], ["-uanep"]):
|
||||
out.extend(self._parse_ss(self._run_ss(args)))
|
||||
for args, kind in (
|
||||
(["-tanep"], "tcp"),
|
||||
(["-uanep"], "udp"),
|
||||
(["-wanep"], "raw"),
|
||||
(["-Sanep"], "sctp"),
|
||||
(["-danep"], "dccp"),
|
||||
(["-0anep"], "packet"),
|
||||
(["-Manep"], "mptcp"),
|
||||
(["--tipc", "-anep"], "tipc"),
|
||||
(["--xdp", "-anep"], "xdp"),
|
||||
(["--vsock", "-anep"], "vsock"),
|
||||
):
|
||||
out.extend(self._parse_ss(self._run_ss(args), kind=kind))
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -182,13 +215,16 @@ class SystemState:
|
|||
return ""
|
||||
|
||||
@staticmethod
|
||||
def _parse_ss(text: str) -> list[Socket]:
|
||||
def _parse_ss(text: str, *, kind: str = "") -> list[Socket]:
|
||||
socks: list[Socket] = []
|
||||
for line in text.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) < 5:
|
||||
continue
|
||||
state, _rq, _sq, local, peer = parts[:5]
|
||||
if parts[0].lower() in _SS_NETIDS and len(parts) >= 6:
|
||||
state, local, peer = parts[1], parts[4], parts[5]
|
||||
else:
|
||||
state, local, peer = parts[0], parts[3], parts[4]
|
||||
rest = line
|
||||
ino_m = _INO_RE.search(rest)
|
||||
usr_m = _USER_RE.search(rest)
|
||||
|
|
@ -199,6 +235,7 @@ class SystemState:
|
|||
inode=int(ino_m.group(1)) if ino_m else None,
|
||||
comm=usr_m.group(1) if usr_m else "",
|
||||
pid=int(usr_m.group(2)) if usr_m else None,
|
||||
kind=kind,
|
||||
))
|
||||
return socks
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue