MCP HubMCP Hub
SKILL·72FD32

libafl

trailofbits
업데이트됨 18 days ago
30 조회
7,515
642
7,515
GitHub에서 보기
메타design

정보

이 Claude Skill은 개발자들이 모듈형 Rust 라이브러리인 LibAFL을 사용하여 맞춤형 퍼저를 구축할 수 있도록 돕습니다. libFuzzer나 AFL++ 같은 도구의 범위를 넘어선 제어가 필요한 비표준 대상이나 고급 연구를 위해 특정 옵저버, 피드백, 뮤테이터를 구성해야 할 때 사용하도록 설계되었습니다. 맞춤형 퍼징 로직을 구현하거나 독특한 아키텍처를 퍼징하는 데 활용하세요.

빠른 설치

Claude Code

추천
기본
npx skills add trailofbits/skills -a claude-code
플러그인 명령대체
/plugin add https://github.com/trailofbits/skills
Git 클론대체
git clone https://github.com/trailofbits/skills.git ~/.claude/skills/libafl

Claude Code에서 이 명령을 복사하여 붙여넣어 스킬을 설치하세요

문서

LibAFL

LibAFL is a modular fuzzing library that implements features from AFL-based fuzzers like AFL++. Unlike traditional fuzzers, LibAFL provides all functionality in a modular and customizable way as a Rust library. It can be used as a drop-in replacement for libFuzzer or as a library to build custom fuzzers from scratch.

When to Use

FuzzerBest ForComplexity
libFuzzerQuick setup, single-threadedLow
AFL++Multi-core, general purposeMedium
LibAFLCustom fuzzers, advanced features, researchHigh

Choose LibAFL when:

  • You need custom mutation strategies or feedback mechanisms
  • Standard fuzzers don't support your target architecture
  • You want to implement novel fuzzing techniques
  • You need fine-grained control over fuzzing components
  • You're conducting fuzzing research

Quick Start

LibAFL can be used as a drop-in replacement for libFuzzer with minimal setup:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Call your code with fuzzer-provided data
    my_function(data, size);
    return 0;
}

Build LibAFL's libFuzzer compatibility layer:

git clone https://github.com/AFLplusplus/LibAFL
cd LibAFL/libafl_libfuzzer_runtime
./build.sh

Compile and run:

clang++ -DNO_MAIN -g -O2 -fsanitize=fuzzer-no-link libFuzzer.a harness.cc main.cc -o fuzz
./fuzz corpus/

Installation

Prerequisites

  • Clang/LLVM 15-18
  • Rust (via rustup)
  • Additional system dependencies

Linux/macOS

Install Clang:

apt install clang

Or install a specific version via apt.llvm.org:

wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 15

Configure environment for Rust:

export RUSTFLAGS="-C linker=/usr/bin/clang-15"
export CC="clang-15"
export CXX="clang++-15"

Install Rust:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Install additional dependencies:

apt install libssl-dev pkg-config

For libFuzzer compatibility mode, install nightly Rust:

rustup toolchain install nightly --component llvm-tools

Verification

Build LibAFL to verify installation:

cd LibAFL/libafl_libfuzzer_runtime
./build.sh
# Should produce libFuzzer.a

Writing a Harness

LibAFL harnesses follow the same pattern as libFuzzer when using drop-in replacement mode:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Your fuzzing target code here
    return 0;
}

When building custom fuzzers with LibAFL as a Rust library, harness logic is integrated directly into the fuzzer. See the "Writing a Custom Fuzzer" section below for the full pattern.

See Also: For detailed harness writing techniques, see the harness-writing technique skill.

Usage Modes

LibAFL supports two primary usage modes:

1. libFuzzer Drop-in Replacement

Use LibAFL as a replacement for libFuzzer with existing harnesses.

Compilation:

clang++ -DNO_MAIN -g -O2 -fsanitize=fuzzer-no-link libFuzzer.a harness.cc main.cc -o fuzz

Running:

./fuzz corpus/

Recommended for long campaigns:

./fuzz -fork=1 -ignore_crashes=1 corpus/

