Expand rootkit monitoring and Claude docs
This commit is contained in:
parent
a7129e5666
commit
3e5f8fc3f7
16 changed files with 524 additions and 32 deletions
|
|
@ -28,6 +28,21 @@ class TestDiffCores(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
rootcheck.find_hidden_ports({22, 80, 31337}, {22, 80}), {31337})
|
||||
|
||||
def test_hidden_udp_ports_in_procnet_not_in_ss(self):
|
||||
self.assertEqual(
|
||||
rootcheck.find_hidden_udp_ports({53, 5353, 4444}, {53, 5353}), {4444})
|
||||
|
||||
def test_known_rootkit_module_names_normalized(self):
|
||||
self.assertEqual(
|
||||
rootcheck.find_known_rootkit_modules({"ext4", "diamorphine", "adore-ng"}),
|
||||
{"diamorphine", "adore-ng"})
|
||||
|
||||
def test_kernel_taint_decoding_and_severity(self):
|
||||
value = (1 << 12) | (1 << 13)
|
||||
self.assertEqual(rootcheck.decode_kernel_taint(value),
|
||||
["out-of-tree-module", "unsigned-module"])
|
||||
self.assertEqual(rootcheck.taint_severity(value), Severity.HIGH)
|
||||
|
||||
|
||||
class TestSsPortsFromState(unittest.TestCase):
|
||||
def test_extracts_listening_ports_from_injected_state(self):
|
||||
|
|
@ -38,6 +53,15 @@ class TestSsPortsFromState(unittest.TestCase):
|
|||
state = SystemState(sockets=socks)
|
||||
self.assertEqual(rootcheck.ss_listen_ports(state), {22, 631})
|
||||
|
||||
def test_extracts_udp_ports_from_injected_state(self):
|
||||
socks = [
|
||||
Socket("UNCONN", "0.0.0.0:53", "0.0.0.0:0", 10, "dnsmasq", 1),
|
||||
Socket("ESTAB", "127.0.0.1:5353", "127.0.0.1:1", 11, "mdns", 2),
|
||||
Socket("LISTEN", "0.0.0.0:22", "0.0.0.0:0", 12, "sshd", 3),
|
||||
]
|
||||
state = SystemState(sockets=socks)
|
||||
self.assertEqual(rootcheck.ss_udp_ports(state), {53, 5353})
|
||||
|
||||
|
||||
class TestRunIntegration(unittest.TestCase):
|
||||
"""Drive run() with every system view monkeypatched to a known state."""
|
||||
|
|
@ -47,7 +71,8 @@ class TestRunIntegration(unittest.TestCase):
|
|||
self._saved = {}
|
||||
for name in ("proc_pids", "alive_pids", "proc_modules",
|
||||
"sys_live_modules", "procnet_listen_ports",
|
||||
"promiscuous_interfaces"):
|
||||
"procnet_udp_ports", "promiscuous_interfaces", "module_taints",
|
||||
"kernel_taint"):
|
||||
self._saved[name] = getattr(rootcheck, name)
|
||||
|
||||
def tearDown(self):
|
||||
|
|
@ -60,7 +85,10 @@ class TestRunIntegration(unittest.TestCase):
|
|||
rootcheck.proc_modules = lambda: views.get("proc_modules", set())
|
||||
rootcheck.sys_live_modules = lambda: views.get("sys_live_modules", set())
|
||||
rootcheck.procnet_listen_ports = lambda: views.get("procnet_ports", set())
|
||||
rootcheck.procnet_udp_ports = lambda: views.get("procnet_udp_ports", set())
|
||||
rootcheck.promiscuous_interfaces = lambda: views.get("promisc", [])
|
||||
rootcheck.module_taints = lambda: views.get("module_taints", {})
|
||||
rootcheck.kernel_taint = lambda: views.get("kernel_taint", 0)
|
||||
|
||||
def test_clean_system_yields_nothing(self):
|
||||
self._patch(proc_pids={1, 2}, alive_pids={1, 2},
|
||||
|
|
@ -72,18 +100,47 @@ class TestRunIntegration(unittest.TestCase):
|
|||
self._patch(
|
||||
proc_pids={1}, alive_pids={1},
|
||||
proc_modules={"ext4"}, sys_live_modules={"ext4", "diamorphine"},
|
||||
procnet_ports={22, 31337}, promisc=["eth0"])
|
||||
procnet_ports={22, 31337}, procnet_udp_ports={53, 4444},
|
||||
promisc=["eth0"])
|
||||
state = SystemState(sockets=[
|
||||
Socket("LISTEN", "0.0.0.0:22", "0.0.0.0:0", 10, "sshd", 1)])
|
||||
Socket("LISTEN", "0.0.0.0:22", "0.0.0.0:0", 10, "sshd", 1),
|
||||
Socket("UNCONN", "0.0.0.0:53", "0.0.0.0:0", 11, "dnsmasq", 2),
|
||||
])
|
||||
alerts = {a.signature: a for a in rootcheck.run(self.cfg, state)}
|
||||
self.assertIn("rootkit_hidden_module", alerts)
|
||||
self.assertEqual(alerts["rootkit_hidden_module"].severity, Severity.CRITICAL)
|
||||
self.assertIn("diamorphine", alerts["rootkit_hidden_module"].detail)
|
||||
self.assertIn("rootkit_known_module", alerts)
|
||||
self.assertIn("rootkit_hidden_port", alerts)
|
||||
self.assertIn("31337", alerts["rootkit_hidden_port"].detail)
|
||||
self.assertIn("rootkit_hidden_udp_port", alerts)
|
||||
self.assertIn("4444", alerts["rootkit_hidden_udp_port"].detail)
|
||||
self.assertIn("promiscuous_interface", alerts)
|
||||
self.assertEqual(alerts["promiscuous_interface"].sid, rootcheck.SID_PROMISC)
|
||||
|
||||
def test_tainted_module_and_kernel_taint(self):
|
||||
self._patch(
|
||||
proc_pids={1}, alive_pids={1},
|
||||
proc_modules={"ext4", "vendor_gpu"},
|
||||
sys_live_modules={"ext4", "vendor_gpu"},
|
||||
module_taints={"vendor_gpu": "OE"},
|
||||
kernel_taint=(1 << 12) | (1 << 13))
|
||||
alerts = {a.signature: a for a in rootcheck.run(self.cfg, SystemState(sockets=[]))}
|
||||
self.assertIn("rootkit_tainted_module", alerts)
|
||||
self.assertEqual(alerts["rootkit_tainted_module"].sid,
|
||||
rootcheck.SID_TAINTED_MODULE)
|
||||
self.assertIn("kernel_tainted", alerts)
|
||||
self.assertEqual(alerts["kernel_tainted"].severity, Severity.HIGH)
|
||||
|
||||
def test_module_allowlist_suppresses_tainted_module(self):
|
||||
self.cfg.rootcheck_module_allow = ("vendor_gpu",)
|
||||
self._patch(
|
||||
proc_pids={1}, alive_pids={1},
|
||||
proc_modules={"vendor_gpu"}, sys_live_modules={"vendor_gpu"},
|
||||
module_taints={"vendor_gpu": "OE"})
|
||||
alerts = {a.signature: a for a in rootcheck.run(self.cfg, SystemState(sockets=[]))}
|
||||
self.assertNotIn("rootkit_tainted_module", alerts)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue