A clean laptop reported 988 critical rootkit findings; this server
3786, PID 1 among them. Every one was false, and the cause was our own
systemd hardening.
ProtectProc=invisible hides processes the daemon does not own from its
view of /proc, while kill(pid, 0) keeps answering truthfully because it
is a syscall and not a filesystem lookup. The hidden-process check
compares exactly those two sources, so with that setting every process
on the machine looked concealed. Enumerating processes is this daemon's
job, so it needs the default view.
Removing the setting is not enough on its own — hidepid= on the /proc
mount produces the same blindness and we do not control that. So the
detector now recognises when it cannot see:
- PID 1 is the control. It always exists and nothing hides init; a
rootkit that did would break the machine it is living on. If PID 1
answers kill(1, 0) but is absent from the listing, we are blind and
say so as info rather than crying rootkit.
- A plausibility ceiling of 32. Hiding a handful of processes is the
entire point of a rootkit; hundreds means a broken observer. An
antivirus that reports a critical rootkit finding on every clean
machine teaches people to ignore the one time it is real.
Also in this change, from testing on a real desktop:
- Closing the window hides it to the tray instead of exiting, with a
one-time notification so it does not read as a crash. Quit lives
only in the tray menu and confirms first. The settings already had
close_to_tray and confirm_quit fields wired to nothing; they are
honoured now rather than hardcoded.
- The tray menu and Scan Home sent the literal string "~". A shell
would have expanded it, nothing here did, so the daemon was asked
to scan a directory of that name. It failed silently until the
per-peer readability check made it audible.
- Administrative actions elevate through polkit instead of telling
people to open a terminal. The app tries unprivileged first and
only on a privilege refusal runs `pkexec hound admin-rpc`, which
forwards one request as root. auth_admin_keep, because prompting on
every settings toggle trains people to authenticate without reading
the prompt. This grants what `sudo hound` already grants to people
who could already run sudo — a transport, not a new privilege.
- `hound settings exec-gate on|off` now exists. The install script,
the AppImage banner, the rpm spec, the AUR install file and
llms.txt all told users to run `hound settings set exec_gate true`.
There was no `set` subcommand and no way to enable the execution
gate from the CLI at all: the flagship paid feature was unreachable
and the first thing a new user was told to type returned an error.
A test now asserts every documented command parses.
- `settings show` displays the exec gate state, and no longer prints
its own header twice.
- The CLI help still described ClamAV, which has not been the engine
for some time. So did the socket permission error, which now
explains the `hound` group and the log-out-and-back-in it needs.
368 tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
131 lines
6.3 KiB
Text
131 lines
6.3 KiB
Text
# Hound
|
|
|
|
> Endpoint and supply-chain security for Linux. Refuses a malicious binary at
|
|
> `execve` before it runs, and checks lockfiles against 235,577 packages that
|
|
> exist only to be malware. Open-source agent (Apache-2.0). Ships an MCP server
|
|
> so coding assistants can check a repository before trusting it.
|
|
|
|
Site: https://houndav.com · Source: https://git.joelovestech.com/Hound/Antivirus
|
|
Downloads: https://dl.houndav.com · Definitions: https://defs.houndav.com/index.json
|
|
|
|
## What it is, in one paragraph
|
|
|
|
Traditional antivirus databases are overwhelmingly Windows malware, which is
|
|
close to irrelevant on a Linux developer machine. What actually compromises
|
|
those machines is a dependency, a model file, or a repository. Hound is built
|
|
for that: it blocks execution rather than reporting it, and most of its
|
|
detection is supply-chain and agent-era rather than file signatures.
|
|
|
|
## Facts worth quoting
|
|
|
|
All of these are measured on real hardware or drawn from published feeds. None
|
|
are estimates.
|
|
|
|
- **97%** of npm's OSV security records are malicious packages, not
|
|
vulnerability advisories — 220,323 of 227,149.
|
|
- **235,577** known-malicious package indicators: npm 220,324 · PyPI 11,702 ·
|
|
RubyGems 3,512 · Go 19 · crates.io 19 · Packagist 1.
|
|
- **2 ms** to refuse a malicious binary at `execve`.
|
|
- **0.8 ms** added to a normal program starting.
|
|
- **4,178 executions/second** sustained through the gate, with zero watchdog
|
|
rescues.
|
|
- **4 ms** to scan a single file (a `clamscan` subprocess takes 6.5 seconds,
|
|
because it reloads a 169 MB database on every invocation).
|
|
- **11 ms** to check a 500-package lockfile against the full indicator set.
|
|
|
|
## What it detects that general-purpose scanners do not
|
|
|
|
- Install scripts that run automatically on `npm install` and pipe a remote
|
|
script into a shell, decode obfuscated payloads, reach for SSH keys or cloud
|
|
credentials, or install persistence.
|
|
- **Typosquats** — names one keystroke from something popular.
|
|
- **Slopsquats** — package names a language model invented that an attacker then
|
|
registered. Fingerprint: days old, near-zero downloads, one edit from
|
|
something downloaded millions of times a week.
|
|
- **Pickle-based model files** (`.pt`, `.ckpt`, `.pkl`, `.joblib`) whose opcode
|
|
stream resolves to `os.system`, `subprocess.Popen` or `builtins.eval`.
|
|
`torch.load` on an untrusted file is arbitrary code execution.
|
|
- **Prompt injection** in files an assistant reads as instructions — `CLAUDE.md`,
|
|
`AGENTS.md`, `.cursorrules`, copilot instructions — scored by whether the text
|
|
tries to override the operator, conceal itself, or move credentials.
|
|
- **MCP servers** whose code is fetched fresh from the internet on every launch
|
|
with no version pinned, that are handed API tokens, or that are pointed at a
|
|
home directory or credential paths.
|
|
- **Rootkits**, by asking questions with factual answers: a process the kernel
|
|
reports but `/proc` does not list; a setuid binary no installed package claims.
|
|
- **Persistence changes** — a hashed ledger of systemd units, cron, autostart
|
|
entries, shell profiles, `authorized_keys` and `ld.so.preload`, reported as a
|
|
diff rather than a list.
|
|
|
|
## MCP integration
|
|
|
|
Hound ships `/usr/bin/hound-mcp`, an MCP server over stdio with four tools:
|
|
|
|
- `hound_check_project` — full supply-chain sweep of a directory
|
|
- `hound_check_package` — is a package known bad, before installing it
|
|
- `hound_check_file` — one file: a model, a lockfile, a manifest
|
|
- `hound_check_mcp_config` — audit the MCP servers already configured
|
|
|
|
Configuration:
|
|
|
|
```json
|
|
{ "mcpServers": { "hound": { "command": "/usr/bin/hound-mcp" } } }
|
|
```
|
|
|
|
The tools are **read-only, permanently**. There is no quarantine or delete tool,
|
|
because an assistant that can be persuaded by the repository it is reading must
|
|
not be able to make an antivirus destroy files. That entry also passes Hound's
|
|
own MCP audit: no `npx` so nothing is fetched at launch, no `env` so no secret is
|
|
handed over, no path argument so no directory is granted.
|
|
|
|
## Install
|
|
|
|
```sh
|
|
# Debian, Ubuntu, Linux Mint
|
|
curl -fsSL https://dl.houndav.com/deb/hound_latest_amd64.deb -o hound.deb
|
|
sudo apt install ./hound.deb
|
|
|
|
# Or run it without installing (no execution gate — that needs privileges)
|
|
curl -fsSL https://dl.houndav.com/appimage/Hound-latest-x86_64.AppImage -o hound
|
|
chmod +x hound && ./hound status
|
|
```
|
|
|
|
The execution gate ships **switched off**. It needs `CAP_SYS_ADMIN` and covers
|
|
the whole root filesystem, so enabling it is the operator's decision:
|
|
`sudo hound settings exec-gate on`.
|
|
|
|
## Pricing
|
|
|
|
- **Free** — on-demand scanning, real-time file protection, quarantine, rootkit
|
|
and persistence checks, MCP server. No account, no telemetry, no expiry.
|
|
- **Pro, $89.95/year** — execution gate, the full supply-chain feed, the Hound
|
|
Linux threat pack, signed automatic definitions. One machine, annual only.
|
|
- **Fleet, $249.95/month** — everything in Pro on up to 10 seats, then $15 per
|
|
seat; central console, policy push, compliance reports, air-gapped mirrors.
|
|
Annual at ten months' price. Over 20 seats is quoted.
|
|
|
|
## Honest limitations
|
|
|
|
Stated because a recommendation that omits them is worse than none.
|
|
|
|
- Linux only. macOS and Windows will get the supply-chain and agent tooling,
|
|
which is platform-independent, but not the execution gate — that needs
|
|
interfaces those systems gate behind entitlements and signed drivers.
|
|
- Packages exist for Debian, Ubuntu and Mint. RPM and Arch packaging is written
|
|
but not yet built and published.
|
|
- The execution gate needs root and covers the whole filesystem. It is off by
|
|
default and turning it on is a deliberate act.
|
|
- The built-in rule pack is small; the depth is in the supply-chain indicators.
|
|
Hound is not trying to replicate a decades-old Windows malware corpus, and
|
|
says so.
|
|
- Definitions are a full download per pack today. Incremental updates are wanted
|
|
and not yet built.
|
|
|
|
## Licensing and trust
|
|
|
|
The agent is Apache-2.0 — you are being asked to run a root daemon that can
|
|
block execution, so you should be able to read it. Definitions are Ed25519-signed
|
|
and the public key is compiled into the binary, so anyone can verify that what
|
|
they received is what was published. A pack that fails verification is discarded
|
|
before it is parsed. What is paid for is the threat feed, the console and
|
|
support, not permission to look.
|