2. Custom Fuzzer as Rust Library

Build a fully customized fuzzer using LibAFL components.

Create project:

cargo init --lib my_fuzzer
cd my_fuzzer
cargo add libafl@0.13 libafl_targets@0.13 libafl_bolts@0.13 libafl_cc@0.13 \
  --features "libafl_targets@0.13/libfuzzer,libafl_targets@0.13/sancov_pcguard_hitcounts"

Configure Cargo.toml:

[lib]
crate-type = ["staticlib"]

Writing a Custom Fuzzer

See Also: For detailed harness writing techniques, patterns for handling complex inputs, and advanced strategies, see the fuzz-harness-writing technique skill.

Fuzzer Components

A LibAFL fuzzer consists of modular components:

  1. Observers - Collect execution feedback (coverage, timing)
  2. Feedback - Determine if inputs are interesting
  3. Objective - Define fuzzing goals (crashes, timeouts)
  4. State - Maintain corpus and metadata
  5. Mutators - Generate new inputs
  6. Scheduler - Select which inputs to mutate
  7. Executor - Run the target with inputs

Basic Fuzzer Structure

use libafl::prelude::*;
use libafl_bolts::prelude::*;
use libafl_targets::{libfuzzer_test_one_input, std_edges_map_observer};

#[no_mangle]
pub extern "C" fn libafl_main() {
    let mut run_client = |state: Option<_>, mut restarting_mgr, _core_id| {
        // 1. Setup observers
        let edges_observer = HitcountsMapObserver::new(
            unsafe { std_edges_map_observer("edges") }
        ).track_indices();
        let time_observer = TimeObserver::new("time");

        // 2. Define feedback
        let mut feedback = feedback_or!(
            MaxMapFeedback::new(&edges_observer),
            TimeFeedback::new(&time_observer)
        );

        // 3. Define objective
        let mut objective = feedback_or_fast!(
            CrashFeedback::new(),
            TimeoutFeedback::new()
        );

        // 4. Create or restore state
        let mut state = state.unwrap_or_else(|| {
            StdState::new(
                StdRand::new(),
                InMemoryCorpus::new(),
                OnDiskCorpus::new(&output_dir).unwrap(),
                &mut feedback,
                &mut objective,
            ).unwrap()
        });

        // 5. Setup mutator
        let mutator = StdScheduledMutator::new(havoc_mutations());
        let mut stages = tuple_list!(StdMutationalStage::new(mutator));

        // 6. Setup scheduler
        let scheduler = IndexesLenTimeMinimizerScheduler::new(
            &edges_observer,
            QueueScheduler::new()
        );

        // 7. Create fuzzer
        let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);

        // 8. Define harness
        let mut harness = |input: &BytesInput| {
            let buf = input.target_bytes().as_slice();
            libfuzzer_test_one_input(buf);
            ExitKind::Ok
        };

        // 9. Setup executor
        let mut executor = InProcessExecutor::with_timeout(
            &mut harness,
            tuple_list!(edges_observer, time_observer),
            &mut fuzzer,
            &mut state,
            &mut restarting_mgr,
            timeout,
        )?;

        // 10. Load initial inputs
        if state.must_load_initial_inputs() {
            state.load_initial_inputs(
                &mut fuzzer,
                &mut executor,
                &mut restarting_mgr,
                &input_dir
            )?;
        }

        // 11. Start fuzzing
        fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;
        Ok(())
    };

    // Launch fuzzer
    Launcher::builder()
        .run_client(&mut run_client)
        .cores(&cores)
        .build()
        .launch()
        .unwrap();
}

Compilation

Verbose Mode

Manually specify all instrumentation flags:

clang++-15 -DNO_MAIN -g -O2 \
  -fsanitize-coverage=trace-pc-guard \
  -fsanitize=address \
  -Wl,--whole-archive target/release/libmy_fuzzer.a -Wl,--no-whole-archive \
  main.cc harness.cc -o fuzz

Compiler Wrapper (Recommended)

Create a LibAFL compiler wrapper to handle instrumentation automatically.

