NullSec FSharpSignal

February 27, 2026 · View on GitHub

Threat Signal Correlator written in F#

Version Language License

Part of the NullSec offensive security toolkit
Twitter: x.com/AnonAntics
Portal: bad-antics.github.io

Overview

FSharpSignal is a threat intelligence correlation engine that aggregates indicators of compromise (IOCs) and identifies attack patterns through functional programming paradigms. Built with F#'s discriminated unions, pattern matching, and immutable data structures.

F# Features Showcased

  • Discriminated Unions: Type-safe threat categories
  • Active Patterns: Custom pattern matching
  • Computation Expressions: Signal builder monad
  • Higher-Order Functions: Composable pipelines
  • Record Types: Immutable data structures
  • Pattern Matching: Exhaustive case handling
  • List Comprehensions: Functional collections
  • Type Inference: Clean, concise code

Correlation Rules

RuleDescriptionRequired TypesMITRE
APT CampaignMulti-vector campaign detectionIP, Domain, HashTA0001
C2 InfrastructureCommand & control patternsIP, DomainT1071
Lateral MovementNetwork traversal indicatorsIP, ProcessTA0008
Data StagingExfiltration preparationHash, Process, RegistryT1074
Phishing InfrastructurePhishing campaign IOCsDomain, Email, URLT1566

Installation

# Clone
git clone https://github.com/bad-antics/nullsec-fsharpsignal.git
cd nullsec-fsharpsignal

# Build with .NET SDK
dotnet build

# Or compile directly
fsharpc FSharpSignal.fs -o fsharpsignal.exe

Usage

# Run demo mode
dotnet run

# Process IOC file
dotnet run -- -f indicators.json

# Enable verbose correlation
dotnet run -- -v -f indicators.json

Options

USAGE:
    fsharpsignal [OPTIONS]

OPTIONS:
    -h, --help       Show help
    -f, --file       IOC file to process
    -v, --verbose    Verbose output
    -r, --rules      Custom correlation rules

Sample Output

╔══════════════════════════════════════════════════════════════════╗
║        NullSec FSharpSignal - Threat Signal Correlator           ║
╚══════════════════════════════════════════════════════════════════╝

[Demo Mode]

Loading threat intelligence indicators...

  Loaded 13 indicators

Running correlation engine...

  ╔═══════════════════════════════════════════════════════════════╗
  ║  CORRELATED SIGNAL: APT Campaign Detection                    ║
  ╚═══════════════════════════════════════════════════════════════╝
    
    Severity:     CRITICAL
    Score:        42.5/100
    MITRE ID:     TA0001
    Correlation:  2024-01-15 14:30:22 UTC
    
    Indicators:
      [HIGH] IP: 185.220.101.45
      [HIGH] Domain: update-service.duckdns.org
      [CRITICAL] Hash: 44d88612fea8a8f36de82e1278abb02f

  ╔═══════════════════════════════════════════════════════════════╗
  ║  CORRELATED SIGNAL: C2 Infrastructure                         ║
  ╚═══════════════════════════════════════════════════════════════╝
    
    Severity:     HIGH
    Score:        28.0/100
    MITRE ID:     T1071
    Correlation:  2024-01-15 14:30:22 UTC
    
    Indicators:
      [HIGH] IP: 185.220.101.45
      [HIGH] Domain: c2-beacon.no-ip.com

═══════════════════════════════════════════════════════════════════

  Summary:
    Total Signals:    4
    Critical:         1
    High:             2
    Medium:           1
    Combined Score:   112.5

Code Highlights

Discriminated Unions for Type Safety

type Severity =
    | Critical
    | High
    | Medium
    | Low
    | Informational

type IndicatorType =
    | IPAddress
    | Domain
    | FileHash
    | URL
    | Email
    | Registry
    | ProcessName
    | Mutex

Active Patterns for Custom Matching

let (|C2Domain|PhishingDomain|LegitDomain|) (domain: string) =
    let c2Patterns = [".onion"; ".bit"; "duckdns.org"; "no-ip.com"]
    let phishingKeywords = ["login"; "secure"; "verify"]
    let lowerDomain = domain.ToLowerInvariant()
    if c2Patterns |> List.exists (fun p -> lowerDomain.EndsWith(p)) then C2Domain
    elif phishingKeywords |> List.exists (fun k -> lowerDomain.Contains(k)) then PhishingDomain
    else LegitDomain

// Usage
let enrichDomain indicator =
    match indicator.Value with
    | C2Domain -> { indicator with Category = C2Infrastructure }
    | PhishingDomain -> { indicator with Category = Phishing }
    | LegitDomain -> indicator

Computation Expression (Signal Builder)

type SignalBuilder() =
    member _.Bind(x, f) = 
        match x with
        | Some value -> f value
        | None -> None
    member _.Return(x) = Some x
    member _.Zero() = None

let signal = SignalBuilder()

// Usage
let result = signal {
    let! indicator = findIndicator "185.220.101.45"
    let! enriched = enrichIndicator indicator
    return correlate enriched
}

Pipeline Composition

let correlate (indicators: Indicator list) =
    defaultRules
    |> List.map (fun rule -> matchRule rule indicators)
    |> List.choose (function
        | SignalFound signal -> Some signal
        | _ -> None)
    |> List.sortByDescending (fun s -> Severity.toInt s.AggregatedSeverity)

Architecture

┌────────────────────────────────────────────────────────────────┐
│               FSharpSignal Architecture                        │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│   ┌──────────────────┐                                        │
│   │  IOC Input       │  (IP, Domain, Hash, URL, Email)        │
│   └────────┬─────────┘                                        │
│            │                                                   │
│            ▼                                                   │
│   ┌──────────────────┐                                        │
│   │  Type Inference  │  Discriminated Unions                  │
│   │  & Validation    │  Active Patterns                       │
│   └────────┬─────────┘                                        │
│            │                                                   │
│            ▼                                                   │
│   ┌──────────────────┐                                        │
│   │  Enrichment      │  IP → GeoIP, ASN                       │
│   │  Pipeline        │  Hash → Malware DB                     │
│   │  (List.map)      │  Domain → Classification               │
│   └────────┬─────────┘                                        │
│            │                                                   │
│            ▼                                                   │
│   ┌──────────────────────────────────────────────────┐        │
│   │           Correlation Engine                      │        │
│   │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │        │
│   │  │ APT Rules   │ │ C2 Rules    │ │ Phish Rules │ │        │
│   │  └─────────────┘ └─────────────┘ └─────────────┘ │        │
│   │                Pattern Matching                   │        │
│   └────────────────────────┬─────────────────────────┘        │
│                            │                                   │
│                            ▼                                   │
│   ┌──────────────────┐    ┌──────────────────┐               │
│   │  Result Type     │    │  Report          │               │
│   │  SignalFound     │───▶│  Generation      │               │
│   │  NoMatch         │    │                  │               │
│   │  InsufficientData│    └──────────────────┘               │
│   └──────────────────┘                                        │
│                                                                │
└────────────────────────────────────────────────────────────────┘

Why F#?

RequirementF# Advantage
CorrectnessExhaustive pattern matching
ImmutabilityDefault immutable data
CompositionFunction pipelines
Type SafetyDiscriminated unions
ConcisenessType inference
.NET EcosystemFull interoperability

License

MIT License - See LICENSE for details.