Skip to content

cochaviz/gomon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

81 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gomon

gomon monitors sandboxed malware or bot traffic and emits structured alerts whenever observed network behavior crosses configurable thresholds. It ingests either a live interface or a pre-recorded PCAP file, groups packets into fixed time windows, and writes Suricata-compatible Eve JSON for each window that shows suspicious activity.

The tool is designed for use inside automated malware-analysis pipelines. It integrates directly with bottle and bottle-warden, but runs standalone on any host with libpcap.

Contents


How it works

gomon runs a sliding-window analysis loop over captured packets.

Packets (live interface or PCAP)
  │
  ▼
FlowCollector          — groups packets into bidirectional flows per window
  │
  │  WindowStats (packet counts per flow, start time, duration)
  ▼
BehaviorClassifier     — applies thresholds, assigns local and global behaviors
  │
  │  LocalBehaviors (per-flow), GlobalBehavior (window-level)
  ▼
Eve JSON output        — Suricata-compatible alert or stats record per event

Each window produces:

  • One global behavior record reflecting the overall character of the window (scanning or idle).
  • Zero or more local behavior records, one per distinct bidirectional flow that crossed a threshold.

When the window is empty (no packets at all), cross-window state (flow IDs, previously seen hosts) is reset so that a resumed session is not contaminated by a previous one.


Classification model

Local behaviors (per-flow)

Every bidirectional flow observed in a window is classified as one of:

Class Condition Eve event_type
attack Packet rate exceeds --packet-threshold and a C2 IP (--c2-ip) is set alert
outbound_connection Packet rate below threshold, or no C2 IP is configured alert

The attack classification deliberately requires a known C2 IP because a high packet rate alone is ambiguous (e.g. bulk file transfer). If no --c2-ip is provided, every flow is logged as outbound_connection regardless of rate.

Packet rate is computed as total packets in both directions divided by the window duration in seconds.

Direction stats are included in every local behavior record: src_to_dst_packets, dst_to_src_packets, src_to_dst_rate, dst_to_src_rate, and an amplification factor (dst_to_src_packets / src_to_dst_packets). An amplification factor greater than 1 indicates the destination responded with more packets than it received, which is a useful signal for reflection attacks.

The "source" of a flow is determined by a priority chain:

  1. Whichever side matches --bot-ip (i.e. <src_ip>).
  2. Whichever side is RFC 1918 (private address space).
  3. Whichever side sent fewer packets in the window (heuristic for scanner/initiator vs. responder).
  4. Canonical tie-break (lower IP address / lower port).

Global behaviors (window-level)

The global behavior summarizes the entire window:

Class Condition Eve event_type
scanning Unique destination host count exceeds --destination-threshold alert
idle No threshold exceeded (emitted only with --show-idle) stats

The destination count and the mode used to compute it are both recorded in the output, making the classification fully reproducible from the log alone.


Scan detection modes

Three modes are available via --scan-detection-mode. All operate on unique destination IP addresses; port diversity (vertical scans) is not yet considered.

Mode Description
filtered-host-rate (default) Unique destination IPs per window, excluding hosts already flagged as attack targets in the same window. Prevents a single victim from inflating the scan rate when the bot attacks the same host many times.
host-rate Raw unique destination IPs per window, no filtering. Use when you want a conservative, easy-to-replicate metric.
new-host-rate Unique destination IPs that were not seen in the previous window. Useful for detecting scanners that enumerate hosts steadily across windows; suppresses the initial burst from already-known hosts.

Output format

gomon writes one JSON object per line (NDJSON), compatible with Suricata's Eve schema. Standard Eve fields are present (timestamp, event_type, src_ip, dest_ip, src_port, dest_port, proto, flow_id, alert), with gomon-specific detail under metadata.gomon.

Local behavior record (attack or outbound_connection)