Create src/bin/libafl_cc.rs:

use libafl_cc::{ClangWrapper, CompilerWrapper, Configuration, ToolWrapper};

pub fn main() {
    let args: Vec<String> = env::args().collect();
    let mut cc = ClangWrapper::new();
    cc.cpp(is_cpp)
      .parse_args(&args)
      .link_staticlib(&dir, "my_fuzzer")
      .add_args(&Configuration::GenerateCoverageMap.to_flags().unwrap())
      .add_args(&Configuration::AddressSanitizer.to_flags().unwrap())
      .run()
      .unwrap();
}

Compile and use:

cargo build --release
target/release/libafl_cxx -DNO_MAIN -g -O2 main.cc harness.cc -o fuzz

See Also: For detailed sanitizer configuration, common issues, and advanced flags, see the address-sanitizer and undefined-behavior-sanitizer technique skills.

Running Campaigns

Basic Run

./fuzz --cores 0 --input corpus/

Multi-Core Fuzzing

./fuzz --cores 0,8-15 --input corpus/

This runs 9 clients: one on core 0, and 8 on cores 8-15.

With Options

./fuzz --cores 0-7 --input corpus/ --output crashes/ --timeout 1000

Text User Interface (TUI)

Enable graphical statistics view:

./fuzz -tui=1 corpus/

Interpreting Output

OutputMeaning
corpus: NNumber of interesting test cases found
objectives: NNumber of crashes/timeouts found
executions: NTotal number of target invocations
exec/sec: NCurrent execution throughput
edges: X%Code coverage percentage
clients: NNumber of parallel fuzzing processes

The fuzzer emits two main event types:

  • UserStats - Regular heartbeat with current statistics
  • Testcase - New interesting input discovered

Advanced Usage

Tips and Tricks

TipWhy It Helps
Use -fork=1 -ignore_crashes=1Continue fuzzing after first crash
Use InMemoryOnDiskCorpusPersist corpus across restarts
Enable TUI with -tui=1Better visualization of progress
Use specific LLVM versionAvoid compatibility issues
Set RUSTFLAGS correctlyPrevent linking errors

Crash Deduplication

Avoid storing duplicate crashes from the same bug:

Add backtrace observer:

let backtrace_observer = BacktraceObserver::owned(
    "BacktraceObserver",
    libafl::observers::HarnessType::InProcess
);

Update executor:

let mut executor = InProcessExecutor::with_timeout(
    &mut harness,
    tuple_list!(edges_observer, time_observer, backtrace_observer),
    &mut fuzzer,
    &mut state,
    &mut restarting_mgr,
    timeout,
)?;

Update objective with hash feedback:

let mut objective = feedback_and!(
    feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()),
    NewHashFeedback::new(&backtrace_observer)
);

This ensures only crashes with unique backtraces are saved.

Dictionary Fuzzing

Use dictionaries to guide fuzzing toward specific tokens:

Add tokens from file:

let mut tokens = Tokens::new();
if let Some(tokenfile) = &tokenfile {
    tokens.add_from_file(tokenfile)?;
}
state.add_metadata(tokens);

Update mutator:

let mutator = StdScheduledMutator::new(
    havoc_mutations().merge(tokens_mutations())
);

Hard-coded tokens example (PNG):

state.add_metadata(Tokens::from([
    vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header
    "IHDR".as_bytes().to_vec(),
    "IDAT".as_bytes().to_vec(),
    "PLTE".as_bytes().to_vec(),
    "IEND".as_bytes().to_vec(),
]));

See Also: For detailed dictionary creation strategies and format-specific dictionaries, see the fuzzing-dictionaries technique skill.

Auto Tokens

Automatically extract magic values and checksums from the program:

Enable in compiler wrapper:

cc.add_pass(LLVMPasses::AutoTokens)

Load auto tokens in fuzzer:

tokens += libafl_targets::autotokens()?;

Verify tokens section:

echo "p (uint8_t *)__token_start" | gdb fuzz

Performance Tuning

