mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-02 09:07:41 +03:00
Add 5 new cybersecurity skills: AFL++ fuzzing, Sysmon scheduled task detection, ModSecurity WAF logging, MITRE Navigator TTP analysis, PowerShell script block hunting
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Mahipal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: performing-fuzzing-with-aflplusplus
|
||||
description: >
|
||||
Perform coverage-guided fuzzing of compiled binaries using AFL++ (American Fuzzy Lop Plus Plus)
|
||||
to discover memory corruption, crashes, and security vulnerabilities. The tester instruments
|
||||
target binaries with afl-cc/afl-clang-fast, manages input corpora with afl-cmin and afl-tmin,
|
||||
runs parallel fuzzing campaigns with afl-fuzz, and triages crashes using CASR or GDB scripts.
|
||||
Activates for requests involving binary fuzzing, crash discovery, coverage-guided testing, or
|
||||
AFL++ fuzzing campaigns.
|
||||
domain: cybersecurity
|
||||
subdomain: application-security
|
||||
tags: [fuzzing, aflplusplus, coverage-guided, crash-triage, binary-analysis, security-testing]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: Apache-2.0
|
||||
---
|
||||
# Performing Fuzzing with AFL++
|
||||
|
||||
## Overview
|
||||
|
||||
AFL++ is a community-maintained fork of American Fuzzy Lop (AFL) that provides coverage-guided
|
||||
fuzzing for compiled binaries. It instruments targets at compile time or via QEMU/Unicorn mode
|
||||
for binary-only fuzzing, then mutates input corpora to discover new code paths. AFL++ includes
|
||||
advanced scheduling (MOpt, rare), custom mutators, CMPLOG for input-to-state comparison solving,
|
||||
and persistent mode for high-throughput fuzzing.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- AFL++ installed (`apt install afl++` or build from source)
|
||||
- Target binary source code (for compile-time instrumentation) or QEMU mode for binary-only
|
||||
- Initial seed corpus of valid inputs for the target format
|
||||
- Linux system with /proc/sys/kernel/core_pattern configured
|
||||
|
||||
## Steps
|
||||
|
||||
1. Instrument the target binary with `afl-cc` or `afl-clang-fast`
|
||||
2. Prepare seed corpus directory with minimal valid inputs
|
||||
3. Minimize corpus with `afl-cmin` to remove redundant seeds
|
||||
4. Run `afl-fuzz` with appropriate flags (-i input -o output)
|
||||
5. Monitor fuzzing progress via afl-whatsup and UI stats
|
||||
6. Triage crashes with `afl-tmin` minimization and CASR/GDB analysis
|
||||
7. Report unique crashes with reproduction steps
|
||||
|
||||
## Expected Output
|
||||
|
||||
```
|
||||
+++ Findings +++
|
||||
unique crashes: 12
|
||||
unique hangs: 3
|
||||
last crash: 00:02:15 ago
|
||||
+++ Coverage +++
|
||||
map density: 4.23% / 8.41%
|
||||
paths found: 1847
|
||||
exec speed: 2145/sec
|
||||
```
|
||||
@@ -0,0 +1,64 @@
|
||||
# AFL++ Fuzzing — API Reference
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
apt install afl++ # Ubuntu/Debian
|
||||
# Or build from source:
|
||||
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus && make all
|
||||
```
|
||||
|
||||
## AFL++ CLI Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `afl-cc` / `afl-clang-fast` | Compile-time instrumentation compiler wrapper |
|
||||
| `afl-fuzz` | Main fuzzer — coverage-guided mutation engine |
|
||||
| `afl-cmin` | Corpus minimization — remove redundant seeds |
|
||||
| `afl-tmin` | Test case minimization — shrink individual inputs |
|
||||
| `afl-whatsup` | Multi-instance campaign status summary |
|
||||
| `afl-plot` | Generate fuzzing progress plots |
|
||||
| `afl-showmap` | Display coverage map for a single input |
|
||||
|
||||
## afl-fuzz Key Flags
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-i <dir>` | Input seed corpus directory |
|
||||
| `-o <dir>` | Output directory for findings |
|
||||
| `-m <MB>` | Memory limit (use `none` for ASAN) |
|
||||
| `-t <ms>` | Execution timeout per test case |
|
||||
| `-x <dict>` | Optional fuzzing dictionary |
|
||||
| `-p <sched>` | Power schedule: fast, coe, explore, rare, mmopt |
|
||||
| `-l <level>` | CMPLOG instrumentation level (2=transforms, 3=all) |
|
||||
| `-c <bin>` | CMPLOG binary for input-to-state |
|
||||
| `-M <name>` | Main fuzzer instance (parallel mode) |
|
||||
| `-S <name>` | Secondary fuzzer instance (parallel mode) |
|
||||
| `-Q` | QEMU mode (binary-only fuzzing) |
|
||||
| `-U` | Unicorn mode |
|
||||
|
||||
## fuzzer_stats File Fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `execs_done` | Total executions completed |
|
||||
| `execs_per_sec` | Current execution speed |
|
||||
| `corpus_count` | Total paths in corpus |
|
||||
| `saved_crashes` | Unique crashes discovered |
|
||||
| `saved_hangs` | Unique hangs discovered |
|
||||
| `stability` | Execution stability percentage |
|
||||
| `bitmap_cvg` | Code coverage bitmap density |
|
||||
|
||||
## Crash Triage Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `casr-afl` | CASR crash severity analysis for AFL++ |
|
||||
| `afl-tmin` | Minimize crash inputs |
|
||||
| `gdb --batch -ex run` | Reproduce crash under debugger |
|
||||
|
||||
## External References
|
||||
|
||||
- [AFL++ Documentation](https://aflplus.plus/docs/)
|
||||
- [AFL++ GitHub](https://github.com/AFLplusplus/AFLplusplus)
|
||||
- [CASR Crash Triage](https://github.com/ispras/casr)
|
||||
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
# For authorized testing only
|
||||
"""AFL++ fuzzing campaign management and crash triage agent."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import glob
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def instrument_target(source_path, output_path, compiler="afl-clang-fast",
|
||||
sanitizer=None):
|
||||
"""Compile target with AFL++ instrumentation."""
|
||||
cmd = [compiler, "-o", output_path, source_path]
|
||||
if sanitizer == "asan":
|
||||
cmd.insert(1, "-fsanitize=address")
|
||||
elif sanitizer == "ubsan":
|
||||
cmd.insert(1, "-fsanitize=undefined")
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
return {
|
||||
"compiler": compiler,
|
||||
"source": source_path,
|
||||
"output": output_path,
|
||||
"sanitizer": sanitizer,
|
||||
"success": result.returncode == 0,
|
||||
"stderr": result.stderr[:500] if result.stderr else "",
|
||||
}
|
||||
|
||||
|
||||
def minimize_corpus(afl_cmin_path, target_binary, input_dir, output_dir):
|
||||
"""Minimize seed corpus using afl-cmin."""
|
||||
cmd = [afl_cmin_path or "afl-cmin", "-i", input_dir, "-o", output_dir,
|
||||
"--", target_binary]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)
|
||||
before = len(os.listdir(input_dir)) if os.path.isdir(input_dir) else 0
|
||||
after = len(os.listdir(output_dir)) if os.path.isdir(output_dir) else 0
|
||||
return {
|
||||
"before": before,
|
||||
"after": after,
|
||||
"reduction_pct": round((1 - after / max(before, 1)) * 100, 1),
|
||||
"success": result.returncode == 0,
|
||||
}
|
||||
|
||||
|
||||
def parse_fuzzer_stats(output_dir):
|
||||
"""Parse afl-fuzz fuzzer_stats file for campaign metrics."""
|
||||
stats_path = os.path.join(output_dir, "default", "fuzzer_stats")
|
||||
if not os.path.exists(stats_path):
|
||||
stats_path = os.path.join(output_dir, "fuzzer_stats")
|
||||
if not os.path.exists(stats_path):
|
||||
return {"error": f"fuzzer_stats not found in {output_dir}"}
|
||||
|
||||
stats = {}
|
||||
with open(stats_path, "r") as f:
|
||||
for line in f:
|
||||
if ":" in line:
|
||||
key, val = line.split(":", 1)
|
||||
stats[key.strip()] = val.strip()
|
||||
|
||||
return {
|
||||
"start_time": stats.get("start_time", ""),
|
||||
"last_update": stats.get("last_update", ""),
|
||||
"execs_done": int(stats.get("execs_done", 0)),
|
||||
"execs_per_sec": float(stats.get("execs_per_sec", 0)),
|
||||
"paths_total": int(stats.get("corpus_count", stats.get("paths_total", 0))),
|
||||
"paths_found": int(stats.get("paths_found", 0)),
|
||||
"unique_crashes": int(stats.get("saved_crashes", stats.get("unique_crashes", 0))),
|
||||
"unique_hangs": int(stats.get("saved_hangs", stats.get("unique_hangs", 0))),
|
||||
"stability": stats.get("stability", ""),
|
||||
"bitmap_cvg": stats.get("bitmap_cvg", ""),
|
||||
"command_line": stats.get("command_line", ""),
|
||||
}
|
||||
|
||||
|
||||
def triage_crashes(output_dir):
|
||||
"""Enumerate and classify crash files from AFL++ output."""
|
||||
crash_dirs = [
|
||||
os.path.join(output_dir, "default", "crashes"),
|
||||
os.path.join(output_dir, "crashes"),
|
||||
]
|
||||
crash_dir = None
|
||||
for d in crash_dirs:
|
||||
if os.path.isdir(d):
|
||||
crash_dir = d
|
||||
break
|
||||
if not crash_dir:
|
||||
return {"crashes": [], "total": 0}
|
||||
|
||||
crashes = []
|
||||
for filename in sorted(os.listdir(crash_dir)):
|
||||
if filename.startswith("README") or filename == ".state":
|
||||
continue
|
||||
filepath = os.path.join(crash_dir, filename)
|
||||
size = os.path.getsize(filepath)
|
||||
sig_parts = filename.split(",")
|
||||
signal = ""
|
||||
for part in sig_parts:
|
||||
if part.startswith("sig:"):
|
||||
signal = part.split(":")[1]
|
||||
crashes.append({
|
||||
"filename": filename,
|
||||
"size_bytes": size,
|
||||
"signal": signal,
|
||||
"path": filepath,
|
||||
})
|
||||
return {"crashes": crashes, "total": len(crashes)}
|
||||
|
||||
|
||||
def minimize_crash(afl_tmin_path, target_binary, crash_file, output_file):
|
||||
"""Minimize a crash test case with afl-tmin."""
|
||||
cmd = [afl_tmin_path or "afl-tmin", "-i", crash_file, "-o", output_file,
|
||||
"--", target_binary]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
orig_size = os.path.getsize(crash_file) if os.path.exists(crash_file) else 0
|
||||
min_size = os.path.getsize(output_file) if os.path.exists(output_file) else 0
|
||||
return {
|
||||
"original_size": orig_size,
|
||||
"minimized_size": min_size,
|
||||
"reduction_pct": round((1 - min_size / max(orig_size, 1)) * 100, 1),
|
||||
"success": result.returncode == 0,
|
||||
}
|
||||
|
||||
|
||||
def run_whatsup(output_dir):
|
||||
"""Run afl-whatsup to get multi-instance campaign summary."""
|
||||
cmd = ["afl-whatsup", "-s", output_dir]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||
return {"output": result.stdout[:2000] if result.stdout else result.stderr[:500]}
|
||||
|
||||
|
||||
def run_audit(args):
|
||||
"""Execute AFL++ fuzzing campaign audit."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" AFL++ FUZZING CAMPAIGN AUDIT")
|
||||
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
report = {}
|
||||
|
||||
if args.output_dir:
|
||||
stats = parse_fuzzer_stats(args.output_dir)
|
||||
report["fuzzer_stats"] = stats
|
||||
print(f"--- FUZZER STATS ---")
|
||||
print(f" Executions: {stats.get('execs_done', 0):,}")
|
||||
print(f" Exec/sec: {stats.get('execs_per_sec', 0)}")
|
||||
print(f" Paths: {stats.get('paths_total', 0)}")
|
||||
print(f" Crashes: {stats.get('unique_crashes', 0)}")
|
||||
print(f" Hangs: {stats.get('unique_hangs', 0)}")
|
||||
print(f" Stability: {stats.get('stability', '')}")
|
||||
print(f" Coverage: {stats.get('bitmap_cvg', '')}")
|
||||
|
||||
crash_data = triage_crashes(args.output_dir)
|
||||
report["crash_triage"] = crash_data
|
||||
print(f"\n--- CRASH TRIAGE ({crash_data['total']} crashes) ---")
|
||||
for c in crash_data["crashes"][:20]:
|
||||
print(f" {c['filename']} ({c['size_bytes']}B) signal={c['signal']}")
|
||||
|
||||
if args.instrument_src and args.instrument_out:
|
||||
inst = instrument_target(args.instrument_src, args.instrument_out,
|
||||
sanitizer=args.sanitizer)
|
||||
report["instrumentation"] = inst
|
||||
print(f"\n--- INSTRUMENTATION ---")
|
||||
print(f" {'SUCCESS' if inst['success'] else 'FAILED'}: {inst['source']}")
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="AFL++ Fuzzing Campaign Agent")
|
||||
parser.add_argument("--output-dir", help="AFL++ output directory to analyze")
|
||||
parser.add_argument("--instrument-src", help="Source file to instrument")
|
||||
parser.add_argument("--instrument-out", help="Output path for instrumented binary")
|
||||
parser.add_argument("--sanitizer", choices=["asan", "ubsan"],
|
||||
help="Address or undefined behavior sanitizer")
|
||||
parser.add_argument("--output", help="Save report to JSON file")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = run_audit(args)
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user