etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
setup.sh (12830B)
1 #!/bin/bash 2 # setup.sh — one-shot bootstrap for the linux-usb-stack syzkaller fuzzing environment. 3 # 4 # What this does (in order): 5 # 1. Install system dependencies 6 # 2. Install Go (from upstream if system Go < 1.21) 7 # 3. Clone + build syzkaller 8 # 4. Download + configure + build Linux mainline kernel 9 # 5. Build static busybox 10 # 6. Build static dropbear (SSH server for syzkaller VM access) 11 # 7. Generate SSH key pair for syzkaller ↔ VM communication 12 # 8. Assemble initramfs (busybox + dropbear + authorized_keys + /init) 13 # 9. Create workdirs for each fuzzing profile 14 # 15 # Re-running is safe: each step checks for existing artifacts and skips. 16 # 17 # Usage: 18 # bash setup.sh 19 # bash setup.sh --no-kernel # skip kernel build (use existing bzImage) 20 # bash setup.sh --no-syzkaller # skip syzkaller build 21 # bash setup.sh --skip-deps # skip apt-get installs 22 23 set -euo pipefail 24 25 SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)" 26 cd "${SCRIPT_DIR}" 27 28 BUSYBOX_VER="${BUSYBOX_VER:-1.36.1}" 29 DROPBEAR_VER="${DROPBEAR_VER:-2024.86}" 30 GO_MIN_VER="1.21" 31 GO_INSTALL_VER="${GO_INSTALL_VER:-1.22.4}" # upstream Go version to install if needed 32 SYZKALLER_REPO="https://github.com/google/syzkaller" 33 LINUX_REPO="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git" 34 35 SKIP_KERNEL=0 36 SKIP_SYZKALLER=0 37 SKIP_DEPS=0 38 for arg in "$@"; do 39 [[ "${arg}" == "--no-kernel" ]] && SKIP_KERNEL=1 40 [[ "${arg}" == "--no-syzkaller" ]] && SKIP_SYZKALLER=1 41 [[ "${arg}" == "--skip-deps" ]] && SKIP_DEPS=1 42 done 43 44 log() { echo "[*] $*"; } 45 ok() { echo "[+] $*"; } 46 warn() { echo "[!] $*"; } 47 48 # ── 1. System dependencies ─────────────────────────────────────────────────── 49 if [[ "${SKIP_DEPS}" -eq 1 ]]; then 50 ok "Skipping dependency install (--skip-deps)." 51 else 52 log "Installing system dependencies..." 53 sudo apt-get update -qq 54 sudo apt-get install -y \ 55 build-essential git curl wget python3 python3-pip tmux \ 56 libelf-dev libssl-dev flex bison bc cpio \ 57 libncurses-dev libncurses5-dev \ 58 pkg-config autoconf automake libtool \ 59 qemu-system-x86 \ 60 gcc-multilib \ 61 openssh-client \ 62 zlib1g-dev \ 63 gdb 64 ok "System dependencies installed." 65 fi 66 67 # ── 2. Go ──────────────────────────────────────────────────────────────────── 68 # syzkaller requires Go >= 1.21. Check system Go; install upstream if too old. 69 GO_BIN="" 70 for cand in "$(go env GOROOT 2>/dev/null)/bin/go" "${SCRIPT_DIR}/go/bin/go" /usr/local/go/bin/go; do 71 if [[ -x "${cand}" ]]; then 72 ver="$("${cand}" version | awk '{print $3}' | sed 's/go//')" 73 major="${ver%%.*}" 74 minor="${ver#*.}"; minor="${minor%%.*}" 75 req_minor="${GO_MIN_VER#*.}" 76 if [[ "${major}" -gt 1 ]] || [[ "${major}" -eq 1 && "${minor}" -ge "${req_minor}" ]]; then 77 GO_BIN="${cand}" 78 break 79 fi 80 fi 81 done 82 83 if [[ -z "${GO_BIN}" ]]; then 84 log "Go >= ${GO_MIN_VER} not found. Installing Go ${GO_INSTALL_VER} from upstream..." 85 GO_ARCHIVE="go${GO_INSTALL_VER}.linux-amd64.tar.gz" 86 GO_URL="https://golang.org/dl/${GO_ARCHIVE}" 87 wget -c --timeout=60 --tries=3 "${GO_URL}" -O "${GO_ARCHIVE}" 88 tar -C "${SCRIPT_DIR}" -xzf "${GO_ARCHIVE}" 89 rm -f "${GO_ARCHIVE}" 90 GO_BIN="${SCRIPT_DIR}/go/bin/go" 91 ok "Go ${GO_INSTALL_VER} installed to ${SCRIPT_DIR}/go/" 92 else 93 ok "Go found: ${GO_BIN} ($(${GO_BIN} version | awk '{print $3}'))" 94 fi 95 96 export PATH="$(dirname "${GO_BIN}"):${PATH}" 97 export GOPATH="${SCRIPT_DIR}/gopath" 98 export GOROOT="$(dirname "$(dirname "${GO_BIN}")")" 99 100 # ── 3. syzkaller ───────────────────────────────────────────────────────────── 101 if [[ "${SKIP_SYZKALLER}" -eq 1 ]]; then 102 ok "Skipping syzkaller build (--no-syzkaller)." 103 elif [[ -x "${SCRIPT_DIR}/syzkaller/bin/linux_amd64/syz-manager" ]]; then 104 ok "syzkaller already built, skipping." 105 else 106 log "Cloning syzkaller..." 107 [[ -d "syzkaller" ]] || git clone --depth=1 "${SYZKALLER_REPO}" syzkaller 108 109 log "Building syzkaller (this takes a few minutes)..." 110 cd syzkaller 111 make all -j"$(nproc)" 2>&1 | tail -5 112 cd "${SCRIPT_DIR}" 113 SYZ_MANAGER="$(find syzkaller/bin -name "syz-manager" -type f 2>/dev/null | head -1)" 114 [[ -n "${SYZ_MANAGER}" ]] || { warn "syz-manager not found after build. Check build output above."; exit 1; } 115 ok "syzkaller built: ${SYZ_MANAGER}" 116 fi 117 118 # ── 4. Linux kernel (mainline) ─────────────────────────────────────────────── 119 if [[ "${SKIP_KERNEL}" -eq 1 ]]; then 120 warn "--no-kernel: skipping kernel build." 121 elif [[ -f "bzImage" ]]; then 122 ok "bzImage already exists, skipping kernel build." 123 else 124 if [[ ! -d "linux" ]]; then 125 log "Cloning Linux mainline (shallow — latest tag only)..." 126 git clone --depth=1 "${LINUX_REPO}" linux 127 fi 128 129 log "Configuring kernel (allnoconfig + USB stack fragment)..." 130 cd linux 131 132 # GCC 15+ defaults to C23, where 'bool'/'false' are keywords — patch compressed/Makefile 133 if gcc --version 2>&1 | grep -qE "gcc \(.*\) (1[5-9]|[2-9][0-9])\."; then 134 sed -i 's/^KBUILD_CFLAGS := /KBUILD_CFLAGS := -std=gnu11 /' \ 135 arch/x86/boot/compressed/Makefile 136 warn "GCC 15+ detected: patched compressed/Makefile with -std=gnu11" 137 fi 138 139 make allnoconfig 140 scripts/kconfig/merge_config.sh -m .config "${SCRIPT_DIR}/qemu/kernel.config" 141 make olddefconfig 142 143 log "Building kernel (this takes ~20-30 min on first run)..." 144 make -j"$(nproc)" bzImage 2>&1 | tee /tmp/kbuild.log | \ 145 grep -E "^\s*(CC|LD|AR|error:|warning: )" || \ 146 { grep "error:" /tmp/kbuild.log | tail -20; exit 1; } 147 148 cp arch/x86/boot/bzImage "${SCRIPT_DIR}/bzImage" 149 cd "${SCRIPT_DIR}" 150 ok "Kernel built: bzImage" 151 fi 152 153 # ── 5. Busybox (static) ────────────────────────────────────────────────────── 154 BUSYBOX_DIR="busybox-${BUSYBOX_VER}" 155 BUSYBOX_TAG="${BUSYBOX_VER//./_}" # 1.36.1 → 1_36_1 156 BUSYBOX_REPO="https://github.com/mirror/busybox" 157 158 if [[ -d "busybox-install" ]]; then 159 ok "Busybox already built, skipping." 160 else 161 if [[ ! -d "${BUSYBOX_DIR}" ]]; then 162 log "Cloning busybox ${BUSYBOX_VER} (tag ${BUSYBOX_TAG})..." 163 git clone --depth=1 --branch "${BUSYBOX_TAG}" "${BUSYBOX_REPO}" "${BUSYBOX_DIR}" 164 fi 165 166 log "Building static busybox..." 167 cd "${BUSYBOX_DIR}" 168 make defconfig 169 sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config 170 sed -i 's/CONFIG_STATIC=n/CONFIG_STATIC=y/' .config 171 echo "CONFIG_STATIC=y" >> .config 172 sed -i 's/CONFIG_TC=y/CONFIG_TC=n/' .config 173 echo "CONFIG_TC=n" >> .config 174 make oldconfig < /dev/null || true 175 make -j"$(nproc)" 176 make install CONFIG_PREFIX="${SCRIPT_DIR}/busybox-install" 177 cd "${SCRIPT_DIR}" 178 ok "Busybox installed to busybox-install/" 179 fi 180 181 # ── 6. Dropbear (static SSH server) ────────────────────────────────────────── 182 # syzkaller connects to VMs via SSH. We bake a static dropbear into the initramfs 183 # so the VM runs a minimal SSH server without needing a full OS. 184 DROPBEAR_DIR="dropbear-${DROPBEAR_VER}" 185 DROPBEAR_TAR="${DROPBEAR_DIR}.tar.bz2" 186 DROPBEAR_URL="https://matt.ucc.asn.au/dropbear/releases/${DROPBEAR_TAR}" 187 188 if [[ -f "dropbear-install/sbin/dropbear" ]]; then 189 ok "Dropbear already built, skipping." 190 else 191 if [[ ! -d "${DROPBEAR_DIR}" ]]; then 192 log "Downloading dropbear ${DROPBEAR_VER}..." 193 wget -c --timeout=30 --tries=2 "${DROPBEAR_URL}" 194 tar -xjf "${DROPBEAR_TAR}" 195 rm -f "${DROPBEAR_TAR}" 196 fi 197 198 log "Building static dropbear (SSH server + dropbearkey)..." 199 cd "${DROPBEAR_DIR}" 200 ./configure \ 201 --prefix="${SCRIPT_DIR}/dropbear-install" \ 202 --disable-syslog \ 203 --disable-pam \ 204 --disable-utmp \ 205 --disable-wtmp \ 206 --disable-lastlog \ 207 CFLAGS="-O2 -static" \ 208 LDFLAGS="-static" 209 210 # Build only the server + key generator (not the client) 211 make PROGRAMS="dropbear dropbearkey" -j"$(nproc)" 212 mkdir -p "${SCRIPT_DIR}/dropbear-install/sbin" 213 cp dropbear "${SCRIPT_DIR}/dropbear-install/sbin/dropbear" 214 cp dropbearkey "${SCRIPT_DIR}/dropbear-install/sbin/dropbearkey" 215 cd "${SCRIPT_DIR}" 216 ok "Dropbear installed to dropbear-install/" 217 fi 218 219 # ── 7. SSH key pair ─────────────────────────────────────────────────────────── 220 # syz-manager uses qemu/id_rsa to SSH into VMs. 221 # The matching public key is baked into initramfs as authorized_keys. 222 if [[ -f "qemu/id_rsa" ]]; then 223 ok "SSH key pair already exists." 224 else 225 log "Generating SSH key pair for syzkaller ↔ VM access..." 226 ssh-keygen -t rsa -b 4096 -f qemu/id_rsa -N "" -C "syz-manager@usb-fuzz" 227 ok "SSH keys: qemu/id_rsa (private), qemu/id_rsa.pub (public)" 228 fi 229 230 # ── 8. Initramfs ───────────────────────────────────────────────────────────── 231 if [[ -f "qemu/initramfs.cpio.gz" ]]; then 232 ok "qemu/initramfs.cpio.gz already exists, skipping." 233 else 234 log "Assembling initramfs..." 235 rm -rf initramfs 236 mkdir -p initramfs/{bin,sbin,usr/bin,usr/sbin,proc,sys,dev,tmp,run,lib,lib64,etc,etc/dropbear,root/.ssh} 237 238 # Busybox — all applets as symlinks 239 cp busybox-install/bin/busybox initramfs/bin/busybox 240 (cd initramfs && bin/busybox --list | while read applet; do 241 ln -sf /bin/busybox bin/"${applet}" 2>/dev/null || true 242 done) 243 ln -sf /bin/busybox initramfs/bin/sh 244 ln -sf /bin/busybox initramfs/sbin/ip 245 ln -sf /bin/busybox initramfs/sbin/modprobe 246 247 # Dropbear (static, so no shared libraries needed) 248 cp dropbear-install/sbin/dropbear initramfs/sbin/dropbear 249 cp dropbear-install/sbin/dropbearkey initramfs/sbin/dropbearkey 250 chmod +x initramfs/sbin/dropbear initramfs/sbin/dropbearkey 251 252 # Bake in the SSH public key so syzkaller can authenticate 253 cp qemu/id_rsa.pub initramfs/root/.ssh/authorized_keys 254 chmod 700 initramfs/root/.ssh 255 chmod 600 initramfs/root/.ssh/authorized_keys 256 257 # init script 258 cp qemu/init initramfs/init 259 chmod +x initramfs/init 260 261 log "Packing initramfs..." 262 (cd initramfs && find . | cpio -oH newc 2>/dev/null | gzip -9) > qemu/initramfs.cpio.gz 263 ok "qemu/initramfs.cpio.gz: $(du -sh qemu/initramfs.cpio.gz | cut -f1)" 264 fi 265 266 # ── 9. Disk image (required by syzkaller's QEMU VM type) ───────────────────── 267 # syzkaller validates that the image file exists even when booting from 268 # kernel + initramfs. We create a minimal ext4 image; QEMU attaches it as 269 # an unused second drive — the actual rootfs comes from the initramfs. 270 if [[ -f "qemu/disk.img" ]]; then 271 ok "qemu/disk.img already exists, skipping." 272 else 273 log "Creating minimal disk image (128M)..." 274 dd if=/dev/zero of=qemu/disk.img bs=1M count=128 status=none 275 mkfs.ext4 -q qemu/disk.img 276 ok "qemu/disk.img created." 277 fi 278 279 # ── 10. Workdirs ────────────────────────────────────────────────────────────── 280 for profile in raw-gadget gadget-fw usbfs; do 281 mkdir -p "workdir-${profile}" 282 done 283 ok "Workdirs created: workdir-raw-gadget/ workdir-gadget-fw/ workdir-usbfs/" 284 285 # ── Done ───────────────────────────────────────────────────────────────────── 286 echo "" 287 ok "Setup complete. Next steps:" 288 echo " 1. Start fuzzing: bash run-fuzzers.sh [--raw-gadget|--gadget-fw|--usbfs|--all]" 289 echo " 2. Web UI: http://127.0.0.1:56741 (raw-gadget profile)" 290 echo " http://127.0.0.1:56742 (gadget-fw profile)" 291 echo " http://127.0.0.1:56743 (usbfs profile)" 292 echo " 3. Verify QEMU: qemu-system-x86_64 -kernel bzImage -initrd qemu/initramfs.cpio.gz \\" 293 echo " -nographic -append 'console=ttyS0 quiet' -m 2048M" 294 echo " 4. Stop: bash cleanup.sh"