SettingImpact
Multi-core fuzzingLinear speedup with cores
InMemoryCorpusFaster but non-persistent
InMemoryOnDiskCorpusBalanced speed and persistence
Sanitizers2-5x slowdown, essential for bugs
Optimization level -O2Balance between speed and coverage

Debugging Fuzzer

Run fuzzer in single-process mode for easier debugging:

// Replace launcher with direct call
run_client(None, SimpleEventManager::new(monitor), 0).unwrap();

// Comment out:
// Launcher::builder()
//     .run_client(&mut run_client)
//     ...
//     .launch()

Then debug with GDB:

gdb --args ./fuzz --cores 0 --input corpus/

Real-World Examples

Example: libpng

Fuzzing libpng using LibAFL:

1. Get source code:

curl -L -O https://downloads.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar xf libpng-1.6.37.tar.xz
cd libpng-1.6.37/
apt install zlib1g-dev

2. Set compiler wrapper:

export FUZZER_CARGO_DIR="/path/to/libafl/project"
export CC=$FUZZER_CARGO_DIR/target/release/libafl_cc
export CXX=$FUZZER_CARGO_DIR/target/release/libafl_cxx

3. Build static library:

./configure --enable-shared=no
make

4. Get harness:

curl -O https://raw.githubusercontent.com/glennrp/libpng/f8e5fa92b0e37ab597616f554bee254157998227/contrib/oss-fuzz/libpng_read_fuzzer.cc

5. Link fuzzer:

$CXX libpng_read_fuzzer.cc .libs/libpng16.a -lz -o fuzz

6. Prepare seeds:

mkdir seeds/
curl -o seeds/input.png https://raw.githubusercontent.com/glennrp/libpng/acfd50ae0ba3198ad734e5d4dec2b05341e50924/contrib/pngsuite/iftp1n3p08.png

7. Get dictionary (optional):

curl -O https://raw.githubusercontent.com/glennrp/libpng/2fff013a6935967960a5ae626fc21432807933dd/contrib/oss-fuzz/png.dict

8. Start fuzzing:

./fuzz --input seeds/ --cores 0 -x png.dict

Example: CMake Project

Integrate LibAFL with CMake build system:

CMakeLists.txt:

project(BuggyProgram)
cmake_minimum_required(VERSION 3.0)

add_executable(buggy_program main.cc)

add_executable(fuzz main.cc harness.cc)
target_compile_definitions(fuzz PRIVATE NO_MAIN=1)
target_compile_options(fuzz PRIVATE -g -O2)

Build non-instrumented binary:

cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .
cmake --build . --target buggy_program

Build fuzzer:

export FUZZER_CARGO_DIR="/path/to/libafl/project"
cmake -DCMAKE_C_COMPILER=$FUZZER_CARGO_DIR/target/release/libafl_cc \
      -DCMAKE_CXX_COMPILER=$FUZZER_CARGO_DIR/target/release/libafl_cxx .
cmake --build . --target fuzz

Run fuzzing:

./fuzz --input seeds/ --cores 0

Troubleshooting

ProblemCauseSolution
No coverage increasesInstrumentation failedVerify compiler wrapper used, check for -fsanitize-coverage
Fuzzer won't startEmpty corpus with no interesting inputsProvide seed inputs that trigger code paths
Linker errors with libafl_mainRuntime not linkedUse -Wl,--whole-archive or -u libafl_main
LLVM version mismatchLibAFL requires LLVM 15-18Install compatible LLVM version, set environment variables
Rust compilation failsOutdated Rust or CargoUpdate Rust with rustup update
Slow fuzzingSanitizers enabledExpected 2-5x slowdown, necessary for finding bugs
Environment variable interferenceCC, CXX, RUSTFLAGS setUnset after building LibAFL project
Cannot attach debuggerMulti-process fuzzingRun in single-process mode (see Debugging section)

Related Skills

Technique Skills

SkillUse Case
fuzz-harness-writingDetailed guidance on writing effective harnesses
address-sanitizerMemory error detection during fuzzing
undefined-behavior-sanitizerUndefined behavior detection
coverage-analysisMeasuring and improving code coverage
fuzzing-corpusBuilding and managing seed corpora
fuzzing-dictionariesCreating dictionaries for format-aware fuzzing

