etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
README.md (3685B)
1 # libnfs fuzzing 2 3 Fuzzing harnesses for **libnfs** (Ronnie Sahlberg's userspace NFSv3 / NFSv4 / 4 MOUNT / NLM / NSM / PORTMAP / RQUOTA client). Same machine setup as the 5 libksba/libusb harnesses: AFL++ (CLASSIC + laf-intel), a CmpLog sibling per 6 harness, ASan + UBSan at `-O0 -g` with `signed-integer-overflow` and `shift` 7 dropped (XDR's `(v << 8) | next` idiom is pure noise under those checks). 8 9 Why libnfs: 10 - Same profile as libksba. Hand-rolled binary parser, ~87% of commits by one 11 person, ubiquitous (VLC `nfs://`, Kodi, gvfs, QEMU block layer, mpd, fio), 12 **not on OSS-Fuzz**, no fuzz harness in-tree. 13 - Attacker model: malicious NFS server. Victim's libnfs client (typically 14 invoked via `vlc nfs://attacker/x.mp4` style URLs) sends a request; the 15 attacker controls every reply byte. 16 17 ## Harnesses 18 19 | harness | drives | reach | 20 |-------------|---------------------------------------------|----------------------------------------| 21 | `fuzz_nfs3` | per-procedure `zdr_*_3res` decoders (21 of) | NFSv3 reply parsing (most common use) | 22 | `fuzz_nfs4` | `zdr_COMPOUND4res` | NFSv4 COMPOUND reply (biggest parser) | 23 | `fuzz_mount`| `zdr_mountres3`, `zdr_exports`, others | MOUNT v3 - the auth-less first surface | 24 | `fuzz_pdu` | `libnfs_zdr_callmsg` / `libnfs_zdr_replymsg`| RPC framing layer above the decoders | 25 26 The first byte of each input picks the sub-decoder where applicable, so each 27 corpus is shared across many procedures. 28 29 ## Usage 30 31 ``` 32 bash setup.sh # deps + AFL++ + instrumented libnfs + harnesses + corpus 33 bash start-fuzzers.sh # 8 instances in tmux (master + CmpLog secondary per harness) 34 bash start-fuzzers.sh --status 35 bash cleanup.sh # kill + remove artifacts (flags: --keep-corpus --keep-source ...) 36 ``` 37 38 `setup.sh` flags: `--skip-deps` (deps already installed), `--skip-afl` (use a 39 system afl-clang-fast instead of building AFL++ from source). 40 41 Each harness builds three ways: `H` (AFL persistent), `H.cmplog` (CmpLog), and 42 `H.repro` (plain clang + ASan, reads files from argv) for crash triage: 43 44 ``` 45 ./fuzz_nfs3.repro output/master_nfs3/crashes/id:000000* 46 ``` 47 48 ## Target version 49 50 `setup.sh` defaults to libnfs git tip (6.0.x). To pin to a release: 51 52 ``` 53 LIBNFS_TAG=libnfs-6.0.1 bash setup.sh 54 ``` 55 56 ## Soft spots flagged before fuzzing (corpus is biased toward these) 57 58 The libnfs parsing stack is XDR-shaped: tagged unions everywhere 59 (accepted-vs-rejected reply, NFS3res ok-vs-fail, NFS4 resop4 by opcode). 60 Classes of bug worth pre-seeding: 61 62 - **Negative/huge length fields** through `zdr_bytes`, `zdr_array`, 63 `zdr_string`. XDR length fields are u32; some callers cast to int and 64 loop. Trigger: `0xffffffff` in length slot. 65 - **Tag mismatch** in tagged unions: claim status=NFS3_OK then truncate the 66 expected `fattr3` payload, or claim a v4 opcode that doesn't match the 67 result body that follows. 68 - **Linked-list cycles** in MOUNT `exports` / `groups` (each entry has a 69 "next" pointer; nothing in the decoder caps traversal depth). 70 71 ## Notes 72 73 - libnfs's ZDR is a re-implementation of Sun XDR; it predates and replaces 74 glibc's `tirpc`. Bugs found here may or may not exist upstream in tirpc - 75 worth checking once a real crash lands. 76 - `--without-libkrb5` is set at configure time to avoid the GSSAPI optional 77 dep. Krb5-protected NFS isn't reachable for the kind of attacker we care 78 about (server-controlled) since the krb5 wrap is verified before the inner 79 payload is fed to ZDR. 80 - `--disable-utils --disable-examples`: skip the bundled tools - they bring 81 in unrelated link deps and we only need `libnfs.la`.