콘텐츠로 이동

VulHunt

VulHunt is Binarly’s open-source binary vulnerability detection and analysis framework designed for firmware security, UEFI analysis, and supply chain risk assessment. It uses Lua-based detection rules on top of the BIAS (Binary Analysis and Inspection System) engine to identify vulnerabilities in compiled binaries.

Clone and build VulHunt from the official repository:

# Clone the repository
git clone https://github.com/vulhunt-re/vulhunt.git
cd vulhunt

# View build requirements
cat README.md

# System prerequisites (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y build-essential cmake cargo rustc git

# macOS prerequisites
brew install cmake rust git

# Build with cargo
cargo build --release

# Binary location
./target/release/vulhunt --version

Build Requirements:

  • C++17-compatible compiler (GCC 7+, Clang 6+, MSVC 2015+)
  • Rust toolchain (latest stable via rustup)
  • CMake 3.12+
  • Git

Begin scanning binaries with VulHunt’s command-line interface:

# Scan a POSIX binary
vulhunt scan /path/to/binary

# Scan UEFI firmware module
vulhunt scan --target uefi /path/to/firmware.efi

# Use specific rule set
vulhunt scan --rules custom_rules.lua /path/to/binary

# Generate JSON report
vulhunt scan --output json /path/to/binary > report.json

# Verbose output with debug info
vulhunt scan --verbose /path/to/binary

# List available rules
vulhunt list-rules

# Display rule details
vulhunt rule-info rule_name

VulHunt supports multiple binary formats and architectures for comprehensive coverage:

# POSIX executable (ELF, Mach-O)
vulhunt scan /path/to/binary

# UEFI firmware
vulhunt scan --target uefi firmware.efi

# Firmware image (raw binary)
vulhunt scan --target raw --arch x86-64 firmware.bin

# Library (shared object)
vulhunt scan /usr/lib/libexample.so

# Windows PE binary (experimental)
vulhunt scan --target pe application.exe
# x86 (32-bit Intel)
vulhunt scan --arch x86 binary_32bit

# x86-64 (64-bit Intel)
vulhunt scan --arch x86-64 binary_64bit

# ARM (32-bit)
vulhunt scan --arch arm binary_arm

# AArch64 (ARM 64-bit)
vulhunt scan --arch aarch64 binary_aarch64

# Auto-detection (recommended)
vulhunt scan binary  # Automatically detects architecture

VulHunt’s detection engine is driven by Lua-based rules that define vulnerability patterns:

-- Basic rule structure
rule {
  metadata = {
    id = "VULN_001",
    name = "Buffer Overflow Detection",
    description = "Detects potential buffer overflow vulnerabilities",
    severity = "high",
    cve = "CVE-2024-12345",
    author = "security-team",
    date = "2026-01-15"
  },
  
  filter = {
    -- Filter conditions (optional)
    function_size_min = 100,
    function_size_max = 5000,
    has_loop = true
  },
  
  scope = "function",
  
  check = function(func)
    -- Analysis logic here
    return results
  end
}
-- Project scope: analyze entire binary
scope = "project"
check = function(binary) ... end

-- Function scope: analyze individual functions
scope = "function"
check = function(func) ... end

-- Call site scope: analyze function calls
scope = "call_site"
check = function(call) ... end

-- Basic block scope: analyze control flow blocks
scope = "basic_block"
check = function(block) ... end
metadata = {
  id = "RULE_ID",              -- Unique identifier
  name = "Rule Name",          -- Human-readable name
  description = "...",         -- Detailed description
  severity = "critical",       -- critical|high|medium|low|info
  cve = "CVE-XXXX-XXXXX",      -- Optional CVE reference
  cwe = "CWE-123",             -- Optional CWE reference
  author = "team",             -- Rule author
  date = "2026-01-15",         -- Creation date
  tags = {"buffer", "overflow"}, -- Classification tags
  references = {               -- Optional references
    "https://binarly.io/...",
    "https://nvd.nist.gov/..."
  }
}

Create targeted vulnerability detection rules tailored to your codebase:

rule {
  metadata = {
    id = "CUSTOM_001",
    name = "Insecure Function Usage",
    description = "Detects dangerous function calls",
    severity = "high",
    author = "your-team",
    date = "2026-04-17"
  },
  
  scope = "call_site",
  
  check = function(call_site)
    -- Get function being called
    local func_name = call_site:function_name()
    
    -- Check for dangerous functions
    local dangerous = {
      "strcpy",
      "strcat",
      "sprintf",
      "gets"
    }
    
    for _, name in ipairs(dangerous) do
      if func_name == name then
        return {
          match = true,
          message = "Use of unsafe function: " .. func_name
        }
      end
    end
    
    return { match = false }
  end
}
rule {
  metadata = {
    id = "CUSTOM_002",
    name = "Unchecked Buffer Write",
    description = "Detects potential buffer writes without bounds checking",
    severity = "critical"
  },
  
  filter = {
    min_instructions = 50,
    has_memory_operations = true
  },
  
  scope = "function",
  
  check = function(func)
    local instructions = func:instructions()
    local has_alloc = false
    local has_write = false
    local has_check = false
    
    for _, instr in ipairs(instructions) do
      if instr:contains("malloc") or instr:contains("alloca") then
        has_alloc = true
      end
      if instr:is_memory_write() then
        has_write = true
      end
      if instr:contains("cmp") or instr:contains("test") then
        has_check = true
      end
    end
    
    if has_alloc and has_write and not has_check then
      return { match = true }
    end
    return { match = false }
  end
}
rule {
  metadata = {
    id = "CUSTOM_003",
    name = "Tainted Data Usage",
    description = "Detects untrusted data flowing to sensitive operations"
  },
  
  scope = "function",
  
  check = function(func)
    -- Track data sources
    local sources = func:find_sources({"user_input", "network"})
    
    -- Track sinks (sensitive operations)
    local sinks = func:find_calls_to({
      "memcpy",
      "strcpy",
      "system",
      "exec"
    })
    
    -- Analyze dataflow
    for _, source in ipairs(sources) do
      for _, sink in ipairs(sinks) do
        if func:can_reach(source, sink) then
          return {
            match = true,
            source = source:address(),
            sink = sink:address()
          }
        end
      end
    end
    
    return { match = false }
  end
}

The Binary Analysis and Inspection System (BIAS) is VulHunt’s core analysis engine:

# BIAS intermediate representation generation
vulhunt bias-export binary.elf > binary.ir

# Disassembly with control flow
vulhunt bias-disasm binary.elf --arch x86-64

# Type inference and reconstruction
vulhunt bias-types binary.elf

# Cross-reference database
vulhunt bias-xrefs binary.elf
-- Access IR in rules
rule {
  scope = "function",
  check = function(func)
    -- Get IR instructions
    local ir = func:ir()
    
    -- Iterate through IR ops
    for _, op in ipairs(ir) do
      -- Analyze IR operations
      if op:is_call() then
        local target = op:target()
        -- Analyze call
      elseif op:is_load() then
        local memory = op:address()
        -- Analyze memory load
      elseif op:is_store() then
        local memory = op:address()
        -- Analyze memory store
      end
    end
  end
}
-- Access high-level decompiled code
rule {
  scope = "function",
  check = function(func)
    -- Get pseudo-code representation
    local pseudo = func:pseudocode()
    
    -- Pattern matching on source level
    if pseudo:contains("while(1)") then
      -- Infinite loop detected
    end
    
    -- Get variable information
    local vars = func:variables()
    for _, var in ipairs(vars) do
      local ty = var:type()
      local uses = var:uses()
    end
  end
}

VulHunt includes a dataflow analysis engine for tracking data movement:

rule {
  metadata = {
    id = "DATAFLOW_001",
    name = "Format String Vulnerability"
  },
  
  scope = "function",
  
  check = function(func)
    -- Create dataflow engine
    local df = func:dataflow()
    
    -- Find format string functions
    local printf_calls = func:find_calls_to({"printf", "fprintf", "sprintf"})
    
    for _, call in ipairs(printf_calls) do
      -- Get argument at position 0 (format string)
      local arg = call:argument(0)
      
      -- Trace backwards to source
      local sources = df:trace_back(arg)
      
      for _, source in ipairs(sources) do
        if source:is_external_input() then
          return {
            match = true,
            vuln_type = "Format String",
            location = call:address()
          }
        end
      end
    end
    
    return { match = false }
  end
}
rule {
  scope = "function",
  
  check = function(func)
    -- Initialize dataflow
    local df = func:dataflow()
    
    -- Define sources (entry points for data)
    local sources = {
      function_params = true,
      global_reads = true,
      external_calls = true
    }
    
    -- Define sinks (sensitive operations)
    local sinks = {
      memory_writes = true,
      system_calls = true,
      cryptographic_ops = true
    }
    
    -- Run full dataflow analysis
    local flows = df:analyze({
      sources = sources,
      sinks = sinks,
      max_depth = 10
    })
    
    -- Report findings
    if #flows > 0 then
      return {
        match = true,
        dataflows = flows
      }
    end
    
    return { match = false }
  end
}

VulHunt supports signature matching and type information:

# Generate function signatures
vulhunt signatures extract binary.elf > sigs.json

# Apply signature database
vulhunt scan --signatures sigs.json binary.elf

# Library signature matching
vulhunt scan --match-libs openssl zlib binary.elf
rule {
  metadata = {
    id = "TYPEINFER_001",
    name = "Type-based Vulnerability Detection"
  },
  
  scope = "function",
  
  check = function(func)
    -- Get function signature
    local sig = func:signature()
    local params = sig:parameters()
    
    -- Analyze parameter types
    for i, param in ipairs(params) do
      local ty = param:type()
      
      -- Check for dangerous types
      if ty:is_pointer() and ty:pointee():is_void() then
        -- Void pointer usage
      end
      
      if ty:is_array() then
        local size = ty:size()
        -- Bounded array
      end
    end
    
    -- Get return type
    local ret_type = sig:return_type()
    
    return { match = false }
  end
}
# Identify linked modules
vulhunt modules binary.elf

# Analyze specific module
vulhunt scan --analyze-module libc.so binary.elf

# Module compatibility checking
vulhunt compat-check binary.elf

VulHunt generates detailed reports in multiple formats:

# Default text output
vulhunt scan binary.elf

# JSON format (machine-readable)
vulhunt scan --format json binary.elf > results.json

# HTML report with visualizations
vulhunt scan --format html --output report.html binary.elf

# CSV for spreadsheet import
vulhunt scan --format csv binary.elf > findings.csv

# SARIF format (standard reporting format)
vulhunt scan --format sarif binary.elf > results.sarif
# Filter by severity
vulhunt scan --min-severity high binary.elf

# Custom severity threshold
vulhunt scan --severity-filter "critical,high" binary.elf

# Severity mapping
# critical  - Immediate exploitation risk
# high      - Likely exploitable vulnerability
# medium    - Potential security impact
# low       - Minor security issue
# info      - Informational finding
{
  "finding_id": "VULN_001",
  "title": "Stack Buffer Overflow",
  "severity": "critical",
  "cve": ["CVE-2024-12345", "CVE-2024-12346"],
  "cwe": ["CWE-121"],
  "location": {
    "file": "binary.elf",
    "function": "process_input",
    "offset": "0x1234"
  },
  "evidence": "strcpy without bounds checking",
  "remediation": "Use strncpy or safer alternatives"
}

VulHunt integrates with Claude Skills for automated analysis and report generation:

# Automated vulnerability analysis
vulhunt analyze --ai binary.elf

# Generate detailed technical report
vulhunt report --ai --depth full binary.elf

# Interactive analysis session
vulhunt console --ai binary.elf
-- MCP-integrated analysis
rule {
  metadata = {
    id = "MCP_ANALYSIS",
    name = "AI-Assisted Vulnerability Analysis"
  },
  
  scope = "project",
  
  check = function(binary)
    -- Use MCP tools for external analysis
    local mcp_result = call_mcp_tool("analyze_codebase", {
      binary = binary:path(),
      rules = binary:active_rules(),
      depth = "comprehensive"
    })
    
    return mcp_result
  end
}