Related Fuzzers

SkillWhen to Consider
libfuzzerSimpler setup, don't need LibAFL's advanced features
aflppMulti-core fuzzing without custom fuzzer development
cargo-fuzzFuzzing Rust projects with less setup

Resources

Official Documentation

Examples and Tutorials

GitHub 저장소

trailofbits/skills
경로: plugins/testing-handbook-skills/skills/libafl
0
agent-skills
FAQ

자주 묻는 질문

libafl Skill이란 무엇인가요?

libafl은(는) trailofbits이(가) 만든 Claude Skill입니다. Skill은 Claude가 필요할 때 불러오는 지침과 리소스를 묶어 추가 프롬프트 없이 libafl 관련 작업을 수행할 수 있게 합니다.

libafl은(는) 어떻게 설치하나요?

이 페이지의 설치 명령을 사용하세요. libafl을(를) Claude Code 플러그인으로 추가하거나 저장소를 skills 디렉터리에 복제한 다음 Claude를 다시 시작해 Skill을 불러옵니다.

libafl은(는) 어떤 카테고리에 속하나요?

libafl은(는) 메타 카테고리에 속합니다.

libafl은(는) 무료로 사용할 수 있나요?

네. libafl은(는) AIMCP에 등록되어 있으며 무료로 설치할 수 있습니다.

연관 스킬

content-collections
메타

이 스킬은 콘텐츠 콜렉션(Content Collections)을 위한 프로덕션 검증된 설정을 제공합니다. 콘텐츠 콜렉션은 Markdown/MDX 파일을 Zod 검증이 포함된 타입 안전한 데이터 콜렉션으로 변환해주는 TypeScript 최우선 도구입니다. 블로그, 문서 사이트 또는 콘텐츠 중심의 Vite + React 애플리케이션을 구축할 때 타입 안전성과 자동 콘텐츠 검증을 보장하기 위해 사용하세요. Vite 플러그인 구성과 MDX 컴파일부터 배포 최적화 및 스키마 검증에 이르기까지 모든 것을 다룹니다.

스킬 보기
polymarket
메타

이 스킬은 개발자들이 Polymarket 예측 시장 플랫폼을 활용한 애플리케이션을 구축할 수 있도록 지원하며, 거래 및 시장 데이터를 위한 API 통합 기능을 포함합니다. 또한 WebSocket을 통한 실시간 데이터 스트리밍을 제공하여 실시간 거래와 시장 활동을 모니터링할 수 있습니다. 이를 통해 거래 전략을 구현하거나 실시간 시장 업데이트를 처리하는 도구를 생성하는 데 활용할 수 있습니다.

스킬 보기
creating-opencode-plugins
메타

이 스킬은 개발자들이 명령어, 파일, LSP 작업 등 25개 이상의 이벤트 유형에 연결되는 OpenCode 플러그인을 만들 수 있도록 돕습니다. JavaScript/TypeScript 모듈을 위한 플러그인 구조, 이벤트 API 명세, 구현 패턴을 제공합니다. OpenCode AI 어시스턴트의 라이프사이클을 사용자 정의 이벤트 기반 로직으로 가로채거나, 모니터링하거나, 확장해야 할 때 사용하세요.

스킬 보기
sglang
메타

SGLang은 RadixAttention 프리픽스 캐싱을 활용하여 JSON, 정규식, 에이전트 워크플로우를 위한 고속 구조화 생성에 특화된 고성능 LLM 서빙 프레임워크입니다. 특히 반복되는 프리픽스가 있는 작업에서 상당히 빠른 추론 속도를 제공하여 복잡한 구조화 출력 및 다중 턴 대화에 이상적입니다. 제약 디코딩이 필요하거나 광범위한 프리픽스 공유가 있는 애플리케이션을 구축할 때는 vLLM과 같은 대안보다 SGLang을 선택하십시오.

스킬 보기