54 lines
1.6 KiB
Python
54 lines
1.6 KiB
Python
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""input_snooper — processes reading keyboard/HID event devices.
|
|
|
|
Keyloggers commonly open ``/dev/input/event*`` or HID raw devices directly.
|
|
Normal display servers, compositors, and input remappers can do this too, so
|
|
known local input stack processes are configurable allow-list entries.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterator
|
|
|
|
from ..alert import Alert, Severity
|
|
from ..config import Config
|
|
from ..system import SystemState
|
|
|
|
SID_INPUT_SNOOPER = 100032
|
|
|
|
_INPUT_PREFIXES = (
|
|
"/dev/input/event",
|
|
"/dev/uinput",
|
|
"/dev/hidraw",
|
|
)
|
|
|
|
|
|
def _fd_targets(proc) -> dict[str, str]:
|
|
targets = getattr(proc, "fd_targets", {})
|
|
return targets() if callable(targets) else targets
|
|
|
|
|
|
def _is_input_device(target: str) -> bool:
|
|
return target.startswith(_INPUT_PREFIXES)
|
|
|
|
|
|
def detect(state: SystemState, cfg: Config) -> Iterator[Alert]:
|
|
for proc in state.processes:
|
|
comm = proc.comm or "?"
|
|
if comm in cfg.input_snooper_allow_comms:
|
|
continue
|
|
for fd, target in _fd_targets(proc).items():
|
|
if not _is_input_device(target):
|
|
continue
|
|
yield Alert(
|
|
severity=Severity.HIGH,
|
|
signature="input_snooper",
|
|
key=f"input:{proc.pid}:{target}",
|
|
detail=(
|
|
f"pid={proc.pid} comm={comm} fd={fd} has input device "
|
|
f"open: {target}"
|
|
),
|
|
pids=(proc.pid,),
|
|
sid=SID_INPUT_SNOOPER,
|
|
classtype="credential-keylogging",
|
|
)
|
|
break
|