VulHunt uses configuration files to customize scanning behavior:

# vulhunt.yaml - Main configuration
version: 1.0
analysis:
  architecture: x86-64
  target: elf
  optimization_level: 2
  enable_dataflow: true
  
rules:
  builtin: true
  custom_paths:
    - ./rules/custom.lua
    - ./rules/firmware.lua
  
reporting:
  format: json
  min_severity: medium
  include_context: true
  
performance:
  parallel_threads: 4
  cache_ir: true
  memory_limit_mb: 4096
# Use predefined profiles
vulhunt scan --profile firmware binary.efi
vulhunt scan --profile uefi binary.efi
vulhunt scan --profile supply-chain dependency.so

# List available profiles
vulhunt list-profiles

# Create custom profile
cat > myprofile.yaml << 'EOF'
rules:
  - buffer_overflow
  - format_string
  - integer_overflow
  - use_after_free
output:
  format: html
  severity_threshold: medium
EOF

vulhunt scan --profile myprofile.yaml binary.elf
# Issue: "Architecture detection failed"
# Solution: Specify explicitly
vulhunt scan --arch x86-64 binary.elf

# Issue: "Rule compilation error"
# Solution: Validate rule syntax
vulhunt validate-rule custom.lua

# Issue: "Out of memory"
# Solution: Increase limit or reduce scope
vulhunt scan --memory-limit 8192 large_binary.elf

# Issue: "No vulnerabilities found"
# Solution: Enable all rules and verbose output
vulhunt scan --all-rules --verbose binary.elf

# Issue: "UEFI firmware not recognized"
# Solution: Specify UEFI target type
vulhunt scan --target uefi firmware.efi
# Enable debug logging
vulhunt scan --debug binary.elf 2>&1 | tee debug.log

# Dump intermediate representation
vulhunt scan --dump-ir binary.elf > ir_dump.txt

# Trace rule execution
vulhunt scan --trace-rules custom.lua binary.elf

# Profile performance
vulhunt scan --profile-perf binary.elf
  • Start simple: Begin with call_site scope before function or project
  • Test incrementally: Validate rules on known-vulnerable binaries
  • Document thoroughly: Use clear descriptions and references
  • Avoid false positives: Use multiple conditions and filtering
  • Version rules: Track changes with metadata dates
# Baseline scan with built-in rules
vulhunt scan baseline.elf > baseline.json

# Enhanced scan with custom rules
vulhunt scan --rules ./custom_rules/ enhanced.elf > enhanced.json

# Comparative analysis
vulhunt compare baseline.json enhanced.json

# Supply chain scanning
vulhunt scan --profile supply-chain \
  --sbom dependencies.json \
  --output audit_report.html
# 1. Extract modules from firmware
binwalk -e firmware.bin

# 2. Scan each module
for module in _firmware.bin.extracted/*/; do
  vulhunt scan --target uefi "$module" >> results.json
done

# 3. Aggregate findings
vulhunt merge results.json --output firmware_audit.html

# 4. Generate CVE report
vulhunt cve-report firmware_audit.html
# Integrate into CI/CD pipeline
vulhunt scan --ci-mode \
  --fail-on critical \
  --report ci_report.sarif \
  binary.elf

# Exit codes:
# 0 - No vulnerabilities
# 1 - Low/medium findings
# 2 - High severity findings
# 3 - Critical vulnerability found

Binarly Transparency Platform

  • Cloud-based firmware analysis
  • Historical vulnerability tracking
  • Supply chain risk assessment

Ghidra

  • NSA’s reverse engineering framework
  • Decompilation and code analysis
  • Compatible with VulHunt findings

Binary Ninja

  • Commercial binary analysis platform
  • Advanced disassembly and patching
  • Scriptable analysis environment

radare2

  • Open-source reverse engineering framework
  • Hexadecimal editor and disassembler
  • Scripting capabilities for automation