network security

Implementing Network Traffic Analysis with Arkime

Deploy and query Arkime (formerly Moloch) for full packet capture network traffic analysis. Uses the Arkime API v3 to search sessions, download PCAPs, analyze connection patterns, detect beaconing behavior, and identify suspicious network flows. Monitors DNS queries, HTTP traffic, and TLS certificate anomalies across captured traffic.

arkimefull-packet-capturenetwork-forensicsnetwork-securityntapcap-analysis
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • When deploying or configuring implementing network traffic analysis with arkime capabilities in your environment
  • When establishing security controls aligned to compliance requirements
  • When building or improving security architecture for this domain
  • When conducting security assessments that require this implementation

Prerequisites

  • Familiarity with network security concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

Instructions

  1. Install dependencies: pip install requests
  2. Configure Arkime viewer URL and credentials.
  3. Run the agent to query Arkime sessions and analyze traffic:
    • Search sessions by IP, port, protocol, or expression
    • Download PCAP data for forensic analysis
    • Detect C2 beaconing via connection interval analysis
    • Identify DNS tunneling through query length statistics
    • Flag connections to known-bad TLS certificate issuers
python scripts/agent.py --arkime-url https://arkime.local:8005 --user admin --password secret --output arkime_report.json

Examples

Beaconing Detection

Source: 10.1.2.50 -> 185.220.101.34:443
Sessions: 288 over 24 hours
Avg interval: 300s, Jitter: 4.2%
Verdict: HIGH confidence C2 beaconing (jitter < 5%)
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 1

api-reference.md2.0 KB

API Reference: Arkime Network Traffic Analysis

Authentication

HTTPDigestAuth(username, password)

All API requests require Digest authentication.

Session Search

GET /api/sessions
Parameter Type Description
date int Time range in hours (1=last hour)
expression string Arkime search expression
length int Max results to return
order string Sort field:direction (e.g. lastPacket:desc)
fields string Comma-separated field list

PCAP Download

GET /api/sessions/pcap
GET /api/sessions/pcapng
Parameter Description
date Time range in hours
expression Filter expression
Returns raw PCAP/PCAPNG binary data.

Connection Graph

GET /api/connections

Returns nodes (IPs) and links (connections) for network graph visualization.

SPI View (Field Statistics)

GET /api/spiview
Parameter Description
spi Comma-separated fields (e.g. srcIp,dstIp,dstPort)
Returns top values and counts for each field.

Session Fields

Field Description
srcIp Source IP address
dstIp Destination IP address
srcPort Source port
dstPort Destination port
srcBytes Bytes sent by source
dstBytes Bytes sent by destination
lastPacket Timestamp of last packet (ms)
srcJa3 JA3 fingerprint of client TLS
tls.issuerCN TLS certificate issuer CN
tls.subjectCN TLS certificate subject CN
tls.notAfter Certificate expiry (ms epoch)

Search Expressions

ip.src == 10.0.0.0/8
port.dst == 443
protocols == tls
country.src == CN
bytes > 1000000

Beaconing Detection Logic

  • Collect connection timestamps per (src, dst, port) tuple
  • Calculate intervals between consecutive connections
  • Compute jitter ratio: std_dev / avg_interval
  • Jitter < 0.05 = high confidence C2, < 0.15 = medium

Scripts 1

