diff --git a/src/AI/AI-Assisted-Fuzzing-and-Vulnerability-Discovery.md b/src/AI/AI-Assisted-Fuzzing-and-Vulnerability-Discovery.md index 50628757bda..2651619b57e 100644 --- a/src/AI/AI-Assisted-Fuzzing-and-Vulnerability-Discovery.md +++ b/src/AI/AI-Assisted-Fuzzing-and-Vulnerability-Discovery.md @@ -79,7 +79,121 @@ Key points: --- -## 3. Agent-Based PoV (Exploit) Generation +## 3. Local-LLM libFuzzer Harness Engineering + +For source that cannot leave an analyst workstation, a local code model can draft the small adapter between fuzzer bytes and a C/C++ API. The useful security boundary is explicit: **the model writes and revises test infrastructure; coverage-guided mutation explores inputs; sanitizers report invalid operations; a human reviews the harness and triages findings**.[[9]](#references) + +For engine-level corpus, feedback, snapshot, and stateful-fuzzing techniques that do not depend on an LLM, see the general [Fuzzing Methodology](../generic-methodologies-and-resources/fuzzing.md). + +Start with the exact declaration copied from the target header and constrain the output format. A narrow prompt reduces invented APIs and makes the result easy to review:[[8]](#references)[[9]](#references) + +```prompt +Write a libFuzzer target for this exact declaration: +int parse_records(const uint8_t *data, size_t size); + +Output only C code. Implement LLVMFuzzerTestOneInput, include the +declaration, pass data and size unchanged, and do not use files, +network access, randomness, clocks, threads, or persistent state. +``` + +For a raw byte parser, the expected result should be no more complicated than:[[6]](#references)[[9]](#references) + +```c +#include +#include + +extern int parse_records(const uint8_t *, size_t); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + (void)parse_records(data, size); + return 0; +} +``` + +Compilation is only the first validation gate. Review every generated target against these invariants before spending CPU time on it:[[6]](#references)[[8]](#references)[[9]](#references) + +- **Exact API:** compare types, ownership rules, initialization, teardown, and calling convention with the real header and existing callers. +- **Harness safety:** guard every read from `data`; sanitizer findings must originate in the target, not generated glue. +- **Repeatability:** make the result a function of `data` and `size`; remove external I/O, time, uncontrolled randomness, unjoined threads, and state that survives between calls. +- **Throughput:** initialize expensive immutable state once only when this cannot affect behavior, bound input size, suppress logging, and free per-input allocations. +- **Reachability:** verify that the intended functions and branches execute. A target that compiles but only exercises rejection paths is a silent failure. + +### Structure-aware targets: preserve invariants without hiding bugs + +If most raw inputs fail a magic, length, version, or checksum gate, use the fuzz bytes as **typed field material** and rebuild a valid object. Fix format constants, leave semantic fields and payload bytes mutable, and derive integrity metadata from the reconstructed payload. `FuzzedDataProvider` is useful for consuming bounded typed values, while the adapter remains responsible for limiting any generated allocation and conversion.[[9]](#references) + +
+Example structure-aware C++ target + +```cpp +#include +#include +#include +#include + +extern "C" int parse_message(const uint8_t *, size_t); + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + uint8_t version = fdp.ConsumeIntegral(); + auto body = fdp.ConsumeRemainingBytes(); + if (body.size() > UINT16_MAX) body.resize(UINT16_MAX); + uint16_t len = static_cast(body.size()); + std::vector msg = {0x8b, 'M', 'S', 'G', version, + static_cast(len >> 8), + static_cast(len)}; + msg.insert(msg.end(), body.begin(), body.end()); + (void)parse_message(msg.data(), msg.size()); + return 0; +} +``` + +
+ +Do not use one structure-repairing target for every question. Maintain one target that derives lengths/checksums to explore deep valid states, and another that keeps the **declared** metadata fuzzer-controlled while bounding only the harness' own reads and allocations. The second target preserves mismatched-length and bad-integrity attack surfaces that automatic repair would otherwise erase.[[9]](#references) + +### Seeds, dictionaries, sanitizers, and coverage feedback + +Ask the model to generate a script that writes binary seeds rather than asking it to print binary directly. Seed empty, minimal, typical, and boundary-valid objects; then extract magic values, tags, enum encodings, and parser keywords into a reviewed dictionary. libFuzzer accepts that dictionary with `-dict=`, and keeps mutations that expose new coverage in the corpus.[[6]](#references)[[9]](#references) + +```text +magic="\x8bMSG" +version_min="\x00" +version_max="\xff" +config_tag="CONFIG" +``` + +Build the same target with complementary instrumentation. ASan and UBSan can share a fuzzing binary; use a separate MSan build and instrument its dependencies to avoid reports caused by uninstrumented code.[[6]](#references)[[9]](#references) + +```bash +clang++ -g -O1 -fsanitize=fuzzer,address,undefined \ + -fno-sanitize-recover=undefined target.cc harness.cc -o fuzz_target +./fuzz_target corpus -dict=msg.dict -max_len=65535 + +clang++ -g -O1 -fsanitize=fuzzer,memory \ + target.cc harness.cc -o fuzz_target_msan +``` + +Treat coverage as the evaluator for each model revision. In libFuzzer output, early-flat `cov`/`ft` and a static corpus usually mean bad framing, an overly restrictive early return, or the wrong call; low `exec/s` points to expensive setup, I/O, allocation, or leaked state. `NEW` only proves that a coverage feature changed, not that the intended parser was reached.[[6]](#references)[[9]](#references) + +For source-level feedback, make a coverage build, fuzz it, and give the model only the uncovered functions plus their guarding conditions. Verify the resulting harness/seed/dictionary diff and measure again:[[7]](#references)[[8]](#references)[[9]](#references) + +```bash +clang++ -g -O1 -fsanitize=fuzzer \ + -fprofile-instr-generate -fcoverage-mapping target.cc harness.cc -o fuzz_cov +mkdir -p profiles +LLVM_PROFILE_FILE='profiles/%p.profraw' ./fuzz_cov -max_total_time=300 corpus +llvm-profdata merge -sparse profiles/*.profraw -o fuzz.profdata +llvm-cov report ./fuzz_cov -instr-profile=fuzz.profdata +llvm-cov show ./fuzz_cov -instr-profile=fuzz.profdata \ + --show-branches=count --region-coverage-lt=1 target.cc +``` + +The scalable loop is therefore **generate → compile/link → smoke-test under sanitizers → measure target coverage → provide the smallest relevant coverage/source context → revise → re-measure**. OSS-Fuzz-Gen applies the same principle by evaluating generated targets for compilability, runtime failures, coverage, and coverage improvement over existing targets instead of accepting plausible code at face value.[[7]](#references)[[8]](#references) + +--- + +## 4. Agent-Based PoV (Exploit) Generation After a crash is found you still need a **proof-of-vulnerability (PoV)** that deterministically triggers it. @@ -100,7 +214,7 @@ Advantages: --- -## 4. Directed Fuzzing with Fine-Tuned Code Models +## 5. Directed Fuzzing with Fine-Tuned Code Models Fine-tune an open-weight model (e.g. Llama-7B) on C/C++ source labelled with vulnerability patterns (integer overflow, buffer copy, format string). Then: @@ -118,9 +232,9 @@ Empirically this shrinks time-to-crash by >2× on real targets. --- -## 5. AI-Guided Patching Strategies +## 6. AI-Guided Patching Strategies -### 5.1 Super Patches +### 6.1 Super Patches Ask the model to *cluster* crash signatures and propose a **single patch** that removes the common root cause. Submit once, fix several bugs → fewer accuracy penalties in environments where each wrong patch costs points. Prompt outline: @@ -128,12 +242,12 @@ Prompt outline: Here are 10 stack traces + file snippets. Identify the shared mistake and generate a unified diff fixing all occurrences. ``` -### 5.2 Speculative Patch Ratio +### 6.2 Speculative Patch Ratio Implement a queue where confirmed PoV-validated patches and *speculative* patches (no PoV) are interleaved at a 1:​N ratio tuned to scoring rules (e.g. 2 speculative : 1 confirmed). A cost model monitors penalties vs. points and self-adjusts N. --- -## 6. Deterministic File-by-File AI Code Review +## 7. Deterministic File-by-File AI Code Review A frequent failure mode in AI-assisted review is asking one agent to inspect a whole repository and hoping it chooses the right files and grep terms. A more reliable pattern is to **force repository coverage**:[[1]](#references) @@ -189,5 +303,9 @@ graph TD - [3] [GitHub Copilot community security-review skill](https://github.com/github/awesome-copilot/blob/main/skills/security-review/SKILL.md) - [4] [Trail of Bits – AIxCC finals: Tale of the tape](https://blog.trailofbits.com/2025/08/07/aixcc-finals-tale-of-the-tape/) - [5] [CTF Radiooo AIxCC finalist interviews](https://www.youtube.com/@ctfradiooo) +- [6] [LLVM — libFuzzer: a library for coverage-guided fuzz testing](https://llvm.org/docs/LibFuzzer.html) +- [7] [Google — OSS-Fuzz-Gen](https://github.com/google/oss-fuzz-gen) +- [8] [Google Online Security Blog — Leveling Up Fuzzing: Finding More Vulnerabilities with AI](https://security.googleblog.com/2024/11/leveling-up-fuzzing-finding-more.html) +- [9] [8kSec — AI-Assisted Fuzzing Harness Generation with a Local LLM](https://8ksec.io/ai-assisted-fuzzing-harness-local-llm) {{#include ../banners/hacktricks-training.md}}