Completes Phase 5. Half of a Linux compromise is not a file on disk, it is a line added to a startup file — a curl in a shell profile, a systemd unit with a dull name, one extra key in authorized_keys. The payload is often unremarkable; what makes it an incident is that it survives a reboot and nobody reads those files from one year to the next. So this is not a scanner but an inventory with a memory. It records systemd units (system and per-user), cron in all its locations, autostart entries, shell profiles, authorized_keys and ld.so.preload, then reports what CHANGED. Three decisions, all of which are the difference between a report people read and one they turn off: * Content is hashed, not stat'd. An mtime can be set backwards with one touch, and someone editing a startup file is exactly the person who would. Verified: a backdated edit is still caught. * A first run reports no changes and says so. Everything would be a change, and a first-run report full of alarms is one nobody reads. What a first run can honestly say is how many entries no package claims — 97 of 1,026 on this machine — because that is true regardless of history. * Writing the baseline is an explicit act (`--accept`, or update_baseline on the wire). A plain check must never quietly record whatever is currently installed as normal; that is how a compromise becomes the new baseline. Package ownership decides what is ordinary: a unit that arrived with a package is the system working, the same unit unowned is somebody's decision. Reuses the merged-/usr-aware index from the rootkit rewrite, with a test asserting most units resolve to a package — if that ratio collapses, ownership lookup has broken and the whole report is noise. Exercised end to end against this machine: baseline of 1,026 items, a planted user unit caught as ADDED, an in-place edit with a backdated mtime caught as CHANGED, and its deletion caught as REMOVED. The test artifact was removed afterwards. Cross-distro verification of the rootkit rewrite is now MET. Henry ran the suite on the Ubuntu box (26.04, glibc 2.43): 20/20, including both unowned_setuid_does_not_fire_on_a_healthy_system and no_false_positives_on_system_binaries. 214 tests pass across the workspace. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
58 lines
2.1 KiB
Text
58 lines
2.1 KiB
Text
hound-verify — cross-distro verification for Hound Antivirus
|
|
============================================================
|
|
|
|
What this is
|
|
------------
|
|
The Hound test suite, compiled. It is READ-ONLY: it installs nothing,
|
|
starts no daemon, needs no root, and does not modify system state. It
|
|
reads /usr/bin, /bin, /usr/sbin, /proc and dpkg's package index.
|
|
|
|
Requirements
|
|
------------
|
|
x86_64, glibc 2.39 or newer — Ubuntu 24.04 and up. (The floor comes from
|
|
Rust's standard library, not from Hound.) Check with: ldd --version
|
|
|
|
How to run
|
|
----------
|
|
chmod +x hound-verify
|
|
|
|
# The two checks that matter, together:
|
|
./hound-verify rootkit rules
|
|
|
|
# Or everything (~30s, mostly the goodware scan):
|
|
./hound-verify
|
|
|
|
What is actually being verified
|
|
-------------------------------
|
|
1. rootkit::tests::unowned_setuid_does_not_fire_on_a_healthy_system
|
|
|
|
Hound flags setuid binaries that no installed package claims. It asks
|
|
dpkg. Ubuntu's merged-/usr layout means every binary has two names
|
|
(/bin/sudo and /usr/bin/sudo), and dpkg records some packages under
|
|
one and some under the other. Getting this wrong makes Hound alarm on
|
|
a clean machine.
|
|
|
|
FAILURE OUTPUT names each binary it wrongly flagged. That is the
|
|
useful part — please paste it.
|
|
|
|
2. rules::tests::no_false_positives_on_system_binaries
|
|
|
|
Every Hound detection rule is scanned against every binary in
|
|
/usr/bin, /bin and /usr/sbin. A single hit fails the build. Ubuntu
|
|
ships binaries Linux Mint does not, so this is genuinely new ground.
|
|
|
|
FAILURE OUTPUT names the binary and the rule. A hit means I delete
|
|
that rule rather than tune it — a rule that flags a system binary is
|
|
worse than no rule.
|
|
|
|
3. Everything else in the suite comes along for the ride and is a bonus
|
|
signal: the fanotify policy tests, the capability arithmetic, the
|
|
supply-chain detectors.
|
|
|
|
What to send back
|
|
-----------------
|
|
Either "all passed" (which is a real result — it closes the last open
|
|
criterion on the rootkit rewrite), or the assertion text of anything
|
|
that failed. The messages are written to be self-explanatory.
|
|
|
|
Thanks — Hound
|