{
  "timestamp": "2024-11-01T12:00:15.000000Z",
  "event_type": "alert",
  "src_ip": "10.0.0.5",
  "dest_ip": "1.2.3.4",
  "src_port": 54321,
  "dest_port": 80,
  "proto": "tcp",
  "flow_id": 12345678901234567,
  "host": "my-sample-42",
  "alert": {
    "action": "allowed",
    "gid": 5,
    "signature_id": 2100001,
    "rev": 1,
    "signature": "gomon high packet-rate to single host",
    "category": "attack",
    "severity": 2
  },
  "metadata": {
    "gomon": {
      "scope": "local",
      "context": {
        "sample_id": "my-sample-42",
        "bot_ip": "10.0.0.5",
        "c2_ip": "203.0.113.4"
      },
      "packet_rate": 47.3,
      "packet_threshold": 20.0,
      "src_to_dst_packets": 120,
      "dst_to_src_packets": 60,
      "src_to_dst_rate": 4.0,
      "dst_to_src_rate": 2.0,
      "amplification_factor": 0.5
    }
  }
}

Global behavior record (scanning)

{
  "timestamp": "2024-11-01T12:00:15.000000Z",
  "event_type": "alert",
  "src_ip": "10.0.0.5",
  "dest_ip": "0.0.0.0",
  "flow_id": 98765432109876543,
  "alert": {
    "signature_id": 2100002,
    "signature": "gomon horizontal scan host-rate exceeded",
    "category": "scan",
    "severity": 3
  },
  "metadata": {
    "gomon": {
      "scope": "global",
      "context": { "sample_id": "my-sample-42", "bot_ip": "10.0.0.5" },
      "packet_rate": 210.0,
      "packet_threshold": 20.0,
      "destination_rate": 35.0,
      "destination_rate_threshold": 10.0
    }
  }
}

The list of scanned destination IPs is available through the corresponding local behavior records emitted in the same window. Each local record's dest_ip is one flow target.

Idle record

Emitted only with --show-idle. Uses event_type: stats (not alert) and carries the measured rates even though no threshold was crossed. Useful for correlating quiet periods against sandbox execution state.

Signature IDs

Behavior signature_id
attack 2100001
scanning 2100002
outbound_connection 2100003

These IDs are stable across versions and can be used as filters in downstream processing (e.g. jq 'select(.alert.signature_id == 2100001)').

Flow IDs

Each behavior is assigned a stable flow_id that persists across consecutive windows as long as the same flow remains active. A gap (empty window) resets continuity. This lets you reconstruct the timeline of a single flow across multiple windows by grouping on flow_id.


Installation

Requirements:

  • Linux with libpcap headers (libpcap-dev on Debian/Ubuntu) for live capture.
  • Root or appropriate group membership to open a network interface. PCAP files work without elevated privileges.
  • Go 1.24+ for building from source.
# Install from the module registry
go install github.com/cochaviz/gomon@latest

# Or build from source
git clone https://github.com/cochaviz/gomon.git
cd gomon
go build -o gomon ./cmd/gomon

Confirm installation:

gomon --version
# gomon version v0.3.0

Usage

gomon <input> <src_ip> [flags]
Argument Description
<input> Network interface name (e.g. eth0, vnet0) or path to a .pcap / .pcapng file.
<src_ip> IPv4 address of the monitored host (the bot). Used to orient flows and populate bot_ip in output.

Analyze a PCAP file (offline)

gomon sample.pcap 10.0.0.5 \
  --window 15 \
  --packet-threshold 20 \
  --destination-threshold 25 \
  --scan-detection-mode filtered-host-rate \
  --c2-ip 203.0.113.4 \
  --sample-id my-sample-42 \
  --eve-log-path /tmp/my-sample-42.eve.json

Monitor a live interface

sudo gomon vnet0 10.10.0.20 \
  --c2-ip 203.0.113.4 \
  --sample-id beacon-42 \
  --save-packets 100 \
  --capture-dir /var/log/gomon/captures \
  --show-idle

Exclude infrastructure IPs from metrics

Use --ignore-dst to drop known-benign endpoints (DNS resolver, gateway, sandbox controller) from flow counts so they do not inflate rates or appear in scan lists. The flag is repeatable:

gomon sample.pcap 10.0.0.5 \
  --ignore-dst 10.0.0.1 \
  --ignore-dst 8.8.8.8 \
  --ignore-dst 192.168.1.254

The C2 IP supplied via --c2-ip is automatically added to this exclusion list.


All flags

