v1.0.0 release + v1.1 features + crates.io publish prep - #22
Merged
Conversation
Error taxonomy (CRATE-DESIGN.md rules 3-6): - Rename NfsStat to Nfs2Stat, add Unknown(u32) for unrecognized wire values - Rename MountError::Denied to MountError::Status for cross-version consistency - Remove blanket From<E> for MountError<E>; construct Rpc(e) explicitly - Drop thiserror from nfswolf-nfs3; manual Display/Error impls - Add Nfs4Status classification methods (is_ok, is_permission_denied, is_stale, is_not_found, is_transient) and std::error::Error impl Binary-level fixes: - Fix auth.rs DRC comment: stamp is not part of the Linux knfsd DRC key (xid+proc+addr+version+arg_len+checksum); XID uniqueness is what matters - Document owner-override in credential ladder (NFSD_MAY_OWNER_OVERRIDE, C702 sec. 12.3.3): owner UID is unconditionally tried regardless of mode - Delete private is_permission_refusal from fuse.rs; use the library Nfs3Error::is_permission_denied instead (CRATE-DESIGN rule 5) - Fix WebNFS public-handle proxy bypass in analyzer: _proxy parameter was accepted but never used, leaking operator IP on both v3 and v2 probes
…tack Add src/proto/nfs2.rs with the Nfs2Client type alias and PooledNfs2 trait, mirroring the existing PooledNfs3 pattern. Extract common pool/circuit/transport setup into make_pooled_transport in probe.rs; add make_v2_client_with_hostname. Migrate all NFSv2 code paths to the shared helpers: - run_nfs2_shell: use make_mount_client + make_v2_client_with_hostname - V2Ops: collapse change_identity from 44-line TCP reconnect to 4-line with_credential swap (zero network round trips) - find_escape_v2: use make_v2_client_with_hostname, globals.hostname - sweep_inodes_v2: use make_v2_client_with_hostname, globals.hostname - Thread --aux-gids through all V2 credentials via build_gid_list Every global option now applies equally to v2 and v3: --proxy, --delay, --jitter, --uid, --gid, --hostname, --aux-gids, --nfs-port, --privileged-port, --mount-port. The per-encode stamp counter and circuit breaker carry over automatically from PooledTransport. Net -61 lines of boilerplate.
Phase 0 completion: - Null-string filename fingerprinting: send LOOKUP with zero-length name to classify server implementation (NFS3ERR_ACCES = spec-conformant, GARBAGE_ARGS = Linux knfsd, other = unknown). Added to scanner output. - Execute-implies-read: secrets-scan now tries reading execute-only files (nfsd_permission falls back to MAY_EXEC, C702 sec. 12.3.3). Analyzer reports when execute-only files are readable. - WebNFS public handle probe: try_webnfs_escape attempts the well-known public handles (v2: all-zero 32 bytes, v3: zero-length) with multi- component LOOKUP path traversal before falling back to handle forging. - Metadata leak on NFS3ERR_ACCES: analyzer harvests post_op_attr from denied operations, reporting uid/gid/size/mtime on access denial. Phase 1 foundation: - Portmapper: implement PMAPPROC_SET (1), PMAPPROC_UNSET (2), PMAPPROC_CALLIT (5). All 6 procedures now covered. - Program name table: program_name() and known_programs() with 10 well-known RPC programs (NFS, mountd, NLM, NSM, ypserv, etc.). Wired into scanner and report output. - Absorb udp.rs: RPC-over-UDP transport moved from src/proto/ to crates/nfswolf-rpc/src/transport/udp.rs where it belongs. - Delete rpc_probe.rs: 266 lines of parallel RPC parsing removed. RpcClient already preserves PROG_MISMATCH version ranges; the workaround outlived the limitation it was written for. Scanner migrated to use RpcClient directly. - trybuild tests: compile-fail test suite for xdr-derive with 4 test cases (missing attr, multi-field variant, named fields, union type) plus round-trip pass tests.
Phase 0 final: - NFSv4 on PooledTransport: add proto/nfs4/ with Nfs4Client type alias, PooledNfs4 trait (with_credential, machinename, uid, gid), and pooled compound helper. v4 shell now uses the same pool/circuit/stealth stack as v2/v3. Nfs4DirectClient remains for non-pooled contexts. Phase 2 extractions: - nfswolf-mount crate: extract MOUNT v1/v3 protocol into its own workspace crate (crates/nfswolf-mount/). Unified MountClient<T> parameterized by version. MountedHandle carries auth_flavors with accepts_auth_sys() and is_auth_sys_only() classification methods. Both nfswolf-nfs2 and nfswolf-nfs3 now depend on nfswolf-mount and re-export its types for backwards compatibility. - Nfs2RawClient<T>: wire-level client added to nfswolf-nfs2. Takes wire types in and returns wire types out, including call_raw() for arbitrary procedure numbers. Completes the rule: every crate that exposes wire types must also expose a wire-level client. - Nfs4Client<T>: generic client added to nfswolf-nfs4 (client.rs), mirroring Nfs3Client's pattern for the COMPOUND procedure.
Move portmapper v2 client (6 procedures: NULL, SET, UNSET, GETPORT, DUMP, CALLIT), rpcbind v3/v4 client (GETTIME, GETSTAT), PortmapError, all wire types, and the program_name/known_programs table from crates/nfswolf-rpc/ into crates/nfswolf-rpcbind/. The portmapper is program 100000 — a specific RPC service, not part of the RPC machinery. nfswolf-rpcbind depends on nfswolf-rpc for the transport seam; nfswolf-rpc no longer knows about portmapper concepts. Net -656 lines from the RPC crate. All callers updated.
…vectors Crate renames (CRATE-DESIGN.md target names): nfswolf-xdr-derive → onc-xdr-derive nfswolf-xdr → onc-xdr nfswolf-rpc → onc-rpc-client nfswolf-rpcbind → onc-rpcbind nfswolf-mount → nfs-mount nfswolf-nfs2 → nfs-v2 nfswolf-nfs3 → nfs-v3 nfswolf-nfs4 → nfs-v4 All Rust import paths updated (onc_xdr, onc_rpc_client, nfs_v2, etc.). Derive macro generates ::onc_xdr:: paths. Publication metadata: - keywords and categories on all 8 sub-crates - [package.metadata.docs.rs] with all-features + docsrs cfg - #[non_exhaustive] on all public enums (~50 enums) - Independent versioning: each sub-crate at 0.1.0 - Pre-1.0 README.md for every sub-crate Testing: - Golden-vector tests for onc-xdr, onc-rpc-client, nfs-v2, nfs-v3, nfs-v4 - MSRV check target added to Makefile 529 tests passing across 3 feature matrix configurations.
…tion Wire all 37 NFSv4.0 operations (RFC 7530 ops 3-39 + ILLEGAL) into ArgOp and ResOpData with full Pack/Unpack. Previously had 9; now complete. Supporting types: stateid4, open_owner4, lock_owner4, open_claim4, createhow4, locker4, state_protect4_a, cb_client4, nfs_client_id4, clientaddr4, OPEN4_RESULT_* flags, etc. Recon features: - SETCLIENTID compound builder convenience method for callback coercion probing (RFC 7530 §16.33) - OPEN compound builder for honest write testing (OPEN4_SHARE_ACCESS_READ) - RDMA presence detection: scanner checks rpcbind for rdma/rdma6 netids and probes port 20049; reports when RDMA transport is available - NFSv4 public filehandle escape: PUTPUBFH + LOOKUP(traversal path) + GETFH added to escape subcommand alongside existing v2/v3 probes 560 tests passing across 3 feature matrix configurations.
Add v41 and v42 Cargo features to nfs-v4 crate (v42 implies v41). v4.1 operations (RFC 8881), behind #[cfg(feature = "v41")]: - EXCHANGE_ID (op 42): server implementation fingerprinting via nfs_impl_id4 (vendor domain, product name, build date) - SECINFO_NO_NAME (op 52): security flavors for current filehandle without needing a filename; strictly better than v4.0 SECINFO - GETDEVICEINFO (op 47): pNFS device addressing — data-server IPs - GETDEVICELIST (op 48): enumerate all storage devices behind the filesystem for lateral-movement reconnaissance v4.2 attribute (RFC 7862), behind #[cfg(feature = "v42")]: - FATTR4_SEC_LABEL (attr 80): MAC/SELinux label as an attribute read Supporting types: client_owner4, nfs_impl_id4, state_protect_how4, secinfo4, sec_label4, deviceid4. Compiles cleanly with no features, v41 only, and all features.
All Phase 5 v4.0/v4.1/v4.2 operations complete. Phase 4 (publishing) removed from scope. Tier 3 sideband protocols annotated with specific research prerequisites per protocol — implementation blocked until spec reading, wire format verification, and live server testing are done.
Complete XDR type declarations, procedure tables, and constants for every Tier 3 protocol, extracted from authoritative specs: - NLM v3/v4: C702 ch 10 (pp 127-159), ch 14 (pp 263-269) 24 procedures, locking types, share reservations, async _MSG/_RES, NLM v4 64-bit deltas, spec erratum on procedure 12 numbering - NSM: C702 ch 11 (pp 161-174) 7 procedures, state model (even=down/odd=up), SM_MON callback - RQUOTA: Sun rquota.x 2 procedures, quota struct, v2 group quota extension - NFS_ACL: observed from Linux nfsacl.h + fs/nfsd/ (no public spec) 5 procedures, POSIX ACL entry types, every constant marked as observed deviation - NIS/YP: Sun yp.x 12 ypserv + 3 ypbind procedures, map types, well-known map names - RPCSEC_GSS: RFC 2203/5403/7861 Credential structure, mechanism OIDs with byte encodings, service enums, what recon-only parsing needs vs full context establishment - PCNFSD: D030 pp 88-108 4 procedures, auth XOR obfuscation algorithm with worked example, AUTH_RES status codes, print spool types This is research only — no Rust implementation. Wire format sections are placed after each protocol's existing security analysis.
…attack chains, C702 deep dive WebNFS wire format (C702 Appendix E pp 299-319): - MCL canonical and native (0x80 prefix) encoding rules - Public handle error semantics per version - Implementation variations (Linux/Solaris/NetApp/FreeBSD) Security analysis deepening: - RQUOTA: UID enumeration oracle mechanics, timing, quota-to-uid-spray pipeline - NIS: domain discovery methods, netgroup exploitation for export ACL bypass, per-map security analysis (passwd, group, netgroup, hosts, ethers) - NFS_ACL: mode-bit vs ACL bypass scenarios, GETACL as credential recon, default ACL inheritance as privilege widening vector Cross-protocol attack chains: - 8 compound chains connecting sideband protocols with NFS operations - NIS->NFS credential theft, NSM->NLM->NFS write, RQUOTA->uid-spray, NFS_ACL->hidden permission discovery, WebNFS->MOUNT bypass, PCNFSD->authenticated access, write verifier->DRC replay, metadata leak C702 deep dive (ch 9, ss12.3-12.4): - Write verifier reboot oracle exploitation details and HA detection - DRC replay window timing and dangerous operations - Locking architecture security (advisory-only, grace period, caller_name) - Server/client implementation vulnerabilities (attribute caching, silly rename)
HVS Consulting gap analysis: - Compared nfs_analyze (1906 lines) + fuse_nfs (538 lines) against NFSWolf's 41 findings. Identified 14 specific gaps with implementation estimates: NFSv4 pseudo-root UUID-v5 detection, fsid-based export inference, Windows NFSv4.1 handle types, OS fingerprinting heuristics, symlink escape feasibility, all_squash detection, shadow GID fallback, NetApp workaround, HP-UX TCP handling, FreeBSD subnet ambiguity, ZFS escape, disk group privesc, client mount option risk assessment RFC security audit (15 RFCs): - Systematic extraction of every file read, code execution, info leak, callback coercion, cache/replay, and implementation pitfall path - Organized by attack class with RFC section citations - Cross-referenced against NFSWolf's existing finding coverage New sections: - Privilege escalation via NFS: 10 client/server chains including the novel disk group GID 6 attack (works WITHOUT no_root_squash) - Filesystem-specific escape mechanics: handle structures for ext4, xfs, btrfs (subvolume 5), ZFS (inode 0x22), Windows (3 NFSv4.1 types) - OS fingerprinting signal catalog: 8 signals (handle prefix, length, version pattern, subnet format, TCP behavior, NetApp protocol, null LOOKUP) - Detection and monitoring gaps: why NFS attacks bypass auditd and IDS - RPC-with-TLS (RFC 9289): security model, AUTH_SYS-inside-TLS weakness, downgrade attack, implementation status 3113 lines, 164 subsections. Research-only — no Rust implementation.
Move tracing, fastrand, and tokio from inline version pins in sub-crate
Cargo.toml files to [workspace.dependencies] in the root. All sub-crates
now reference these via { workspace = true }, making version management
uniform across the 8-crate workspace.
docs: HVS gap analysis, RFC security audit, privilege escalation catalog,
filesystem escape mechanics, OS fingerprinting, detection gaps, RPC-with-TLS
FUTURE-RESEARCH.md now 3113 lines covering the complete NFS security
attack surface from HVS Consulting research, all 15 RFCs, C702, D030,
and cross-protocol attack chains.
All sub-crate Cargo.toml dependencies now use { workspace = true }.
Zero inline version pins remain. Audited every non-uniform line:
each is justified by the dependency graph (see commit message body).
NFSv4 is one protocol with one operation namespace (RFC 8178). The crate now unconditionally includes all v4.0, v4.1, and v4.2 types: 37 v4.0 operations, 4 v4.1 recon operations (EXCHANGE_ID, SECINFO_NO_NAME, GETDEVICEINFO, GETDEVICELIST), and the v4.2 FATTR4_SEC_LABEL attribute. Removed 33 #[cfg(feature)] gates and the [features] section from Cargo.toml.
None of the 8 sub-crates have Cargo features. The all-features = true in [package.metadata.docs.rs] was a no-op on every one of them.
Ensures only source, README, and upstream attribution (NOTICE) ship in published packages. Prevents accidental leakage of workspace files. - 6 crates with NOTICE: src/**, README.md, NOTICE - 2 crates without NOTICE: src/**, README.md - onc-xdr-derive also includes tests/** (trybuild suite)
homepage = https://strongwind.dev/NFSWolf Defined in [workspace.package], inherited by all 8 sub-crates + binary.
readme = "README.md" defined once in [workspace.package], inherited by all 9 packages via readme.workspace = true.
- Fix stale comments (eight crates, not two; nfs-v4 covers v4.0/4.1/4.2) - Use members glob: [".", "crates/*"] instead of listing all 8 - Deduplicate tokio and tracing: binary now uses workspace refs - Workspace tokio uses features = ["full"] (superset of the old split) - Trim verbose docs.rs comment to zero lines - Remove redundant dependency grouping comments - Trim lint policy comment to two lines
Crate READMEs (8 crates, 56-79 lines each): - Professional library-grade documentation with quick-start code examples using actual public API types (DirectTransport, Nfs3Client, etc.) - API overview tables listing key public types - Protocol coverage sections - Safety/hardening notes (codec crates) and security properties (NFS crates) Root README: - Test count: 469 -> 560 - Workspace crates: 6 -> 8 - Credits: nfswolf-* -> onc-*/nfs-* crate names Project instructions: - Crate table updated to current names and 8-crate layout - Protocol stack API examples use onc_rpc_client/nfs_v3/nfs_v2 paths - Test count and module references updated docs/ARCHITECTURE.md: - All crate name references updated - New modules documented (proto/nfs2.rs, nfs-mount, onc-rpcbind) - Deleted modules removed (proto/rpc_probe.rs) docs/DESIGN.md, NFSv2.md, NFSv3.md, NFSv4.md: - Stale crate name references updated throughout
New analyzer checks: - F-3.7: AUTH_DH (flavor 3) detection in MOUNT and SECINFO - F-3.8: AUTH_TLS STARTTLS probe (RFC 9289) for TLS detection - F-4.6: PATHCONF chown_restricted=false (ownership hijacking) - F-5.7: PATHCONF case_insensitive=true (Windows/NTFS fingerprint) - F-5.8: AUTH_NONE GETATTR metadata leak (RFC 2623 S2.3.2) Scanner enrichment: - Security notes for sideband RPC programs (NLM, NSM, RQUOTA, NFS_ACL, NIS, PCNFSD) in console and JSON output - NetApp management program 400010 in program table - Auth flavor enumeration per export via MNT probe Protocol crate changes: - AUTH_TLS (7) and RPCSEC_GSS (6) added to auth_flavor enum - security_note() function in onc-rpcbind for program labeling - Removed cfg(fuse) gate from NfsMountClient::unmount() - auth_flavors field added to ExportEntry Documentation: - FINDINGS.md: 6 new finding entries (F-3.7/3.8/4.6/5.6/5.7/5.8) - REQUIREMENTS.md: traceability for all new checks - TASKLIST.md: moved to project root, tracks v1.0 progress - Removed stale dead-code comment from main.rs
Numbers updated across all docs: - Finding count: 41 -> 47 (README, CLAUDE.md, DESIGN.md, ARCHITECTURE.md, REQUIREMENTS.md, findings/README.md) - Test count: 560 -> 542 (README, CLAUDE.md) - Shell command count: 44 -> 52 (README, CLAUDE.md, DESIGN.md, ARCHITECTURE.md) docs/NFSv4.md: - Operation coverage: all 37 v4.0 ops now representable as ArgOp variants, CompoundBuilder has 13+ typed methods - Added v4.1 ops (GETDEVICEINFO, GETDEVICELIST) and v4.2 security labels (FATTR4_SEC_LABEL, SecLabel4) - Updated shell commands table with v0.8.0 aliases - Nfs4Status variant count: 26 (25 named + Unknown) docs/DESIGN.md: - Backpressure: Notify -> Semaphore (matches actual pool code) docs/FINDINGS.md: - Cross-reference table: added 6 new findings docs/findings/: - Created F-5.6-metadata-on-access-denial.md (was missing) - findings/README.md: added F-3.7, F-3.8, F-4.6, F-5.6, F-5.7, F-5.8 Crate READMEs: - Fixed dirpath compilation errors in nfs-mount, nfs-v2, nfs-v3 - onc-rpcbind: added security_note() and netapp_mgmt to API table - onc-rpc-client: added auth_flavor with AUTH_TLS/RPCSEC_GSS - nfs-v4: updated operation coverage, v4.1/v4.2 extensions TASKLIST.md: replaced non-ASCII characters with ASCII equivalents
Wire layer: - Parse rpcsec_gss_info from SECINFO responses (mechanism OID, QOP, service level) instead of skipping it. New SecInfoEntry struct carries the full GSS tuple alongside the flavor number. - ResOpData::SecFlavors now returns Vec<SecInfoEntry> instead of Vec<u32>, enabling krb5/krb5i/krb5p discrimination from SECINFO without relying on Linux krb5 pseudo-flavors. Auth infrastructure: - Added AuthFlavor::Tls (7) for RFC 9289 AUTH_TLS classification. - Added flavor_name() in src/proto/auth.rs as the single source of truth for flavor-to-name mapping. Covers AUTH_NONE through AUTH_TLS and krb5 pseudo-flavors (390003-390005). - Replaced three separate inline match blocks (analyzer, scanner, mount) with calls to flavor_name(). - Fixed parse_flavor() in mount.rs to delegate to AuthFlavor::from_u32() which correctly handles krb5 pseudo-flavors (previously mapped them to Unknown). Analyzer: - check_nfs4_secinfo() now reports GSS service levels in evidence strings (RPCSEC_GSS(krb5) vs krb5i vs krb5p) decoded from the SECINFO rpcsec_gss_info, not just from MOUNT pseudo-flavors.
Emit F-3.9 when AUTH_SHORT (flavor 2) appears in MOUNT auth_flavors or NFSv4 SECINFO results. AUTH_SHORT opaque tokens are replayable from wire captures without knowing the original UID/GID (RFC 1057 S9.2, RFC 5531 Appendix A).
Scanner now issues SECINFO per NFSv4 pseudo-root entry to discover auth flavors. V4ExportEntry gains auth_flavors field. Console output shows [AUTH_SYS,RPCSEC_GSS(krb5p)] alongside v4 export paths. JSON output includes auth_flavors array for v4 exports.
Detect Kerberos enforcement at the NFS operation level via the AUTH_TOOWEAK RPC error (RFC 5531 S8.3). When MOUNT accepts AUTH_SYS but a subsequent GETATTR returns AUTH_TOOWEAK, the export enforces stronger authentication for data operations. Emits F-1.8 as an informational finding (positive security indicator).
flavor_name() now covers every assigned value from the IANA 'RPC Authentication Flavor Numbers' registry: - Added AUTH_KERB (4), AUTH_RSA (5), AUTH_NW (30001), AUTH_SEC (200000), AUTH_ESV (200004), AUTH_NQNFS (300000), AUTH_GSSAPI (300001), AUTH_ILU_UGEN (300002), AUTH_SPNEGO (390000) - Source: https://www.iana.org/assignments/rpc-authentication-numbers/flavor.csv AuthFlavor::from_u32 now classifies AUTH_SPNEGO (390000) as Gss. auth_stat enum now covers values 0-14 from the IANA 'RPC Authentication Status Numbers' registry, adding AUTH_KERB_GENERIC (8) through RPCSEC_GSS_CTXPROBLEM (14). RFC 7861 values 15-18 are omitted (require RPCSEC_GSS v3 context, which nfswolf does not establish). - Source: https://www.iana.org/assignments/rpc-authentication-numbers/status.csv
Complete the auth_stat enum with the remaining 4 IANA-assigned values: RPCSEC_GSS_INNER_CREDPROBLEM (15), RPCSEC_GSS_LABEL_PROBLEM (16), RPCSEC_GSS_PRIVILEGE_PROBLEM (17), RPCSEC_GSS_UNKNOWN_MESSAGE (18). auth_stat now covers 19/19 assigned values from the IANA registry.
Replace the hand-curated 13-entry program table with the full IANA 'RPC Program Numbers' registry. Every assigned program number from the CSV is now decoded by name in portmapper DUMP output. Source: https://www.iana.org/assignments/rpc-program-numbers/rpc-program-numbers-data.csv - 1251 entries (removed unassigned ranges, [unknown] entries, and duplicate program numbers) - Binary-searchable static table, zero heap allocation - Security notes retained for NFS-relevant sideband programs - Added security notes for rstatd, rusersd, yppasswdd, rexec, ypupdate, keyserv, ypxfrd, ttdbserverd, nsm_addr, sadmind
Version bump to 1.0.0. CHANGELOG documents all changes since v0.8.0: comprehensive auth enumeration, 8 new findings (F-1.8, F-3.7-3.9, F-4.6, F-5.6-5.8), complete IANA registries (1251 programs, 19 auth flavors, 19 status codes), scanner SECINFO probing, and NFSv4 wire coverage expansion. 49 documented findings. 543 tests. Live-tested across 6 VMs (32 exports, all write ops verified). Removed TASKLIST.md (all items complete). Added F-1.8 and F-3.9 write-ups. Updated finding counts across all docs to 49.
The verifier body was double-length-prefixed: a manual \x00\x00\x00\x07 was prepended to 'STARTTLS' (8 bytes, not 7), then the opaque_auth XDR encoding added another length prefix. RFC 9289 S4.1: 'The length of the verifier body field is eight' as a fixed-length opaque. The XDR layer handles the length prefix; the body should be the raw 8 bytes only.
Scanner: - OS fingerprint column from MNT handle (table, CSV, JSON) - CSV auth flavor column (deduplicated across v2/v3/v4 exports) Analyzer: - Windows v4.1 (28-byte) handle signing detection - Windows version pattern (v3+v4 without v2) in OS fingerprint - FreeBSD truncated subnet warning in EXPORT ACL - FSINFO time_delta (Solaris fingerprint) + properties bitmask - FSSTAT free-space and inode exhaustion check - .nfs* silly-rename detection (open-unlinked indicator) - Write verifier reboot oracle via zero-count COMMIT - EXCHANGE_ID (v4.1) vendor/version fingerprinting Shell: - verifier command (writeverf3 hex dump via COMMIT) Protocol crates: - commit_verifier() domain API on Nfs3Client - compound_v41() on Nfs4DirectClient (minorversion=1) - OsGuess::HpUx variant (reserved) - Independent crate versioning (0.2.0 for stable crates) Other: - FUSE mount suid+dev passthrough warning
…CINFO, xattrs Escape: - NFSv4 LOOKUPP chain escape (PUTROOTFH + LOOKUPP*N + GETFH) - verify_nfs4_root() confirms FS root via well-known entry LOOKUPs - Wired as final fallback in escape and scan --auto-escape Analyzer: - probe_pnfs_topology(): EXCHANGE_ID + GETDEVICELIST for pNFS MDS detection (F-3.5) - check_nfs4_sec_label(): FATTR4_SEC_LABEL SELinux label extraction (F-4.5) - check_nfs4_secinfo_per_path(): per-subdirectory SECINFO comparison (F-3.6) - check_nfs4_xattrs(): OPENATTR + READDIR for named attributes (F-5.13) Wire: - FATTR4_SEC_LABEL decode in GETATTR responses (RFC 7862 S12.2.4) - compound_v41() and lookup_fh() now used (dead_code removed)
…ocks 1-4) Handle variant derivation (file_handle.rs): - derive_handle_variants(): raw, trimmed, pad32, pad64 for any handle - dedup_variants(): remove byte-identical duplicates Handle probe orchestrator (probe.rs): - acquire_and_test_handles(): MOUNT v3 + v1, derive all variants, test each against NFSv3 + NFSv2 GETATTR - HandleProbeResult with best_v3() and escape_seeds() - TestedHandle records v3_ok, v3_stale, v2_ok per variant Analyzer integration (analyzer.rs): - analyze_export() now uses the handle matrix instead of MOUNT v3-only - Emits F-1.6 when MOUNT v1 leaks a handle that v3 denied - Selects the best working v3 handle for all subsequent checks - nfs_port field added to Analyzer struct for probe forwarding
… variants (Block 5) New find_escape_matrix() in escape.rs: - Uses acquire_and_test_handles() to get ALL handle variants from both MOUNT versions - For each working seed, constructs root candidates and derives length variants (raw, trimmed, pad32, pad64) of each candidate - Tests every variant against both NFSv3 and NFSv2 GETATTR - Catches F-1.6: MOUNT v1 leaks handle that v3 denies, used with v3 ops Integration: - run_inner(): matrix runs between v3 failure and v2 fallback - find_escape_any(): same insertion point for scan --auto-escape
Handle matrix probe client now connects directly to port 2049 instead of relying on the PooledTransport's lazy MOUNT (which fails when mountd v3 is disabled). This fixes the critical scenario where MOUNT v3 is off but NFSv3 is still running -- the v1 handle can now be tested against v3 ops. Analyzer export enumeration falls back to MOUNT v1 EXPORT when v3 EXPORT returns no results (covers mountd -N 3 configurations). Live-tested: .14 with mountd v3 disabled achieved full filesystem escape via MOUNT v1 handle -> NFSv3 GETATTR, including /etc/shadow read.
Scanner: - ExportEntry now has handle_hex field, populated from MNT response - v3 exports: calls MOUNT v3 MNT per export (handle + auth flavors) Falls back to MOUNT v1 MNT if v3 fails (F-1.6) - v2 exports: calls MOUNT v1 MNT per export (handle + auth flavors) Previously only called EXPORT (no MNT, no handles) - Handles shown in console output (fh=01000700... truncated) - Handles included in JSON output per export Shell: - v3 shell now falls back to MOUNT v1 when v3 fails - Forces direct port 2049 on v1 fallback to avoid lazy MOUNT v3 Live-tested: .14 with mountd v3 disabled -- shell connected via v1 handle
Previously only tested NFSv2 GETATTR on handles that happened to be exactly 32 bytes. Now every variant (raw, trimmed, pad32, pad64) gets its 32-byte form tested against NFSv2 via Nfs2FileHandle::from_bytes (which pads short and truncates long handles to exactly FHSIZE=32). This means a 20-byte trimmed v3 handle or a 28-byte raw v3 handle now also gets tested as a zero-padded 32-byte v2 handle -- catching cases where the v2 daemon accepts the padded form.
ROADMAP.md updated: - Analyzer/scanner enhancements: FSINFO, FSSTAT, xattrs, per-path SECINFO, CSV auth column marked Done - OS fingerprinting: Windows version pattern, FreeBSD subnet, write verifier marked Done - NFSv4 recon: EXCHANGE_ID, GETDEVICELIST, FATTR4_SEC_LABEL marked Done - Pre-publish checklist: keywords, cargo-hack, MSRV CI, versioning, name check all Done - HVS gaps: 5 of 6 Done (ZFS remains, needs lab) - Write verifier oracle + silly-rename detection marked Done - Publication phases updated (Phase 3 done, Phase 4 ready, Phase 5 done) tasklist.md removed -- served its purpose as a working document, all items either completed or tracked in ROADMAP.md
WebNFS MCL path traversal: marked Done (was incorrectly listed as pending -- try_webnfs_escape() with ../../../etc/passwd traversal has been implemented for v2/v3/v4 since v1.0.0). #[non_exhaustive] audit: corrected from 'all covered' to '16 pub enum types still lack it' -- mostly XDR wire types with derive macros. Phase 3 publication status updated to reflect this gap. OPEN honest write test: noted partial wire types exist (ArgOp::Open, encode_open_read, setclientid) even though integration is not done.
…fied
Analyzer SECINFO cascade (check_nfs4_secinfo):
1. SECINFO (op 33, v4.0) -- existing path
2. SECINFO_NO_NAME (op 52, v4.1) -- new fallback via compound_v41()
3. WRONGSEC oracle -- iterates AUTH_NONE/AUTH_SYS, tests each via
PUTROOTFH+LOOKUP, NFS4ERR_WRONGSEC (10016) = rejected
Extracted emit_secinfo_findings() shared helper so all three paths
emit the same findings (F-3.4, F-1.7, F-3.7, F-3.9) with source
attribution in evidence.
non_exhaustive: verified all 48 pub enum types are annotated (previous
audit claiming 16 gaps was incorrect -- automated scan confirms 0 gaps).
ROADMAP.md updated.
MOUNT v3 (nfswolf-nfs3/wire/mount.rs): - golden_mountres3_ok: 28-byte lab handle (fsid_type=7) + auth flavors - golden_mountres3_err_acces: MNT3ERR_ACCES status - golden_export_node: export list entry with path + allowed groups MOUNT v1 (nfswolf-nfs2/mount.rs): - golden_fhstatus_ok: 32-byte lab handle (fsid_type=4) Portmapper (nfswolf-rpc/portmap/types.rs): - golden_pmaplist_single_nfsv3: DUMP response with NFSv3/TCP/2049 - golden_getport_response: GETPORT returning port 2049 All 6 tests verify pack -> golden bytes -> unpack round-trip with real handle bytes from lab VM 10.252.0.14.
Removed all 'Done' entries: WebNFS MCL, write verifier, silly-rename, FSINFO/FSSTAT/xattrs, per-path SECINFO, WRONGSEC, CSV auth column, EXCHANGE_ID, SECINFO_NO_NAME, GETDEVICELIST, FATTR4_SEC_LABEL, Windows v4.1 handles, FreeBSD subnet, NFSv4 LOOKUPP escape, FUSE warning, and all pre-publish checklist items. Pre-publish checklist section replaced with 'complete' note. HVS gap table reduced to the one remaining item (ZFS). OS fingerprint table reduced to 2 pending items (HP-UX, CALLIT).
…crates.io - Standardize all 8 crate READMEs: centered headers, badges (CI, crates.io, edition, MSRV, license, docs.rs), nav links, API reference tables, ASCII dependency graph, protocol coverage, safety sections - Bump all crate versions from 0.x to 1.0.0 - Remove publish = false from all crates - Add documentation field (docs.rs) to all crates - Fix provenance text for nfs-v2 and nfs-v4 (were incorrectly listed as derived from Vaiz/nfs3; NOTICE files say original work) - Add crates.io and docs.rs badges to top-level README - Add cargo install nfswolf installation option - Add Protocol crates section with crates.io links - Add WEPWolf to Related tools
Each crate has its own README.md. The workspace-inherited readme resolved to ../../README.md (the workspace root), causing cargo publish to warn and fall back. Point each crate at its own file.
StrongWind1
force-pushed
the
v1-parity-and-fixes
branch
from
August 7, 2026 16:37
a9dfe6c to
2d17e72
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch covers three phases of work since v0.8.0: the v1.0.0 release (auth flavor enumeration, IANA registries, crate restructuring), post-v1.0.0 feature work (NFSv4 escape, handle matrix, SECINFO_NO_NAME oracle, OS fingerprinting), and crates.io publish preparation (standardized READMEs, version bump, metadata).
v1.0.0 release highlights
flavor_name()nfswolf-*to vendor-neutral names (onc-xdr,onc-rpc-client,nfs-v3, etc.)Post-v1.0.0 features
crates.io publish prep
publish = falsedocumentationfield (docs.rs) to all packagesreadmepath: workspace-inherited resolved outside the package; now crate-localcargo installoption, protocol crates table, WEPWolfTesting
make check-allpasses: fmt, clippy (zero warnings), cargo deny, 543 tests, doc, ascii-check, lf-check, machete.Publish order (after merge)