etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
build_fuzzers.sh (17597B)
1 #!/bin/bash 2 # Build TTYD fuzzing harnesses 3 # 4 # What this script does: 5 # 1. Installs build dependencies 6 # 2. Clones ttyd source + runs cmake (for validation & compile_commands.json) 7 # 3. Generates mock headers/stubs so ttyd's .c files compile without real lws/uv 8 # 4. Compiles instrumented ttyd objects (http.c, protocol.c, utils.c) 9 # 5. Builds AFL++ harnesses that #include the real ttyd source files 10 # 6. Sets up AFL++ dictionaries (download from AFL++ repo, fall back to custom) 11 # 7. Generates seed corpus 12 # 8. Installs a crontab entry to back up crashes every 15 minutes 13 # 14 # Usage: ./build_fuzzers.sh [afl|libfuzzer] 15 # 16 # Note: gen-corpus.sh is merged into this script and is no longer separate. 17 18 set -e 19 20 SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 21 MODE="${1:-afl}" 22 SANITIZER="address,undefined" 23 24 TTYD_SRC="$SCRIPT_DIR/ttyd-src" 25 TTYD_OBJ="$SCRIPT_DIR/ttyd-obj" 26 FUZZ_INCLUDE="$SCRIPT_DIR/fuzz-include" 27 CRONTAB_MARKER="# ttyd-fuzz-crash-backup" 28 BACKUP_SCRIPT="$SCRIPT_DIR/backup_crashes.sh" 29 30 echo "[*] Building TTYD fuzzing harnesses" 31 echo "[*] Mode: $MODE" 32 echo "[*] Sanitizer: $SANITIZER" 33 echo "[*] Script: $SCRIPT_DIR" 34 echo 35 36 # ─── 1. dependency install ──────────────────────────────────────────────────── 37 38 install_deps() { 39 if [ "$(id -u)" -ne 0 ]; then 40 echo "[!] Warning: Not running as root. Skipping apt-get installs." 41 echo " If builds fail, run: sudo apt-get install afl++ libjson-c-dev" 42 echo " cmake git libwebsockets-dev libuv1-dev pkg-config" 43 return 44 fi 45 46 local DEPS="libjson-c-dev cmake git pkg-config" 47 if [ "$MODE" = "afl" ]; then 48 DEPS="$DEPS afl++" 49 else 50 DEPS="$DEPS clang" 51 fi 52 53 local MISSING="" 54 for pkg in $DEPS; do 55 dpkg -s "$pkg" &>/dev/null || MISSING="$MISSING $pkg" 56 done 57 58 if [ -n "$MISSING" ]; then 59 echo "[*] Installing:$MISSING" 60 apt-get install -y --no-install-recommends $MISSING 61 echo "[+] Dependencies installed" 62 fi 63 64 # System tuning for AFL++ 65 if [ "$MODE" = "afl" ]; then 66 local CORE_PATTERN 67 CORE_PATTERN=$(cat /proc/sys/kernel/core_pattern 2>/dev/null || echo "") 68 if [[ "$CORE_PATTERN" == "|"* ]]; then 69 echo core > /proc/sys/kernel/core_pattern 70 echo "[+] core_pattern set to 'core'" 71 fi 72 for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do 73 [ -f "$gov" ] && echo performance > "$gov" 2>/dev/null || true 74 done 75 fi 76 } 77 78 # ─── 2. clone + cmake ttyd ──────────────────────────────────────────────────── 79 80 setup_ttyd_source() { 81 # Clone 82 if [ ! -d "$TTYD_SRC/.git" ]; then 83 echo "[*] Cloning ttyd source..." 84 git clone --depth=1 https://github.com/tsl0922/ttyd "$TTYD_SRC" 85 echo "[+] Cloned ttyd to $TTYD_SRC" 86 else 87 echo "[*] ttyd source already present — pulling latest..." 88 git -C "$TTYD_SRC" pull --ff-only 2>/dev/null || \ 89 echo "[!] Pull skipped (uncommitted local changes or offline)" 90 fi 91 92 # cmake configure (validates deps, produces compile_commands.json for IDEs) 93 local BUILD_DIR="$TTYD_SRC/build" 94 mkdir -p "$BUILD_DIR" 95 echo "[*] Running cmake configure..." 96 ( 97 cd "$BUILD_DIR" 98 # Use the real compiler here (not AFL++) — this is only for validation/headers 99 cmake -DCMAKE_BUILD_TYPE=Debug \ 100 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ 101 .. 2>&1 | tail -5 || true 102 ) 103 echo "[+] cmake done (compile_commands.json at $BUILD_DIR/compile_commands.json)" 104 105 # ── verify mock headers/sources exist ──────────────────────────────── 106 # These files are checked into the repo alongside the harnesses. 107 # (Previously they were generated inline by this script.) 108 for f in "$FUZZ_INCLUDE/libwebsockets.h" "$FUZZ_INCLUDE/uv.h" \ 109 "$SCRIPT_DIR/fuzz_lws_mock.h" "$SCRIPT_DIR/fuzz_lws_mock.c" \ 110 "$SCRIPT_DIR/mock_server_globals.c" "$SCRIPT_DIR/mock_pty_stubs.c"; do 111 if [ ! -f "$f" ]; then 112 echo "[!] Missing required file: $f" 113 echo " These files should be checked in alongside the harnesses." 114 exit 1 115 fi 116 done 117 echo "[+] Mock headers and stubs verified" 118 119 # ── compile instrumented ttyd objects ──────────────────────────────── 120 mkdir -p "$TTYD_OBJ" 121 local JSON_CFLAGS 122 JSON_CFLAGS=$(pkg-config --cflags json-c 2>/dev/null || echo "") 123 124 local OBJ_CC OBJ_CFLAGS 125 if [ "$MODE" = "afl" ]; then 126 OBJ_CC="afl-clang-fast" 127 else 128 OBJ_CC="clang" 129 fi 130 OBJ_CFLAGS="-fsanitize=$SANITIZER -g -O1 -D_GNU_SOURCE -std=gnu99" 131 OBJ_CFLAGS="$OBJ_CFLAGS -I$FUZZ_INCLUDE -I$TTYD_SRC/src $JSON_CFLAGS" 132 # Suppress warnings from upstream code we don't control 133 OBJ_CFLAGS="$OBJ_CFLAGS -Wno-implicit-function-declaration -Wno-int-conversion" 134 135 echo 136 echo "[*] Compiling instrumented ttyd objects..." 137 # Only utils.c is pre-compiled as a shared object. 138 # http.c and protocol.c are compiled directly into each harness via 139 # #include — pre-compiling them here would cause multiple-definition 140 # linker errors. 141 for src in utils; do 142 echo " $src.c → ttyd-obj/$src.o" 143 $OBJ_CC $OBJ_CFLAGS -c "$TTYD_SRC/src/$src.c" -o "$TTYD_OBJ/$src.o" 144 done 145 146 # Mock objects 147 for src in fuzz_lws_mock mock_server_globals mock_pty_stubs; do 148 echo " $src.c → ttyd-obj/$src.o" 149 $OBJ_CC $OBJ_CFLAGS -c "$SCRIPT_DIR/$src.c" -o "$TTYD_OBJ/$src.o" 150 done 151 152 echo "[+] ttyd objects compiled to $TTYD_OBJ/" 153 } 154 155 # ─── 3. dictionaries ────────────────────────────────────────────────────────── 156 157 setup_dictionaries() { 158 local DICT_DIR="$SCRIPT_DIR/dictionaries" 159 mkdir -p "$DICT_DIR" 160 161 # Try system AFL++ dictionaries first 162 for sys_dir in /usr/share/afl++/dictionaries /usr/local/share/afl++/dictionaries; do 163 if [ -d "$sys_dir" ]; then 164 [ -f "$sys_dir/http.dict" ] && [ ! -f "$DICT_DIR/http.dict" ] && \ 165 cp "$sys_dir/http.dict" "$DICT_DIR/http.dict" && \ 166 echo "[+] Copied http.dict from $sys_dir" 167 [ -f "$sys_dir/json.dict" ] && [ ! -f "$DICT_DIR/json.dict" ] && \ 168 cp "$sys_dir/json.dict" "$DICT_DIR/json.dict" && \ 169 echo "[+] Copied json.dict from $sys_dir" 170 break 171 fi 172 done 173 174 # Download from AFL++ GitHub if still missing (http.dict and json.dict 175 # ship under those exact names in the AFL++ stable branch) 176 local BASE="https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/dictionaries" 177 local DL="" 178 command -v wget &>/dev/null && DL="wget -q -O" 179 command -v curl &>/dev/null && [ -z "$DL" ] && DL="curl -fsSL -o" 180 181 for dict in http json; do 182 if [ ! -f "$DICT_DIR/$dict.dict" ] && [ -n "$DL" ]; then 183 echo "[*] Downloading $dict.dict from AFL++ repo..." 184 $DL "$DICT_DIR/$dict.dict" "$BASE/$dict.dict" && \ 185 echo "[+] Downloaded $dict.dict" || \ 186 rm -f "$DICT_DIR/$dict.dict" 187 fi 188 done 189 190 # Minimal fallback if still missing (from checked-in fallback files) 191 if [ ! -f "$DICT_DIR/http.dict" ] && [ -f "$SCRIPT_DIR/dictionaries/http_fallback.dict" ]; then 192 cp "$SCRIPT_DIR/dictionaries/http_fallback.dict" "$DICT_DIR/http.dict" 193 echo "[+] Created dictionaries/http.dict (fallback)" 194 fi 195 196 if [ ! -f "$DICT_DIR/json.dict" ] && [ -f "$SCRIPT_DIR/dictionaries/json_fallback.dict" ]; then 197 cp "$SCRIPT_DIR/dictionaries/json_fallback.dict" "$DICT_DIR/json.dict" 198 echo "[+] Created dictionaries/json.dict (fallback)" 199 fi 200 201 echo "[+] Dictionaries: $(ls "$DICT_DIR")" 202 } 203 204 # ─── 4. corpus generation ───────────────────────────────────────────────────── 205 206 generate_corpus() { 207 echo "[*] Running gen_corpus.py..." 208 python3 "$SCRIPT_DIR/gen_corpus.py" "$SCRIPT_DIR" 209 } 210 211 # ─── 5. crash backup crontab ────────────────────────────────────────────────── 212 213 setup_crash_backup() { 214 local BACKUP_DIR="$SCRIPT_DIR/crashes-backup" 215 216 # Write the backup script from template 217 sed -e "s|%%CRONTAB_MARKER%%|$CRONTAB_MARKER|g" \ 218 -e "s|%%SCRIPT_DIR%%|$SCRIPT_DIR|g" \ 219 -e "s|%%BACKUP_DIR%%|$BACKUP_DIR|g" \ 220 "$SCRIPT_DIR/backup_crashes.sh.in" > "$BACKUP_SCRIPT" 221 chmod +x "$BACKUP_SCRIPT" 222 echo "[+] Created $BACKUP_SCRIPT" 223 224 # Install crontab entry (idempotent — remove stale entry first) 225 local TMPFILE 226 TMPFILE=$(mktemp) 227 crontab -l 2>/dev/null | grep -v "$CRONTAB_MARKER" | grep -v "backup_crashes.sh" > "$TMPFILE" || true 228 { 229 cat "$TMPFILE" 230 echo "$CRONTAB_MARKER" 231 echo "*/15 * * * * $BACKUP_SCRIPT >> $SCRIPT_DIR/crashes-backup/backup.log 2>&1" 232 } | crontab - 233 rm -f "$TMPFILE" 234 echo "[+] Crontab entry installed (every 15 min)" 235 crontab -l | grep -A1 "$CRONTAB_MARKER" 236 } 237 238 # ─── 6. select compiler ─────────────────────────────────────────────────────── 239 240 case "$MODE" in 241 afl) 242 if ! command -v afl-clang-fast &>/dev/null; then 243 echo "[!] afl-clang-fast not found. Install AFL++ first." 244 exit 1 245 fi 246 CC="afl-clang-fast" 247 CFLAGS="-fsanitize=$SANITIZER -g -O1" 248 SUFFIX="" 249 ;; 250 libfuzzer) 251 if ! command -v clang &>/dev/null; then 252 echo "[!] clang not found. Install clang first." 253 exit 1 254 fi 255 CC="clang" 256 CFLAGS="-fsanitize=fuzzer,$SANITIZER -g -O1" 257 SUFFIX="_lf" 258 ;; 259 *) 260 echo "[!] Unknown mode: $MODE (use 'afl' or 'libfuzzer')" 261 exit 1 262 ;; 263 esac 264 265 # ─── find json-c ───────────────────────────────────────────────────────────── 266 267 JSON_C_CFLAGS=$(pkg-config --cflags json-c 2>/dev/null || echo "") 268 JSON_C_LIBS=$(pkg-config --libs json-c 2>/dev/null || echo "-ljson-c") 269 if [ -z "$JSON_C_CFLAGS" ]; then 270 for d in /usr/include /usr/local/include; do 271 [ -f "$d/json-c/json.h" ] && JSON_C_CFLAGS="-I$d" && break 272 done 273 fi 274 if [ -z "$JSON_C_CFLAGS" ]; then 275 echo "[!] json-c headers not found. Run: sudo apt-get install libjson-c-dev" 276 exit 1 277 fi 278 279 # ─── run setup stages ──────────────────────────────────────────────────────── 280 281 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 282 echo "[1/7] Installing dependencies" 283 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 284 install_deps 285 286 echo 287 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 288 echo "[2/7] Setting up ttyd source + instrumented objects" 289 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 290 setup_ttyd_source 291 292 # ─── build harnesses ───────────────────────────────────────────────────────── 293 294 echo 295 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 296 echo "[3/7] Building harnesses (with real ttyd code)" 297 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 298 299 # Common flags for all harnesses: our mock includes take priority over system 300 COMMON_CFLAGS="$CFLAGS -I$FUZZ_INCLUDE -I$TTYD_SRC/src $JSON_C_CFLAGS" 301 COMMON_CFLAGS="$COMMON_CFLAGS -Wno-implicit-function-declaration -Wno-int-conversion" 302 303 # http.c and protocol.c are compiled into each harness via #include — do NOT 304 # include their pre-compiled objects here or every symbol will be defined twice. 305 MOCK_OBJS="$TTYD_OBJ/utils.o" 306 MOCK_OBJS="$MOCK_OBJS $TTYD_OBJ/fuzz_lws_mock.o $TTYD_OBJ/mock_server_globals.o $TTYD_OBJ/mock_pty_stubs.o" 307 308 # http.c decompresses gzip-encoded HTML using zlib 309 EXTRA_LIBS="-lz" 310 311 echo "[*] Building fuzz_websocket_auth..." 312 $CC $COMMON_CFLAGS fuzz_websocket_auth.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS -o fuzz_websocket_auth$SUFFIX 313 echo "[+] Built: fuzz_websocket_auth$SUFFIX" 314 315 echo "[*] Building fuzz_http_parsing..." 316 $CC $COMMON_CFLAGS fuzz_http_parsing.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS -o fuzz_http_parsing$SUFFIX 317 echo "[+] Built: fuzz_http_parsing$SUFFIX" 318 319 echo "[*] Building fuzz_ws_fragments..." 320 $CC $COMMON_CFLAGS fuzz_ws_fragments.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS -o fuzz_ws_fragments$SUFFIX 321 echo "[+] Built: fuzz_ws_fragments$SUFFIX" 322 323 echo "[*] Building fuzz_prefs_message..." 324 $CC $COMMON_CFLAGS fuzz_prefs_message.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS -o fuzz_prefs_message$SUFFIX 325 echo "[+] Built: fuzz_prefs_message$SUFFIX" 326 327 echo "[*] Building fuzz_json_recv..." 328 $CC $COMMON_CFLAGS fuzz_json_recv.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS -o fuzz_json_recv$SUFFIX 329 echo "[+] Built: fuzz_json_recv$SUFFIX" 330 331 # CmpLog variants (AFL++ only) 332 if [ "$MODE" = "afl" ]; then 333 echo 334 echo "[*] Building CmpLog variants..." 335 AFL_LLVM_CMPLOG=1 $CC $COMMON_CFLAGS fuzz_websocket_auth.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS \ 336 -o fuzz_websocket_auth_cmplog 337 echo "[+] Built: fuzz_websocket_auth_cmplog" 338 AFL_LLVM_CMPLOG=1 $CC $COMMON_CFLAGS fuzz_http_parsing.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS \ 339 -o fuzz_http_parsing_cmplog 340 echo "[+] Built: fuzz_http_parsing_cmplog" 341 AFL_LLVM_CMPLOG=1 $CC $COMMON_CFLAGS fuzz_ws_fragments.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS \ 342 -o fuzz_ws_fragments_cmplog 343 echo "[+] Built: fuzz_ws_fragments_cmplog" 344 AFL_LLVM_CMPLOG=1 $CC $COMMON_CFLAGS fuzz_prefs_message.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS \ 345 -o fuzz_prefs_message_cmplog 346 echo "[+] Built: fuzz_prefs_message_cmplog" 347 AFL_LLVM_CMPLOG=1 $CC $COMMON_CFLAGS fuzz_json_recv.c $MOCK_OBJS $JSON_C_LIBS $EXTRA_LIBS \ 348 -o fuzz_json_recv_cmplog 349 echo "[+] Built: fuzz_json_recv_cmplog" 350 351 echo 352 echo "[*] Building HTTP custom mutator..." 353 cc -shared -fPIC -O2 http_mutator.c -o http_mutator.so 354 echo "[+] Built: http_mutator.so" 355 fi 356 357 echo 358 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 359 echo "[4/7] Setting up dictionaries" 360 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 361 setup_dictionaries 362 363 echo 364 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 365 echo "[5/7] Generating seed corpus" 366 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 367 generate_corpus 368 369 echo 370 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 371 echo "[6/7] Installing crash-backup crontab" 372 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 373 setup_crash_backup 374 375 echo 376 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 377 echo "[7/7] Done" 378 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" 379 echo 380 echo "Ready to fuzz:" 381 if [ "$MODE" = "afl" ]; then 382 echo " ./run-fuzzers.sh # launch all targets in tmux" 383 echo " afl-fuzz -i corpus/websocket_auth -o output/websocket_auth -x dictionaries/json.dict ./fuzz_websocket_auth" 384 else 385 echo " ./fuzz_auth_header_lf corpus/auth_header/" 386 fi 387 echo 388 echo "Crashes backed up automatically every 15 min → crashes-backup/" 389 echo "To stop backup: ./clean.sh"