Flag Default Description
--window 30 Analysis window size in seconds.
--packet-threshold 5 Packets per second per flow before the flow is classified as attack (requires --c2-ip).
--destination-threshold 10 Unique destination hosts per second before the window is classified as scanning.
--scan-detection-mode filtered-host-rate One of host-rate, new-host-rate, filtered-host-rate. See Scan detection modes.
--c2-ip (unset) Known C2 server IP. Required for attack classification; automatically excluded from metrics.
--sample-id (unset) Free-form identifier written into every output record's context.sample_id.
--ignore-dst (none) Destination IPs to exclude from all metrics. Repeatable.
--show-idle false Emit a stats record for every window that produces no alerts.
--eve-log-path (stdout) File path for Eve JSON output. Appends if the file already exists.
--save-packets 0 (off) Number of most recent packets per attack destination to write as a PCAP artifact when an attack alert fires.
--capture-dir ./captures Directory for packet capture artifacts. Created if it does not exist.
--log-level info Verbosity of the operational log written to stderr: debug, info, warn, error.
--version Print the binary version and exit.

Memory note: Flow tracking is capped at 1024 unique bidirectional flows per window. Windows that hit the cap log a warning; flows beyond the cap are not counted. In practice this limit is not reached in single-sandbox scenarios.


Integration with bottle / bottle-warden

gomon is designed as a sidecar to the bottle sandbox instrumentation framework. Add it to a bottle profile so every sandbox run inherits consistent thresholds:

cli:
  - command: >
      gomon {{ .VmInterface }} {{ .VmIp }}
      {{- if .C2Ip }} --c2-ip {{ .C2Ip }}{{ end }}
      --sample-id {{ .SampleName }}
      --window 30
      --packet-threshold 20
      --destination-threshold 25
      --save-packets 100
      --capture-dir {{ .LogDir }}/captures
      --eve-log-path {{ .LogDir }}/{{ .SampleName }}.eve.json
    output: file

bottle-warden can tail the Eve file to detect when beaconing stops or spikes, using standard Suricata tooling or plain jq queries.


Reproducibility notes for research

Configuration provenance

At startup, gomon prints every active flag value to stderr before processing begins:

Configuration:
  version: v0.3.0
  input: sample.pcap
  source-ip: 10.0.0.5
  window: 30s
  packet-threshold: 20.00
  destination-threshold: 25.00
  scan-detection-mode: filtered-host-rate
  ...

Capturing this output alongside Eve logs gives a complete record of the parameters used for a run.

Threshold selection

The default thresholds (--packet-threshold 5, --destination-threshold 10) are intentionally conservative. For publication, report the thresholds used, the window size, and the scan detection mode explicitly, since all three affect what is and is not classified as suspicious.

Offline vs. live capture

PCAP file analysis is fully deterministic: the same file with the same flags always produces the same output. Live interface capture is not deterministic (packet ordering depends on the OS scheduler). For reproducible experiments, record traffic first with tcpdump or Wireshark and replay offline.

Packet artifacts

When --save-packets N is set, gomon writes a .pcap file to --capture-dir for each attack alert, named after the sample ID, destination IP, and timestamp. These artifacts let you inspect the exact packets that triggered an alert without re-running the sandbox.


Development

# Run all tests
go test ./...

# Run with verbose window accounting
gomon sample.pcap 10.0.0.5 --log-level debug

# Build and install locally
go build -o gomon ./cmd/gomon

Module path: github.com/cochaviz/gomon

Source layout:

Path Contents
cmd/cli.go CLI entry point, flag definitions, startup banner
internal/collector.go FlowCollector — packet accumulation and windowing
internal/classifier.go BehaviorClassifier — threshold application, flow ID continuity
internal/analysis.go AnalysisConfiguration — orchestration, Eve output, capture
internal/behavior.go Data types: LocalBehavior, GlobalBehavior, BehaviorFlow
internal/flows.go Flow key types, canonical orientation, normalizedFlowCounts
internal/eve_logger.go Suricata Eve JSON serialization
internal/utils.go Packet counting, RFC 1918 checks, source endpoint heuristics

About

Network monitoring for botnet clients, extract attacks packets with eve-compatible logging

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages