etc.git


etc

~ scripts and random things
git clone https://github.com/djnnvx/etc.git
log | files | refs | archive

gen_corpus.sh (6260B)

      1 #!/bin/bash
      2 # gen_corpus.sh - mint XDR-shaped seeds for each harness.
      3 #
      4 # libnfs reply bytes are XDR (Sun RPC variant) encodings of nested structs.
      5 # Easiest path for real seeds: run rpcinfo/showmount/nfs-ls against an actual
      6 # NFS server and capture replies with tcpdump. Without that, this script
      7 # writes a handful of minimal-but-valid synthetic encodings so AFL has
      8 # something better than /dev/urandom to mutate from.
      9 #
     10 # Seeds are intentionally tiny - the goal is to stake out the boundaries of
     11 # each procedure's accepted-vs-rejected tag and let AFL grow them.
     12 set -euo pipefail
     13 
     14 SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)"
     15 cd "${SCRIPT_DIR}"
     16 
     17 mkdir -p corpus/nfs3 corpus/nfs4 corpus/mount corpus/pdu
     18 
     19 w32() { printf '%08x' "$1" | xxd -r -p; }   # 32-bit big-endian write
     20 
     21 # --- NFSv3 GETATTR3res (NFS3_OK + a small fattr3) ---
     22 # First byte of fuzz input picks the decoder; seed 0x0c = GETATTR3res table idx
     23 # (counts from the fuzz_nfs3.c order; FSINFO is index 18, READ is 5, etc.)
     24 #
     25 # fattr3 layout (per RFC1813): ftype(u32) mode(u32) nlink(u32) uid(u32) gid(u32)
     26 # size(u64) used(u64) rdev(specdata3=u64) fsid(u64) fileid(u64)
     27 # atime(nfstime3=u64) mtime ctime  = 80 bytes.
     28 #
     29 # GETATTR3res = status(u32=0 OK) + fattr3
     30 {
     31     printf '\x0c'                   # idx=12 (GETATTR3res)
     32     w32 0                           # NFS3_OK
     33     w32 1                           # NF3REG
     34     w32 33188                       # mode = 0100644 octal
     35     w32 1                           # nlink
     36     w32 1000                        # uid
     37     w32 1000                        # gid
     38     printf '\x00\x00\x00\x00\x00\x00\x00\x10'  # size=16
     39     printf '\x00\x00\x00\x00\x00\x00\x00\x20'  # used=32
     40     printf '\x00\x00\x00\x00\x00\x00\x00\x00'  # rdev
     41     printf '\x00\x00\x00\x00\x00\x00\x00\x01'  # fsid
     42     printf '\x00\x00\x00\x00\x00\x00\x00\x42'  # fileid
     43     printf '\x00\x00\x00\x00\x00\x00\x00\x00'  # atime
     44     printf '\x00\x00\x00\x00\x00\x00\x00\x00'  # mtime
     45     printf '\x00\x00\x00\x00\x00\x00\x00\x00'  # ctime
     46 } > corpus/nfs3/getattr3_ok.bin
     47 
     48 # NFSv3 READDIR3res failure with post-op-attrs absent
     49 {
     50     printf '\x0f'                   # idx=15 READDIR3res
     51     w32 5                           # NFS3ERR_NOENT
     52     w32 0                           # post_op_attr present=false
     53 } > corpus/nfs3/readdir3_noent.bin
     54 
     55 # NFSv3 FSINFO3res success with the standard 1<<20 properties
     56 {
     57     printf '\x12'                   # idx=18 FSINFO3res
     58     w32 0                           # NFS3_OK
     59     w32 0                           # post_op_attr present=false
     60     w32 1048576                     # rtmax
     61     w32 1048576                     # rtpref
     62     w32 4096                        # rtmult
     63     w32 1048576                     # wtmax
     64     w32 1048576                     # wtpref
     65     w32 4096                        # wtmult
     66     w32 4096                        # dtpref
     67     printf '\x00\x00\x00\xff\xff\xff\xff\xff'   # maxfilesize
     68     printf '\x00\x00\x00\x00\x00\x00\x00\x00'   # time_delta seconds
     69     w32 0                           # time_delta nseconds
     70     w32 0x1b                        # properties = LINK|SYMLINK|HOMOGENOUS|CANSETTIME
     71 } > corpus/nfs3/fsinfo3.bin
     72 
     73 # --- NFSv4 COMPOUND4res: minimal NFS4_OK status, empty tag, zero ops ---
     74 {
     75     w32 0                           # status NFS4_OK
     76     w32 0                           # tag length = 0
     77     w32 0                           # array of resop4: count = 0
     78 } > corpus/nfs4/compound4_empty.bin
     79 
     80 # NFSv4 COMPOUND with one PUTROOTFH op
     81 {
     82     w32 0                           # status NFS4_OK
     83     w32 0                           # tag length = 0
     84     w32 1                           # resop count = 1
     85     w32 24                          # OP_PUTROOTFH = 24
     86     w32 0                           # NFS4_OK
     87 } > corpus/nfs4/compound4_putrootfh.bin
     88 
     89 # --- MOUNT3 mountres3: ok + small file handle + 1 auth flavour (AUTH_SYS=1) ---
     90 {
     91     printf '\x00'                   # idx=0 mountres3
     92     w32 0                           # MNT3_OK
     93     w32 8                           # fhandle3.len = 8
     94     printf 'ABCDEFGH'               # fhandle bytes
     95     w32 1                           # auth_flavors.len = 1
     96     w32 1                           # AUTH_SYS
     97 } > corpus/mount/mountres3_ok.bin
     98 
     99 # MOUNT3 exports listing one export with no group restriction
    100 {
    101     printf '\x01'                   # idx=1 exports
    102     w32 1                           # 1 entry (XDR optional: present=1)
    103     w32 5                           # dirpath length = 5
    104     printf '/data'
    105     printf '\x00\x00\x00'           # padding to 4-byte boundary
    106     w32 0                           # groups: empty (present=0)
    107     w32 0                           # next-entry present=0
    108 } > corpus/mount/exports_one.bin
    109 
    110 # --- PDU envelope: an accepted REPLY with SUCCESS body ---
    111 {
    112     printf '\x00'                   # as_reply (bit 0 = 0)
    113     w32 0x12345678                  # xid
    114     w32 1                           # direction = REPLY
    115     w32 0                           # reply_stat = MSG_ACCEPTED
    116     w32 0                           # verf flavour = AUTH_NONE
    117     w32 0                           # verf len = 0
    118     w32 0                           # accept_stat = SUCCESS
    119 } > corpus/pdu/reply_ok.bin
    120 
    121 # A rejected REPLY (RPC_MISMATCH)
    122 {
    123     printf '\x00'
    124     w32 0xdeadbeef
    125     w32 1                           # REPLY
    126     w32 1                           # MSG_DENIED
    127     w32 0                           # RPC_MISMATCH
    128     w32 2 ; w32 2                   # low=2 high=2 (we speak RPCv2)
    129 } > corpus/pdu/reply_denied.bin
    130 
    131 # A CALL frame (server side) - PORTMAP NULL
    132 {
    133     printf '\x01'                   # as_call (bit 0 = 1)
    134     w32 0xcafebabe
    135     w32 0                           # direction = CALL
    136     w32 2                           # rpcvers
    137     w32 100000                      # program = PORTMAP
    138     w32 2                           # version
    139     w32 0                           # procedure NULL
    140     w32 0 ; w32 0                   # AUTH_NONE cred
    141     w32 0 ; w32 0                   # AUTH_NONE verf
    142 } > corpus/pdu/call_pmap_null.bin
    143 
    144 ls -la corpus/nfs3/ corpus/nfs4/ corpus/mount/ corpus/pdu/
    145 echo "[+] Corpus generated. Drop additional real captures via:"
    146 echo "    sudo tcpdump -i any -w nfs.pcap port 2049"
    147 echo "    then split TCP payloads out (see README.md)."