agent.py8.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Arkime Network Traffic Analysis Agent - Queries Arkime API for session analysis and anomaly detection."""

import json
import logging
import os
import argparse
from datetime import datetime
from collections import defaultdict

import requests
from requests.auth import HTTPDigestAuth

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)


def arkime_request(base_url, endpoint, auth, params=None):
    """Make an authenticated request to Arkime API v3."""
    url = f"{base_url}{endpoint}"
    try:
        resp = requests.get(url, auth=HTTPDigestAuth(*auth), params=params, verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30)  # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
        resp.raise_for_status()
        return resp.json()
    except requests.RequestException as e:
        logger.error("Arkime API error %s: %s", endpoint, e)
        return None


def search_sessions(base_url, auth, expression, date_range=1, length=500):
    """Search Arkime sessions with an expression filter."""
    params = {
        "date": date_range,
        "expression": expression,
        "length": length,
        "order": "lastPacket:desc",
    }
    data = arkime_request(base_url, "/api/sessions", auth, params)
    if data and "data" in data:
        logger.info("Found %d sessions for expression: %s", len(data["data"]), expression)
        return data["data"]
    return []


def get_connections(base_url, auth, expression, date_range=1):
    """Get connection graph data from Arkime."""
    params = {"date": date_range, "expression": expression}
    data = arkime_request(base_url, "/api/connections", auth, params)
    if data:
        nodes = data.get("nodes", [])
        links = data.get("links", [])
        logger.info("Connection graph: %d nodes, %d links", len(nodes), len(links))
        return {"nodes": nodes, "links": links}
    return {"nodes": [], "links": []}


def get_spi_view(base_url, auth, expression, date_range=1):
    """Get SPI view field statistics from Arkime."""
    params = {"date": date_range, "expression": expression, "spi": "srcIp,dstIp,dstPort"}
    data = arkime_request(base_url, "/api/spiview", auth, params)
    return data if data else {}


def detect_beaconing(sessions, interval_threshold=0.15):
    """Detect C2 beaconing by analyzing connection intervals."""
    connections = defaultdict(list)
    for s in sessions:
        key = (s.get("srcIp", ""), s.get("dstIp", ""), s.get("dstPort", 0))
        connections[key].append(s.get("lastPacket", 0))

    beacons = []
    for (src, dst, port), timestamps in connections.items():
        if len(timestamps) < 10:
            continue
        timestamps.sort()
        intervals = [timestamps[i + 1] - timestamps[i] for i in range(len(timestamps) - 1)]
        if not intervals:
            continue
        avg_interval = sum(intervals) / len(intervals)
        if avg_interval == 0:
            continue
        std_dev = (sum((i - avg_interval) ** 2 for i in intervals) / len(intervals)) ** 0.5
        jitter_ratio = std_dev / avg_interval

        if jitter_ratio < interval_threshold:
            beacons.append({
                "src_ip": src,
                "dst_ip": dst,
                "dst_port": port,
                "session_count": len(timestamps),
                "avg_interval_sec": round(avg_interval / 1000, 1),
                "jitter_ratio": round(jitter_ratio, 4),
                "confidence": "high" if jitter_ratio < 0.05 else "medium",
                "severity": "critical",
            })
            logger.warning("Beaconing: %s -> %s:%d (jitter: %.4f)", src, dst, port, jitter_ratio)
    return beacons


def detect_dns_tunneling(sessions, query_len_threshold=50):
    """Detect DNS tunneling via abnormally long DNS queries."""
    dns_sessions = [s for s in sessions if s.get("dstPort") == 53]
    suspicious = []
    src_stats = defaultdict(lambda: {"count": 0, "total_bytes": 0})

    for s in dns_sessions:
        src = s.get("srcIp", "")
        src_stats[src]["count"] += 1
        src_stats[src]["total_bytes"] += s.get("srcBytes", 0) + s.get("dstBytes", 0)

    for src, stats in src_stats.items():
        avg_bytes = stats["total_bytes"] / max(stats["count"], 1)
        if stats["count"] > 100 and avg_bytes > query_len_threshold:
            suspicious.append({
                "src_ip": src,
                "dns_query_count": stats["count"],
                "avg_bytes_per_query": round(avg_bytes, 1),
                "total_bytes": stats["total_bytes"],
                "severity": "high",
                "indicator": "DNS tunneling - high volume with large payloads",
            })
    return suspicious


def detect_large_transfers(sessions, threshold_mb=100):
    """Detect unusually large data transfers."""
    threshold_bytes = threshold_mb * 1024 * 1024
    large = []
    for s in sessions:
        total = s.get("srcBytes", 0) + s.get("dstBytes", 0)
        if total > threshold_bytes:
            large.append({
                "src_ip": s.get("srcIp", ""),
                "dst_ip": s.get("dstIp", ""),
                "dst_port": s.get("dstPort", 0),
                "total_bytes": total,
                "total_mb": round(total / (1024 * 1024), 2),
                "severity": "high",
            })
    return large


def detect_tls_anomalies(sessions):
    """Detect TLS certificate anomalies (self-signed, expired, unusual issuers)."""
    anomalies = []
    for s in sessions:
        tls = s.get("tls", {})
        if not tls:
            continue
        ja3 = s.get("srcJa3", "")
        issuer_cn = tls.get("issuerCN", "")
        not_after = tls.get("notAfter", 0)
        if issuer_cn and issuer_cn == tls.get("subjectCN", ""):
            anomalies.append({
                "src_ip": s.get("srcIp", ""),
                "dst_ip": s.get("dstIp", ""),
                "issue": "self-signed certificate",
                "issuer": issuer_cn,
                "severity": "medium",
            })
        if not_after and not_after < int(datetime.utcnow().timestamp() * 1000):
            anomalies.append({
                "src_ip": s.get("srcIp", ""),
                "dst_ip": s.get("dstIp", ""),
                "issue": "expired certificate",
                "issuer": issuer_cn,
                "severity": "medium",
            })
    return anomalies


def generate_report(beacons, dns_tunneling, large_transfers, tls_anomalies, session_count):
    """Generate network traffic analysis report."""
    all_findings = beacons + dns_tunneling + large_transfers + tls_anomalies
    critical = [f for f in all_findings if f.get("severity") == "critical"]
    report = {
        "timestamp": datetime.utcnow().isoformat(),
        "sessions_analyzed": session_count,
        "findings_total": len(all_findings),
        "critical_count": len(critical),
        "beaconing_detected": beacons,
        "dns_tunneling": dns_tunneling,
        "large_transfers": large_transfers,
        "tls_anomalies": tls_anomalies,
    }
    print(f"ARKIME REPORT: {len(all_findings)} findings ({len(critical)} critical) from {session_count} sessions")
    return report


def main():
    parser = argparse.ArgumentParser(description="Arkime Network Traffic Analysis Agent")
    parser.add_argument("--arkime-url", required=True, help="Arkime viewer URL")
    parser.add_argument("--user", required=True)
    parser.add_argument("--password", required=True)
    parser.add_argument("--expression", default="*", help="Arkime search expression")
    parser.add_argument("--date-range", type=int, default=1, help="Date range in hours")
    parser.add_argument("--output", default="arkime_report.json")
    args = parser.parse_args()

    auth = (args.user, args.password)
    sessions = search_sessions(args.arkime_url, auth, args.expression, args.date_range)
    beacons = detect_beaconing(sessions)
    dns_tunnel = detect_dns_tunneling(sessions)
    large = detect_large_transfers(sessions)
    tls = detect_tls_anomalies(sessions)

    report = generate_report(beacons, dns_tunnel, large, tls, len(sessions))
    with open(args.output, "w") as f:
        json.dump(report, f, indent=2)
    logger.info("Report saved to %s", args.output)


if __name__ == "__main__":
    main()
Keep exploring