SEAS-8414 Chapter 3 Doctoral Teaching Notebook¶

Detective Analytics for Vulnerability Assessment, Protocol Security, and AI-Assisted Triage¶

This notebook is designed as a Chapter 3 teaching artifact for experienced cybersecurity practitioners. Each section follows the same pattern:

  1. Explain the concept and how it works internally.
  2. Use a real public dataset or a real local tool.
  3. Demonstrate the mechanism with executable Python.
  4. Interpret what the result teaches before moving to the next concept.

The notebook intentionally separates three epistemic categories:

  • Measured: produced by code in this notebook from a live public source, local file, or local tool.
  • Simulated: a controlled local lab example used because executing against third-party systems would be unsafe or unauthorized.
  • Design pattern: how commercial platforms generally implement the capability, based on public documentation and product patterns.

Safety rule: scanner and credential-checking execution is restricted to local loopback targets created inside this notebook. Public Internet resources are used only as datasets, documentation, templates, or firmware files.

Audio explanation for original cell 01

Transcript: This opening cell frames the notebook as the master Chapter 3 teaching artifact. It teaches that vulnerability work is an evidence pipeline, not a tool demo, and it sets the rule that measured, simulated, and design-pattern claims must stay separate.

AI mentor analysis for original cell 01: Introduction to SEAS-8414 Chapter 3

This introductory markdown cell outlines the structure and purpose of the SEAS-8414 Chapter 3 notebook. It emphasizes the focus on detective analytics for vulnerability assessment, protocol security, and AI-assisted triage. The notebook is designed for experienced cybersecurity practitioners and follows a consistent pattern: explaining concepts, using real datasets or tools, demonstrating mechanisms with Python, and interpreting results. It categorizes evidence into measured, simulated, and design patterns, ensuring safe execution by restricting scanner and credential-checking to local targets.

Discussion question: How does categorizing evidence into measured, simulated, and design patterns enhance the learning experience in cybersecurity education?

Source Contract and Teaching Map¶

Topic Concept taught Real dataset or tool used Execution status
Detective analytics Evidence fusion from CVE, KEV, exploit likelihood, and asset context CISA KEV JSON, NVD API, FIRST EPSS API Live public feeds
CPE-to-CVE pipeline Banner normalization, CPE selection, NVD CVE correlation, CVSS interpretation NVD CPE and CVE API Live public API
AutoML for triage Supervised severity classification and model selection NVD CVE descriptions labeled by CVSS severity Live public API
OpenVAS/GVM Scanner architecture, GMP XML workflows, NASL vulnerability tests Greenbone/OpenVAS public source tree, local XML examples Public repo plus simulated GMP contract
Nuclei YAML template internals, matchers, metadata, safe execution ProjectDiscovery nuclei-templates repository, local nuclei binary Public templates plus local target
Default credentials Credential evidence loops, false-positive controls, lockout safety SecLists default credential dataset, local HTTP Basic server Public dataset plus local target
Firmware integrity Hash validation, vendor manifest matching, entropy anomaly analysis OpenWrt firmware and sha256sums Live public files
Protocol grammar inference PCAP fields, frame geometry, byte-position profiles Wireshark DHCP sample PCAP, local tshark Public PCAP plus local tool
LLM-guided mutation fuzzing Parser-state feedback, semantic mutation, RL operator selection Local toy parser and deterministic mutator Simulated safe target
Transformers, GenAI, graphs, AR-ready data Self-attention over protocol tokens and evidence graph export NumPy, NetworkX, local JSON export Local computation

Reference URLs used by the notebook and the commercial-platform discussion:

  • NVD API: https://nvd.nist.gov/developers/vulnerabilities
  • NVD CPE API: https://nvd.nist.gov/developers/products
  • CISA KEV catalog: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
  • FIRST EPSS API: https://www.first.org/epss/api
  • ProjectDiscovery nuclei templates: https://github.com/projectdiscovery/nuclei-templates
  • ProjectDiscovery template syntax: https://docs.projectdiscovery.io/templates/introduction
  • Greenbone/OpenVAS scanner source: https://github.com/greenbone/openvas-scanner
  • Greenbone community documentation: https://greenbone.github.io/docs/latest/
  • SecLists default credentials: https://github.com/danielmiessler/SecLists
  • Wireshark sample captures: https://gitlab.com/wireshark/wireshark/-/wikis/SampleCaptures
  • OpenWrt downloads: https://downloads.openwrt.org/
  • Microsoft Defender for IoT: https://learn.microsoft.com/en-us/azure/defender-for-iot/
  • Tenable OT Security docs: https://docs.tenable.com/OT-security/
  • Armis platform pages: https://www.armis.com/platform/
  • Claroty xDome pages: https://www.claroty.com/products/xdome

Audio explanation for original cell 02

Transcript: This source contract cell tells students where every dataset and tool comes from. It teaches source discipline: NVD, CISA KEV, EPSS, Nuclei templates, SecLists, OpenWrt firmware, Wireshark PCAPs, and commercial product references are explicitly named before analysis begins.

AI mentor analysis for original cell 02: Source Contract and Teaching Map Overview

This markdown cell provides a structured overview of the topics covered in the notebook, detailing the concept taught, the dataset or tool used, and the execution status. It highlights the use of live public feeds and APIs for detective analytics, CPE-to-CVE pipelines, and AutoML for triage. It also mentions the use of public repositories and local targets for tools like OpenVAS/GVM and Nuclei. This map serves as a guide for practitioners to understand the scope and resources involved in each section, ensuring clarity and focus on practical applications.

Discussion question: Why is it important to use both live public datasets and local simulations in cybersecurity training?

Mermaid concept diagram before original cell 03: Lab Environment Architecture

This diagram explains the runtime boundaries before students run setup cells.

Mermaid concept diagram before original cell 03: Lab Environment Architecture

0. Lab Setup¶

This cell installs small Python dependencies only when they are missing. The heavy external tools, tshark and nuclei, are expected to be installed locally. On this machine they are installed through Homebrew and discovered by shutil.which.

The notebook uses a cache directory under ~/Downloads/seas8414_ch03_master_teaching so repeated teaching runs do not hammer public APIs.

Audio explanation for original cell 03

Transcript: This setup explanation prepares the lab environment. It teaches that repeatable security analytics starts with controlled dependencies, local tool discovery, and a cache directory so that teaching runs do not depend on one fragile live request.

AI mentor analysis for original cell 03: Lab Setup Explanation

This markdown cell explains the lab setup process, focusing on the installation of Python dependencies and the expectation that external tools like `tshark` and `nuclei` are pre-installed locally. It mentions the use of a cache directory to prevent excessive API calls during repeated teaching runs. This setup ensures that the notebook can be executed smoothly without unnecessary delays, while also respecting the rate limits of public APIs. The setup process is crucial for maintaining an efficient and effective learning environment.

Discussion question: What are the benefits of using a cache directory in a teaching notebook that interacts with public APIs?

InĀ [1]:
import sys
import subprocess
import pkgutil

required_modules = {
    "pandas": "pandas",
    "numpy": "numpy",
    "requests": "requests",
    "sklearn": "scikit-learn",
    "yaml": "pyyaml",
    "networkx": "networkx",
    "matplotlib": "matplotlib",
    "plotly": "plotly",
    "bs4": "beautifulsoup4",
    "lxml": "lxml",
}

missing = [pkg for module, pkg in required_modules.items() if pkgutil.find_loader(module) is None]
if missing:
    print(f"Installing missing packages: {missing}")
    subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", *missing])
else:
    print("All Python dependencies already available.")
All Python dependencies already available.

Audio explanation for original cell 04

Transcript: This code cell installs only the small Python packages that are missing. It teaches a pragmatic lab pattern: keep setup minimal, avoid changing the repository, and make the notebook runnable on a clean teaching machine.

AI mentor analysis for original cell 04: Python Dependency Check and Installation

This code cell checks for the presence of essential Python modules required for the notebook's execution. It uses `pkgutil.find_loader` to identify missing packages and installs them using `pip` if necessary. This ensures that all dependencies are available, preventing runtime errors and facilitating a seamless learning experience. By automating the dependency management process, the notebook reduces setup time and allows practitioners to focus on the core cybersecurity concepts being taught.

Discussion question: How does automating dependency management improve the usability of educational notebooks in cybersecurity?

Scanner Architecture and Protocol Security Slide Module

This compact module adds the requested architecture slides: three vulnerability-scanner design slides, one protocol-security slide, and one architecture slide for each major Chapter 3 term. These slides are intentionally placed near the front so they can be used as conceptual framing before walking through the executable notebook.

Vulnerability Scanner Design, Slide 1: Evidence Engine

A doctoral-grade scanner should be designed as an evidence engine, not as a CVE lookup wrapper. Its core job is to convert observations into defensible findings with confidence, provenance, and actionability. Discovery identifies the assets and attack surface. Evidence collection gathers banners, packages, SBOMs, passive fingerprints, firmware artifacts, and authenticated state. Normalization converts messy observations into identifiers such as CPE, PURL, package coordinates, model numbers, and firmware versions. Detection engines then correlate, prove, or falsify risk claims. The output is not merely ā€œvulnerableā€; it is an explained judgment.

Definitions

  • Evidence: An observed fact with source, timestamp, method, and confidence.
  • Finding: A risk claim derived from evidence, intelligence, and applicability logic.
  • Confidence: The probability that the finding applies to this asset under current evidence.
Vulnerability Scanner Design, Slide 1: Evidence Engine

Vulnerability Scanner Design, Slide 2: Control Plane and Data Plane

The scanner architecture should separate control-plane decisions from data-plane collection. The control plane owns scope, policy, scheduling, credential access, plugin selection, rate limits, and safety rules. The data plane performs the actual observations through active probes, authenticated collectors, passive sensors, agents, firmware analyzers, and cloud connectors. This separation matters because IoT, OT, cloud, and enterprise endpoints tolerate different forms of inspection. A mature scanner therefore has a policy engine before it has a packet engine.

Definitions

  • Control plane: The orchestration layer that decides what may be scanned, when, how, and with which credentials.
  • Data plane: The collectors and probes that gather technical evidence from assets.
  • Plugin/feed system: The versioned detection logic and vulnerability intelligence used by the engines.
Vulnerability Scanner Design, Slide 2: Control Plane and Data Plane

Vulnerability Scanner Design, Slide 3: Detection Engines and Safety Gates

A serious scanner composes multiple detection engines. Version correlation is useful but weak when vendors backport patches. Authenticated package inspection is stronger. Proof checks can be strong but must be safe. Template scanners are fast but depend on template quality. Default-credential checks require strict authorization. Firmware and protocol engines reach embedded and unknown-vulnerability territory. Unknown vulnerabilities are approached through invariant falsification: detecting events that should be impossible under the security model. Every engine must pass safety gates before touching a target.

Definitions

  • Safety gate: A pre-execution control that enforces authorization, rate limits, protocol risk, and non-destructive behavior.
  • Applicability logic: Rules that decide whether a vulnerability actually applies to the observed asset.
  • Invariant falsification: Finding a violation of a property that should always hold, such as auth-before-admin or valid length-before-copy.
Vulnerability Scanner Design, Slide 3: Detection Engines and Safety Gates

Protocol Security and Detective Analytics

Protocol security is the discipline of ensuring that communicating systems preserve confidentiality, integrity, authentication, authorization, availability, and safe parsing across message exchanges. In detective analytics, protocol security gives the scanner a behavioral model: what messages are legal, which state transitions are allowed, which fields control length or identity, and what effects are forbidden. This turns raw packets into security evidence. A protocol anomaly is not automatically a vulnerability, but an accepted impossible message, an invalid state transition, or a parser crash is a high-value investigative signal.

Definitions

  • Protocol: A rule system for message format, ordering, semantics, and error handling between communicating parties.
  • Protocol grammar: The formal or inferred structure of valid messages: fields, lengths, delimiters, encodings, and checksums.
  • State machine: The legal sequence of protocol states, such as unauthenticated, authenticated, command, transfer, and close.
  • Detective analytics role: Transform protocol observations into evidence of exposure, misuse, anomaly, exploit precondition, or exploit effect.
Protocol Security and Detective Analytics

Architecture Term: CPE-to-CVE Pipeline

The CPE-to-CVE pipeline maps observed product evidence to vulnerability records. CPE, or Common Platform Enumeration, is a structured naming scheme for vendor, product, version, edition, and target attributes. The pipeline is fragile because real evidence is messy: banners omit patch status, distributions backport fixes, and vendors fork components. A strong pipeline assigns confidence to each CPE candidate and applies version-range and advisory logic before creating a finding.

Definitions

  • CPE: A standardized product identifier such as vendor, product, version, update, edition, and target software/hardware.
  • CVE: A public identifier for a disclosed vulnerability, not proof that a specific asset is affected.
  • Backport: A downstream patch applied without changing the upstream-looking version string.
Architecture Term: CPE-to-CVE Pipeline

Architecture Term: OpenVAS/GVM Integration

OpenVAS/GVM integration means automating Greenbone-style scanning through the manager, scanner, feed, and report layers. GVM manages targets, credentials, tasks, schedules, results, and reports. OpenVAS scanner executes vulnerability tests, many of which are NASL-based. GMP is the management protocol automation clients use. The architecture is useful because it separates orchestration from scanning and makes findings exportable into a broader evidence pipeline.

Definitions

  • GVM: Greenbone Vulnerability Management, the orchestration and management layer.
  • OpenVAS scanner: The scanning engine that executes vulnerability tests against targets.
  • NASL/VT: Nessus Attack Scripting Language vulnerability tests used to encode detection logic.
  • GMP: Greenbone Management Protocol, an XML-based automation interface.
Architecture Term: OpenVAS/GVM Integration

Architecture Term: Nuclei Template Scanning

Nuclei template scanning is a fast signature-and-workflow execution model. A YAML template declares metadata, request logic, matchers, extractors, severity, tags, and classification fields. The engine expands the template against an authorized target, evaluates responses, and emits structured output. It is powerful for known web and protocol checks, but its quality depends on template correctness, target authorization, safe payloads, and false-positive controls.

Definitions

  • Template: A YAML file containing request definitions and detection logic.
  • Matcher: A condition that decides whether a response indicates a match.
  • Extractor: Logic that pulls evidence such as version strings or tokens from a response.
  • JSONL finding: One structured result per line, suitable for pipelines.
Architecture Term: Nuclei Template Scanning

Architecture Term: Default Credential Checking

Default credential checking validates whether a device or service still accepts vendor-known credentials. It is not brute forcing. A safe implementation fingerprints the product, selects a very small candidate list, enforces scope, rate limits attempts, avoids lockouts, and proves success through a low-impact authenticated endpoint. The finding should record that authentication succeeded without exposing the password in downstream reports.

Definitions

  • Default credential: A vendor-shipped or commonly deployed username/password pair.
  • Scope allowlist: The explicit set of targets where credential validation is authorized.
  • Proof endpoint: A low-impact authenticated request used to confirm access without changing state.
Architecture Term: Default Credential Checking

Architecture Term: Firmware Integrity

Firmware integrity asks whether the device image, boot chain, filesystem, and components match an expected vendor or organizational state. Hash verification is the simplest form. Stronger approaches add signed manifests, secure boot, filesystem extraction, package/SBOM correlation, entropy analysis, and runtime attestation. This matters because many IoT vulnerabilities live below the application banner: bootloaders, kernels, drivers, embedded libraries, and management daemons.

Definitions

  • Firmware image: The binary software bundle that runs an embedded device.
  • Manifest: A vendor or owner record of expected hashes, versions, signatures, or components.
  • Entropy analysis: A byte-distribution technique used to locate compressed, encrypted, sparse, or unusual regions.
  • Secure boot: A boot process that verifies each stage before execution.
Architecture Term: Firmware Integrity

Architecture Term: Protocol Grammar Inference

Protocol grammar inference learns message structure from captures or examples. It identifies fixed headers, length fields, command bytes, delimiters, checksums, encodings, and state-dependent payloads. The goal is not merely parsing; it is building a model that distinguishes valid, invalid, rare, and impossible messages. This model improves passive detection and guides safer fuzzing because mutations can preserve structure while testing semantic boundaries.

Definitions

  • Grammar inference: Deriving message structure and constraints from observed examples.
  • Field alignment: Comparing byte positions across messages to identify stable and variable regions.
  • Semantic boundary: A value or transition that is structurally valid but security-relevant, such as max length or admin command.
Architecture Term: Protocol Grammar Inference

Architecture Term: LLM-Guided Mutation Fuzzing

LLM-guided mutation fuzzing uses a model to propose semantically meaningful input changes based on protocol context and execution feedback. The LLM should not directly attack targets. It should operate inside a harness that enforces scope, rate limits, reproducibility, and crash capture. The useful pattern is closed-loop: seed input, mutate, execute locally, observe parser state or crash behavior, summarize feedback, and choose the next mutation strategy.

Definitions

  • Mutation fuzzing: Testing by modifying inputs to explore new execution states or failures.
  • Harness: A controlled execution wrapper that feeds inputs to the target and records results.
  • Coverage feedback: Signals showing which code paths or parser states were reached.
  • Semantic mutator: A mutator that changes fields according to protocol meaning, not random bytes only.
Architecture Term: LLM-Guided Mutation Fuzzing
InĀ [2]:
import base64
import csv
import hashlib
import http.server
import io
import json
import math
import os
import random
import re
import shutil
import socket
import socketserver
import struct
import subprocess
import tempfile
import threading
import time
import urllib.parse
import xml.etree.ElementTree as ET
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import pandas as pd
import requests
import yaml
from bs4 import BeautifulSoup
from IPython.display import Markdown, display
from sklearn.cluster import KMeans
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, silhouette_score
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC

pd.set_option("display.max_columns", 30)
pd.set_option("display.max_colwidth", 140)
random.seed(7)
np.random.seed(7)

BASE = Path.home() / "Downloads" / "seas8414_ch03_master_teaching"
DATA = BASE / "data"
CACHE = BASE / "cache"
OUT = BASE / "outputs"
for directory in (DATA, CACHE, OUT):
    directory.mkdir(parents=True, exist_ok=True)

ENABLE_LIVE_OPENAI_CALLS = os.environ.get("ENABLE_LIVE_OPENAI_CALLS", "false").lower() == "true"
LAB_ALLOW_EXTERNAL_TARGETS = False
NVD_API_KEY = os.environ.get("NVD_API_KEY", "")

print(f"Working directory: {BASE}")
print(f"tshark: {shutil.which('tshark')}")
print(f"nuclei: {shutil.which('nuclei')}")
print(f"Live OpenAI calls enabled: {ENABLE_LIVE_OPENAI_CALLS}")
print(f"External scanner targets enabled: {LAB_ALLOW_EXTERNAL_TARGETS}")
Working directory: notebook-runtime/seas8414_ch03_master_teaching
tshark: /opt/homebrew/bin/tshark
nuclei: /opt/homebrew/bin/nuclei
Live OpenAI calls enabled: False
External scanner targets enabled: False

Audio explanation for original cell 05

Transcript: This code cell imports the analytics, network, plotting, parsing, and machine-learning libraries, then defines output directories and safety flags. It teaches that scanner demonstrations should default to local-only execution unless external scope is explicitly authorized.

Plotly visual analytics

This Plotly cell adds an interactive visual analytics view for the preceding evidence section.

InĀ [3]:
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
pio.renderers.default = "notebook_connected"
print("Plotly visual analytics enabled.")
Plotly visual analytics enabled.

AI mentor analysis for original cell 05: Importing Libraries and Setting Up Environment

This code cell imports a comprehensive set of Python libraries and sets up the environment for the notebook. It includes libraries for data manipulation, machine learning, visualization, and network analysis. The cell also configures directories for data, cache, and outputs, ensuring organized storage of resources. Environment variables control the use of live OpenAI calls and external targets, maintaining a controlled and secure execution environment. This setup is essential for executing the various analytical tasks covered in the notebook.

Discussion question: Why is it important to configure environment variables and directories in a cybersecurity notebook?

Mermaid concept diagram before original cell 06: Source and Cache Contract

Use this diagram to explain why reliable vulnerability analytics needs source control, caching, and evidence preservation.

Mermaid concept diagram before original cell 06: Source and Cache Contract

0.1 Shared Dataset Utilities¶

The utility layer below is part of the lesson. Production scanners have to solve the same basic problems:

  • Respect rate limits and authentication requirements.
  • Cache source data for repeatability.
  • Preserve raw evidence so a finding can be audited later.
  • Parse nested security data without flattening away the details that matter.

NVD enforces rate limits. This notebook adds a conservative delay when no NVD_API_KEY is provided.

Audio explanation for original cell 06

Transcript: This markdown cell introduces the shared data utility layer. It teaches that production scanners must cache evidence, respect source rate limits, preserve raw data, and parse nested vulnerability records without losing audit context.

AI mentor analysis for original cell 06: Shared Dataset Utilities Explanation

This markdown cell describes the utility layer used for handling datasets in the notebook. It highlights key challenges faced by production scanners, such as respecting rate limits, caching data, preserving raw evidence, and parsing complex security data. The notebook addresses these challenges by implementing caching and rate-limiting strategies, particularly for the NVD API. This utility layer ensures that data retrieval is efficient and reliable, allowing practitioners to focus on analyzing and interpreting security evidence.

Discussion question: What are the advantages of implementing caching and rate-limiting strategies in cybersecurity data retrieval?

InĀ [4]:
SOURCE_EVENTS: list[dict[str, Any]] = []
_LAST_NVD_REQUEST = 0.0


def _cache_key(url: str, params: dict[str, Any] | None = None) -> str:
    payload = json.dumps({"url": url, "params": params or {}}, sort_keys=True)
    return hashlib.sha256(payload.encode("utf-8")).hexdigest()


def get_json(url: str, params: dict[str, Any] | None = None, *, nvd: bool = False, cache_seconds: int = 24 * 3600) -> dict[str, Any]:
    """Fetch JSON with disk caching and conservative NVD rate limiting."""
    global _LAST_NVD_REQUEST
    key = _cache_key(url, params)
    cache_path = CACHE / f"{key}.json"
    if cache_path.exists() and time.time() - cache_path.stat().st_mtime < cache_seconds:
        data = json.loads(cache_path.read_text())
        SOURCE_EVENTS.append({"url": url, "params": params or {}, "source": "cache"})
        return data

    if nvd:
        delay = 1.2 if NVD_API_KEY else 6.2
        elapsed = time.time() - _LAST_NVD_REQUEST
        if elapsed < delay:
            time.sleep(delay - elapsed)

    headers = {"User-Agent": "SEAS-8414-teaching-notebook/1.0"}
    if nvd and NVD_API_KEY:
        headers["apiKey"] = NVD_API_KEY

    response = requests.get(url, params=params, headers=headers, timeout=60)
    if nvd:
        _LAST_NVD_REQUEST = time.time()
    response.raise_for_status()
    data = response.json()
    cache_path.write_text(json.dumps(data, indent=2))
    SOURCE_EVENTS.append({"url": response.url, "params": params or {}, "source": "network"})
    return data


def get_bytes(url: str, name: str, *, cache_seconds: int = 7 * 24 * 3600) -> bytes:
    cache_path = DATA / name
    if cache_path.exists() and time.time() - cache_path.stat().st_mtime < cache_seconds:
        SOURCE_EVENTS.append({"url": url, "file": str(cache_path), "source": "cache"})
        return cache_path.read_bytes()
    response = requests.get(url, headers={"User-Agent": "SEAS-8414-teaching-notebook/1.0"}, timeout=120)
    response.raise_for_status()
    cache_path.write_bytes(response.content)
    SOURCE_EVENTS.append({"url": response.url, "file": str(cache_path), "source": "network"})
    return response.content


def extract_cpes(configurations: list[dict[str, Any]]) -> list[str]:
    cpes: list[str] = []

    def walk_node(node: dict[str, Any]) -> None:
        for match in node.get("cpeMatch", []) or []:
            criteria = match.get("criteria")
            if criteria:
                cpes.append(criteria)
        for child in node.get("children", []) or []:
            walk_node(child)

    for config in configurations or []:
        for node in config.get("nodes", []) or []:
            walk_node(node)
    return sorted(set(cpes))


def flatten_nvd_item(item: dict[str, Any]) -> dict[str, Any]:
    cve = item.get("cve", {})
    descriptions = cve.get("descriptions", [])
    description = next((d.get("value", "") for d in descriptions if d.get("lang") == "en"), "")
    metrics = cve.get("metrics", {}) or {}
    metric_record = None
    metric_key = None
    for candidate in ("cvssMetricV31", "cvssMetricV30", "cvssMetricV2"):
        if metrics.get(candidate):
            metric_record = metrics[candidate][0]
            metric_key = candidate
            break
    cvss = (metric_record or {}).get("cvssData", {})
    cpes = extract_cpes(cve.get("configurations", []))
    return {
        "cve_id": cve.get("id"),
        "published": cve.get("published"),
        "last_modified": cve.get("lastModified"),
        "description": description,
        "metric_key": metric_key,
        "base_score": cvss.get("baseScore"),
        "base_severity": (metric_record or {}).get("baseSeverity") or cvss.get("baseSeverity"),
        "cvss_vector": cvss.get("vectorString"),
        "affected_cpe_count": len(cpes),
        "affected_cpes_sample": cpes[:5],
        "weaknesses": [d.get("value") for w in cve.get("weaknesses", []) for d in w.get("description", []) if d.get("lang") == "en"],
    }


def show_source_events(limit: int = 10) -> pd.DataFrame:
    return pd.DataFrame(SOURCE_EVENTS[-limit:])

Audio explanation for original cell 07

Transcript: This utility code implements cached JSON and byte downloads, conservative NVD pacing, CPE extraction, and NVD CVE flattening. It teaches the plumbing behind reliable vulnerability evidence collection and repeatable classroom execution.

AI mentor analysis for original cell 07: Utility Functions for Data Retrieval and Caching

This code cell defines utility functions for fetching JSON data and bytes with disk caching and rate limiting. The `get_json` function handles API requests, caching responses to minimize redundant network calls and applying delays to respect NVD rate limits. The `get_bytes` function retrieves binary data, also utilizing caching. These functions ensure efficient data access and compliance with API usage policies, which is crucial for maintaining the integrity and reliability of the notebook's analytical processes.

Discussion question: How does caching contribute to the efficiency and reliability of data retrieval in cybersecurity analysis?

Mermaid concept diagram before original cell 08: Detective Analytics Pipeline

This diagram frames vulnerability scanning as evidence-backed reasoning instead of magic tool output.

Mermaid concept diagram before original cell 08: Detective Analytics Pipeline

1. Detective Analytics for Vulnerability Assessment¶

Concept¶

Detective analytics turns raw observations into evidence-backed security judgments. In vulnerability management, an observation might be a banner string, a package version, a protocol field, a firmware hash, a passive network fingerprint, an authenticated package inventory, or a crash signal from a fuzzing loop.

A vulnerability scanner does not magically "know" that a system is vulnerable. It usually executes one or more of these internal methods:

  • Version and package correlation: map installed software, CPEs, package manager metadata, or firmware versions to CVEs.
  • Configuration checks: compare observed settings to hardening rules or known insecure defaults.
  • Proof checks: send a safe request that reveals a vulnerable behavior without exploiting impact.
  • Patch presence checks: inspect local package state, registry state, kernel module versions, or vendor advisory identifiers.
  • Behavioral or anomaly checks: identify suspicious protocol behavior, entropy changes, traffic clusters, exploit preconditions, or unusual response timing.

The stack depth matters. A kernel vulnerability may require kernel build, module, syscall, driver, or interrupt-handler evidence. An embedded firmware issue may require bootloader, kernel image, filesystem, package manifest, and hardware target context. An application CVE may be detectable from an HTTP header, but that is the weakest evidence class.

Zero-days and APT tradecraft break pure signature logic. Modern detection combines signatures, exploit intelligence, anomaly models, reachability analysis, SBOM/package inventories, and environmental context.

Audio explanation for original cell 08

Transcript: This detective analytics concept cell explains how scanners turn observations into findings. It teaches the difference between version correlation, configuration checks, proof checks, patch evidence, behavioral analytics, and deeper firmware or kernel evidence.

AI mentor analysis for original cell 08: Detective Analytics for Vulnerability Assessment

This markdown cell introduces the concept of detective analytics in vulnerability assessment. It explains how raw observations are transformed into evidence-backed security judgments through various methods, such as version correlation, configuration checks, and behavioral analysis. The cell emphasizes the importance of understanding the stack depth and context when assessing vulnerabilities. This foundational knowledge is critical for practitioners to accurately identify and prioritize vulnerabilities in complex systems.

Discussion question: Why is it important to consider stack depth and context in vulnerability assessment?

InĀ [5]:
CISA_KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
kev_json = get_json(CISA_KEV_URL, cache_seconds=12 * 3600)
kev_df = pd.DataFrame(kev_json.get("vulnerabilities", []))
kev_df["dateAdded"] = pd.to_datetime(kev_df["dateAdded"], errors="coerce")
kev_df["dueDate"] = pd.to_datetime(kev_df["dueDate"], errors="coerce")
print(f"CISA KEV records loaded: {len(kev_df):,}")
display(kev_df[["cveID", "vendorProject", "product", "vulnerabilityName", "dateAdded", "knownRansomwareCampaignUse"]].tail(8))
CISA KEV records loaded: 1,606
cveID vendorProject product vulnerabilityName dateAdded knownRansomwareCampaignUse
1598 CVE-2020-25213 WordPress File Manager Plugin WordPress File Manager Plugin Remote Code Execution Vulnerability 2021-11-03 Unknown
1599 CVE-2020-11738 WordPress Snap Creek Duplicator Plugin WordPress Snap Creek Duplicator Plugin File Download Vulnerability 2021-11-03 Unknown
1600 CVE-2019-9978 WordPress Social Warfare Plugin WordPress Social Warfare Plugin Cross-Site Scripting (XSS) Vulnerability 2021-11-03 Unknown
1601 CVE-2021-27561 Yealink Device Management Yealink Device Management Server-Side Request Forgery (SSRF) Vulnerability 2021-11-03 Unknown
1602 CVE-2021-40539 Zoho ManageEngine Zoho ManageEngine ADSelfService Plus Authentication Bypass Vulnerability 2021-11-03 Known
1603 CVE-2020-10189 Zoho ManageEngine Zoho ManageEngine Desktop Central File Upload Vulnerability 2021-11-03 Unknown
1604 CVE-2019-8394 Zoho ManageEngine Zoho ManageEngine ServiceDesk Plus (SDP) File Upload Vulnerability 2021-11-03 Unknown
1605 CVE-2020-29583 Zyxel Multiple Products Zyxel Multiple Products Use of Hard-Coded Credentials Vulnerability 2021-11-03 Unknown

Audio explanation for original cell 09

Transcript: This code cell loads the live CISA Known Exploited Vulnerabilities catalog and displays recent exploited CVEs. It teaches that KEV is exploitation intelligence, not scanner output, and that it should enrich prioritization.

AI mentor analysis for original cell 09: Loading CISA Known Exploited Vulnerabilities

This code cell retrieves and processes the CISA Known Exploited Vulnerabilities (KEV) dataset. It uses the `get_json` utility function to fetch the data and loads it into a Pandas DataFrame for analysis. The cell converts date fields to datetime objects and displays a sample of the dataset, providing insights into recent vulnerabilities and their exploitation status. This exercise demonstrates how to handle and analyze real-world vulnerability data, a crucial skill for cybersecurity practitioners.

Discussion question: What insights can be gained from analyzing the CISA Known Exploited Vulnerabilities dataset?

InĀ [6]:
kev_vendor_summary = (
    kev_df.groupby("vendorProject", dropna=False)
    .agg(cve_count=("cveID", "count"), latest_added=("dateAdded", "max"))
    .sort_values(["cve_count", "latest_added"], ascending=[False, False])
    .head(12)
)
print("Top vendors/projects by KEV catalog count:")
display(kev_vendor_summary)

kev_ransomware_counts = kev_df["knownRansomwareCampaignUse"].fillna("Unknown").value_counts().rename_axis("known_ransomware_use").to_frame("count")
display(kev_ransomware_counts)
Top vendors/projects by KEV catalog count:
cve_count latest_added
vendorProject
Microsoft 377 2026-05-20
Apple 93 2026-03-20
Cisco 90 2026-05-14
Adobe 79 2026-05-20
Google 71 2026-04-01
Oracle 42 2025-11-21
Apache 39 2026-04-16
Ivanti 34 2026-05-07
D-Link 26 2026-04-24
Fortinet 26 2026-04-13
VMware 26 2025-03-04
Linux 25 2026-05-01
count
known_ransomware_use
Unknown 1281
Known 325

Audio explanation for original cell 10

Transcript: This code summarizes KEV by vendor and ransomware usage. It teaches how raw threat-intelligence records become operational views that help prioritize remediation and explain exposure to decision makers.

AI mentor analysis for original cell 10: Analyzing Vendor and Ransomware Data from KEV

This code cell performs aggregation and analysis on the KEV dataset to summarize vulnerabilities by vendor and assess known ransomware campaign usage. It groups data by vendor, counts CVEs, and identifies the latest additions, highlighting vendors with the most entries. It also categorizes vulnerabilities by their association with ransomware, providing a count of known and unknown cases. This analysis helps practitioners understand the distribution of vulnerabilities across vendors and the potential ransomware risks, aiding in prioritization and response planning.

Discussion question: How can analyzing vendor-specific vulnerability data inform security strategy and risk management?

Plotly visual analytics

This Plotly cell visualizes KEV concentration and ransomware-use mix so students can see exploitation intelligence as a distribution, not only a table.

InĀ [7]:
# Plotly visual analytics: KEV vendor concentration and ransomware-use mix.
top_kev_vendors = kev_vendor_summary.reset_index().head(12)
fig = px.bar(
    top_kev_vendors,
    x="cve_count",
    y="vendorProject",
    orientation="h",
    color="cve_count",
    title="CISA KEV Concentration by Vendor/Project",
    labels={"cve_count": "KEV CVE count", "vendorProject": "Vendor/project"},
)
fig.update_layout(yaxis={"categoryorder": "total ascending"}, height=520)
fig.show()
ransomware_mix = kev_ransomware_counts.reset_index()
fig = px.pie(
    ransomware_mix,
    names="known_ransomware_use",
    values="count",
    title="KEV Records by Known Ransomware Campaign Use",
    hole=0.45,
)
fig.show()

What the KEV cell teaches¶

CISA KEV is not a vulnerability scanner. It is an exploited-in-the-wild signal. A mature detector uses KEV as a prioritization feature, not as the sole detection method. The internal workflow is:

  1. Discover asset and software evidence.
  2. Map evidence to a vulnerability identifier.
  3. Enrich the identifier with exploitation signals such as KEV and EPSS.
  4. Add reachability, exposure, business criticality, and compensating controls.
  5. Produce an action decision.

This is the detective-analytics pattern repeated throughout the rest of the notebook.

Audio explanation for original cell 11

Transcript: This interpretation cell explains why KEV must be fused with asset and CVE evidence. It teaches the detective analytics sequence: discover evidence, map to identifiers, enrich with intelligence, add asset context, and decide action.

AI mentor analysis for original cell 11: Understanding the KEV Cell

This cell explains the role of CISA's Known Exploited Vulnerabilities (KEV) list in vulnerability management. KEV is not a scanner but a signal indicating active exploitation. It is used to prioritize vulnerabilities by integrating it with other factors like asset discovery, vulnerability mapping, and exploitation signals. The workflow described is a detective-analytics pattern, emphasizing the importance of combining multiple data sources and signals to make informed action decisions. This approach highlights the need for a comprehensive view of vulnerabilities beyond just detection.

Discussion question: How can KEV signals be effectively integrated into existing vulnerability management workflows?

Mermaid concept diagram before original cell 12: CPE to CVE Correlation

Use this diagram before the NVD CPE cells to show the internal scanner workflow.

Mermaid concept diagram before original cell 12: CPE to CVE Correlation

2. CPE-to-CVE Pipeline¶

Concept¶

CPE-to-CVE correlation is the backbone of traditional vulnerability scanning. The internal pipeline usually looks like this:

  1. Collect evidence: banner, package inventory, authenticated command output, SBOM, firmware manifest, or passive fingerprint.
  2. Normalize evidence: vendor names, product names, major/minor versions, editions, update tags, and target hardware.
  3. Resolve CPE candidates: use exact rules where possible and fuzzy matching only when evidence is weak.
  4. Query CVE records: identify CVEs whose affected configurations match the selected CPE.
  5. Interpret applicability: evaluate version ranges, platform constraints, vendor advisories, patch status, and known exceptions.
  6. Score and prioritize: combine CVSS, EPSS, KEV, exposure, exploitability, and asset value.

The failure mode is false precision. A banner such as OpenSSH_8.4 is not enough to prove vulnerability if a distribution backported a patch without changing the upstream version string. Authenticated evidence is stronger than unauthenticated banner evidence.

Audio explanation for original cell 12

Transcript: This CPE-to-CVE concept cell explains the backbone of traditional vulnerability scanning. It teaches the internal pipeline from evidence collection and normalization to CPE candidate resolution, CVE lookup, applicability checks, and prioritization.

AI mentor analysis for original cell 12: CPE-to-CVE Pipeline Mechanics

This cell outlines the CPE-to-CVE correlation process, a fundamental aspect of vulnerability scanning. It describes how evidence is collected, normalized, and resolved into CPEs, which are then used to query CVE records. The process involves evaluating applicability and prioritizing based on various factors like CVSS scores and asset value. The cell warns against false precision, emphasizing the importance of authenticated evidence over unauthenticated banner evidence. This highlights the challenges in accurately identifying vulnerabilities and the need for robust evidence collection methods.

Discussion question: What are the limitations of using unauthenticated evidence in vulnerability scanning?

Mermaid concept diagram before original cell 13: CPE Candidate Resolution

This diagram explains the specific API lookup performed by the next code cell.

Mermaid concept diagram before original cell 13: CPE Candidate Resolution
InĀ [8]:
NVD_CPE_URL = "https://services.nvd.nist.gov/rest/json/cpes/2.0"
NVD_CVE_URL = "https://services.nvd.nist.gov/rest/json/cves/2.0"


def search_cpes(keyword: str, limit: int = 8) -> pd.DataFrame:
    data = get_json(NVD_CPE_URL, params={"keywordSearch": keyword, "resultsPerPage": limit}, nvd=True)
    rows = []
    for product in data.get("products", []):
        cpe = product.get("cpe", {})
        rows.append({
            "cpe_name": cpe.get("cpeName"),
            "title": next((t.get("title") for t in cpe.get("titles", []) if t.get("lang") == "en"), None),
            "deprecated": cpe.get("deprecated"),
            "last_modified": cpe.get("lastModified"),
        })
    return pd.DataFrame(rows)

openssh_cpes = search_cpes("OpenSSH 8.4", limit=10)
openwrt_cpes = search_cpes("OpenWrt 23.05", limit=10)
print("OpenSSH CPE candidates:")
display(openssh_cpes)
print("OpenWrt CPE candidates:")
display(openwrt_cpes)
OpenSSH CPE candidates:
cpe_name title deprecated last_modified
0 cpe:2.3:a:openbsd:openssh:8.4:-:*:*:*:*:*:* OpenBSD OpenSSH 8.4 False 2021-02-11T16:18:59.657
1 cpe:2.3:a:openbsd:openssh:8.4:p1:*:*:*:*:*:* OpenBSD OpenSSH 8.4 p1 False 2022-08-04T15:08:27.340
OpenWrt CPE candidates:
cpe_name title deprecated last_modified
0 cpe:2.3:o:openwrt:openwrt:23.05.0:-:*:*:*:*:*:* OpenWrt 23.05.0 False 2025-10-28T20:10:46.350
1 cpe:2.3:o:openwrt:openwrt:23.05.0:rc1:*:*:*:*:*:* OpenWrt 23.05.0 Release Candidate 1 False 2025-10-28T20:10:46.350
2 cpe:2.3:o:openwrt:openwrt:23.05.0:rc2:*:*:*:*:*:* OpenWrt 23.05.0 Release Candidate 2 False 2025-10-28T20:10:46.350
3 cpe:2.3:o:openwrt:openwrt:23.05.0:rc3:*:*:*:*:*:* OpenWrt 23.05.0 Release Candidate 3 False 2025-10-28T20:10:46.350
4 cpe:2.3:o:openwrt:openwrt:23.05.0:rc4:*:*:*:*:*:* OpenWrt 23.05.0 Release Candidate 4 False 2025-10-28T20:10:46.350
5 cpe:2.3:o:openwrt:openwrt:23.05.1:*:*:*:*:*:*:* OpenWrt 23.05.1 False 2025-10-28T20:10:46.350
6 cpe:2.3:o:openwrt:openwrt:23.05.2:*:*:*:*:*:*:* OpenWrt 23.05.2 False 2025-10-28T20:10:46.350
7 cpe:2.3:o:openwrt:openwrt:23.05.3:*:*:*:*:*:*:* OpenWrt 23.05.3 False 2025-10-28T20:10:46.350
8 cpe:2.3:o:openwrt:openwrt:23.05.4:*:*:*:*:*:*:* OpenWrt 23.05.4 False 2025-10-28T20:10:46.350
9 cpe:2.3:o:openwrt:openwrt:23.05.5:*:*:*:*:*:*:* OpenWrt 23.05.5 False 2025-10-28T20:10:46.350

Audio explanation for original cell 13

Transcript: This code cell searches the live NVD CPE API for OpenSSH and OpenWrt candidates. It teaches why scanners need product normalization, because a banner string has to become a precise platform identifier before CVE correlation is credible.

AI mentor analysis for original cell 13: NVD CPE Search Function

This code cell demonstrates how to query the National Vulnerability Database (NVD) for CPEs using a keyword search. It retrieves and displays CPE candidates for specific software versions, such as OpenSSH 8.4 and OpenWrt 23.05. The cell illustrates the mechanics of interacting with NVD's API to gather CPE data, which is crucial for mapping software to known vulnerabilities. This process is foundational for vulnerability management, enabling practitioners to identify potential vulnerabilities based on software inventory.

Discussion question: How does the accuracy of CPE matching impact vulnerability management?

InĀ [9]:
selected_cpe = None
if not openssh_cpes.empty:
    # Prefer the p1 CPE when it is available because many real banners expose OpenSSH_8.4p1.
    p1 = openssh_cpes[openssh_cpes["cpe_name"].str.contains("8.4:p1", regex=False, na=False)]
    selected_cpe = (p1.iloc[0] if not p1.empty else openssh_cpes.iloc[0])["cpe_name"]

if not selected_cpe:
    raise RuntimeError("No OpenSSH CPE candidate returned by NVD.")

cve_data = get_json(NVD_CVE_URL, params={"cpeName": selected_cpe, "resultsPerPage": 40}, nvd=True)
cpe_cves = pd.DataFrame([flatten_nvd_item(v) for v in cve_data.get("vulnerabilities", [])])
print(f"Selected CPE: {selected_cpe}")
print(f"NVD CVEs returned for selected CPE: {len(cpe_cves)}")
display(cpe_cves[["cve_id", "published", "base_score", "base_severity", "affected_cpe_count", "description"]].head(12))
Selected CPE: cpe:2.3:a:openbsd:openssh:8.4:p1:*:*:*:*:*:*
NVD CVEs returned for selected CPE: 17
cve_id published base_score base_severity affected_cpe_count description
0 CVE-2007-2768 2007-05-21T20:30:00.000 4.3 MEDIUM 5 OpenSSH, when using OPIE (One-Time Passwords in Everything) for PAM, allows remote attackers to determine the existence of certain user ...
1 CVE-2008-3844 2008-08-27T20:41:00.000 9.3 HIGH 6 Certain Red Hat Enterprise Linux (RHEL) 4 and 5 packages for OpenSSH, as signed in August 2008 using a legitimate Red Hat GPG key, conta...
2 CVE-2021-28041 2021-03-05T21:15:13.200 7.1 HIGH 12 ssh-agent in OpenSSH before 8.5 has a double free that may be relevant in a few less-common scenarios, such as unconstrained agent-socke...
3 CVE-2016-20012 2021-09-15T20:15:07.310 5.3 MEDIUM 5 OpenSSH through 8.7 allows remote attackers, who have a suspicion that a certain combination of username and public key is known to an S...
4 CVE-2021-41617 2021-09-26T19:15:07.263 7.0 HIGH 18 sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supple...
5 CVE-2021-36368 2022-03-13T00:15:07.937 3.7 LOW 4 An issue was discovered in OpenSSH before 8.9. If a client is using public-key authentication with agent forwarding but without -oLogLev...
6 CVE-2023-38408 2023-07-20T03:15:10.170 9.8 CRITICAL 5 The PKCS#11 feature in ssh-agent in OpenSSH before 9.3p2 has an insufficiently trustworthy search path, leading to remote code execution...
7 CVE-2023-48795 2023-12-18T16:15:10.897 5.9 MEDIUM 76 The SSH transport protocol with certain OpenSSH extensions, found in OpenSSH before 9.6 and other products, allows remote attackers to b...
8 CVE-2023-51385 2023-12-18T19:15:08.773 6.5 MEDIUM 4 In ssh in OpenSSH before 9.6, OS command injection might occur if a user name or host name has shell metacharacters, and this name is re...
9 CVE-2023-51767 2023-12-24T07:15:07.410 7.0 HIGH 4 OpenSSH through 10.0, when common types of DRAM are used, might allow row hammer attacks (for authentication bypass) because the integer...
10 CVE-2025-26465 2025-02-18T19:15:29.230 6.8 MEDIUM 10 A vulnerability was found in OpenSSH when the VerifyHostKeyDNS option is enabled. A machine-in-the-middle attack can be performed by a m...
11 CVE-2025-32728 2025-04-10T02:15:30.873 4.3 MEDIUM 2 In sshd in OpenSSH before 10.0, the DisableForwarding directive does not adhere to the documentation stating that it disables X11 and ag...

Audio explanation for original cell 14

Transcript: This code selects an OpenSSH CPE and queries the NVD CVE API for matching vulnerabilities. It teaches how a resolved CPE becomes a concrete CVE set, while also warning that banner-based evidence can still overstate applicability.

AI mentor analysis for original cell 14: Selecting and Querying CVEs for a CPE

This code cell selects a specific CPE, preferring the 'p1' version of OpenSSH 8.4, and queries the NVD for associated CVEs. It demonstrates the importance of selecting the most accurate CPE to ensure relevant CVEs are identified. The cell highlights the process of retrieving CVE data, including details like CVE ID, publication date, and severity. This step is critical for understanding the vulnerabilities affecting a specific software version and prioritizing remediation efforts.

Discussion question: Why is it important to select the most accurate CPE when querying for CVEs?

InĀ [10]:
def parse_cvss_vector(vector: str | None) -> dict[str, str]:
    if not vector:
        return {}
    parts = vector.split("/")
    parsed = {"version": parts[0]}
    for part in parts[1:]:
        if ":" in part:
            key, value = part.split(":", 1)
            parsed[key] = value
    return parsed

cvss_rows = []
for _, row in cpe_cves.dropna(subset=["cvss_vector"]).head(12).iterrows():
    parsed = parse_cvss_vector(row["cvss_vector"])
    parsed["cve_id"] = row["cve_id"]
    parsed["base_score"] = row["base_score"]
    parsed["base_severity"] = row["base_severity"]
    cvss_rows.append(parsed)

cvss_df = pd.DataFrame(cvss_rows)
print("CVSS is a structured vector, not just a number. AV/AC/PR/UI/S/C/I/A encode exploit path and impact.")
display(cvss_df)
CVSS is a structured vector, not just a number. AV/AC/PR/UI/S/C/I/A encode exploit path and impact.
version AC Au C I A cve_id base_score base_severity AV PR UI S
0 AV:N M N P N N CVE-2007-2768 4.3 MEDIUM NaN NaN NaN NaN
1 AV:N M N C C C CVE-2008-3844 9.3 HIGH NaN NaN NaN NaN
2 CVSS:3.1 H NaN H H H CVE-2021-28041 7.1 HIGH N L R U
3 CVSS:3.1 L NaN L N N CVE-2016-20012 5.3 MEDIUM N N N U
4 CVSS:3.1 H NaN H H H CVE-2021-41617 7.0 HIGH L L N U
5 CVSS:3.1 H NaN L N N CVE-2021-36368 3.7 LOW N N N U
6 CVSS:3.1 L NaN H H H CVE-2023-38408 9.8 CRITICAL N N N U
7 CVSS:3.1 H NaN N H N CVE-2023-48795 5.9 MEDIUM N N N U
8 CVSS:3.1 L NaN L L N CVE-2023-51385 6.5 MEDIUM N N N U
9 CVSS:3.1 H NaN H H H CVE-2023-51767 7.0 HIGH L L N U
10 CVSS:3.1 H NaN H H N CVE-2025-26465 6.8 MEDIUM N N R U
11 CVSS:3.1 L NaN N L N CVE-2025-32728 4.3 MEDIUM L N N C

Audio explanation for original cell 15

Transcript: This code parses CVSS vectors into their individual fields. It teaches that CVSS is not only a score; attack vector, complexity, privileges, user interaction, scope, and impact fields encode the exploit model.

AI mentor analysis for original cell 15: Parsing CVSS Vectors

This code cell parses CVSS vectors from CVE data to extract detailed information about the exploitability and impact of vulnerabilities. It highlights that CVSS is more than just a score; it includes structured information about attack vectors, complexity, privileges required, and impact on confidentiality, integrity, and availability. Understanding these components is crucial for accurately assessing the risk posed by a vulnerability and making informed decisions about mitigation strategies.

Discussion question: How does understanding the components of a CVSS vector improve vulnerability assessment?

Plotly visual analytics

This Plotly cell visualizes CVSS severity and vector dimensions for the selected CPE, making exploit-path assumptions easier to compare.

InĀ [11]:
# Plotly visual analytics: CVE severity and CVSS dimension profile for the selected CPE.
if not cpe_cves.empty:
    fig = px.histogram(
        cpe_cves.dropna(subset=["base_severity"]),
        x="base_severity",
        color="base_severity",
        title=f"NVD Severity Distribution for {selected_cpe}",
        labels={"base_severity": "CVSS severity"},
    )
    fig.update_layout(showlegend=False)
    fig.show()
if not cvss_df.empty:
    impact_cols = [col for col in ["AV", "AC", "PR", "UI", "S", "C", "I", "A"] if col in cvss_df.columns]
    melted_cvss = cvss_df.melt(id_vars=["cve_id"], value_vars=impact_cols, var_name="cvss_dimension", value_name="value")
    fig = px.histogram(
        melted_cvss,
        x="cvss_dimension",
        color="value",
        barmode="group",
        title="CVSS Vector Dimension Mix Across Selected CPE CVEs",
    )
    fig.show()

Interpretation: how scanners use CPE internally¶

A scanner can run this CPE pipeline in several ways:

  • Unauthenticated: infer product and version from protocol banners or HTTP metadata. Fast, broad, but weak evidence.
  • Authenticated: query package databases such as RPM, dpkg, Windows registry, kernel build metadata, container image manifests, or SBOMs. Slower, stronger evidence.
  • Passive: infer CPEs from network traffic and TLS/HTTP/industrial protocol fingerprints. Lower operational risk, but often lower version precision.
  • Hybrid: combine all three and attach an evidence confidence score to each finding.

A mature product must also handle vendor backports. For example, an enterprise Linux package may carry a vulnerable-looking upstream version string but include a downstream patch. That is why package advisory metadata often outranks naive banner matching.

Audio explanation for original cell 16

Transcript: This interpretation cell compares unauthenticated, authenticated, passive, and hybrid CPE evidence. It teaches why package advisory metadata and patch state often outrank naive service-banner matching.

AI mentor analysis for original cell 16: Internal Use of CPE by Scanners

This markdown cell explains how scanners utilize CPEs internally, detailing different methods of evidence collection: unauthenticated, authenticated, passive, and hybrid. Each method has its strengths and weaknesses, affecting the precision and reliability of vulnerability assessments. The cell emphasizes the importance of handling vendor backports, where version strings may not reflect actual vulnerability status due to downstream patches. This underscores the need for comprehensive evidence evaluation to avoid false positives and negatives.

Discussion question: What are the benefits and drawbacks of using a hybrid approach to CPE evidence collection?

Mermaid concept diagram before original cell 17: Learning Paradigms to AutoML

Use this diagram to distinguish supervised, unsupervised, reinforcement learning, and AutoML before the model-selection cells.

Mermaid concept diagram before original cell 17: Learning Paradigms to AutoML

3. AutoML for Vulnerability Triage¶

Concept¶

Before jumping to AutoML, separate the learning paradigms:

  • Supervised learning: train on labeled examples. Here, CVE descriptions are text and CVSS severity is the label.
  • Unsupervised learning: discover structure without labels. Later we cluster Nuclei templates by metadata.
  • Reinforcement learning: choose actions using rewards from environment feedback. Later we select fuzzing mutation operators based on parser-state novelty.
  • AutoML: automate part of model selection, preprocessing, or hyperparameter search. In production this might include feature pipelines, model families, calibration, drift checks, and champion/challenger promotion.

This section uses a small AutoML-style model selection loop over real NVD CVE descriptions. It is not a production severity oracle. It teaches why text features can approximate triage labels and why model selection should be measured instead of guessed.

Audio explanation for original cell 17

Transcript: This AutoML concept cell separates supervised learning, unsupervised learning, reinforcement learning, and AutoML. It teaches where machine learning fits in vulnerability triage before using it on real NVD data.

AI mentor analysis for original cell 17: Introduction to AutoML for Vulnerability Triage

This section introduces the concept of using AutoML for vulnerability triage, distinguishing between supervised, unsupervised, and reinforcement learning. It explains how AutoML can automate parts of model selection and preprocessing, which is useful for handling large datasets like NVD CVE descriptions. The goal is to approximate triage labels using text features, demonstrating the potential of machine learning in prioritizing vulnerabilities. This approach highlights the importance of measuring model performance rather than relying on intuition.

Discussion question: How can AutoML improve the efficiency of vulnerability triage in cybersecurity?

Mermaid concept diagram before original cell 18: Supervised Severity Training Set

This diagram explains how real NVD descriptions become a small teaching dataset.

Mermaid concept diagram before original cell 18: Supervised Severity Training Set
InĀ [12]:
severity_frames = []
for severity in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
    data = get_json(
        NVD_CVE_URL,
        params={"cvssV3Severity": severity, "resultsPerPage": 70},
        nvd=True,
        cache_seconds=7 * 24 * 3600,
    )
    frame = pd.DataFrame([flatten_nvd_item(v) for v in data.get("vulnerabilities", [])])
    frame = frame.dropna(subset=["description", "base_severity"])
    severity_frames.append(frame)

severity_corpus = pd.concat(severity_frames, ignore_index=True)
severity_corpus = severity_corpus.drop_duplicates("cve_id")
severity_corpus = severity_corpus[severity_corpus["base_severity"].isin(["CRITICAL", "HIGH", "MEDIUM", "LOW"])]
print(f"Training examples from NVD: {len(severity_corpus)}")
display(severity_corpus["base_severity"].value_counts().rename_axis("severity").to_frame("count"))
Training examples from NVD: 280
count
severity
CRITICAL 70
HIGH 70
MEDIUM 70
LOW 70

Audio explanation for original cell 18

Transcript: This code downloads severity-labeled CVE descriptions from NVD. It teaches supervised learning with real labels, where vulnerability descriptions are the features and CVSS severity is the training target.

AI mentor analysis for original cell 18: Building a Severity Corpus from NVD

This code cell constructs a dataset of CVE descriptions and their associated severities from the NVD, categorized into CRITICAL, HIGH, MEDIUM, and LOW. It retrieves data for each severity level and ensures the corpus is free of duplicates. This dataset serves as the foundation for training machine learning models to predict vulnerability severity. By using real-world data, the cell emphasizes the importance of having a balanced and representative dataset for training effective models.

Discussion question: Why is it important to have a balanced dataset when training models for severity prediction?

InĀ [13]:
model_candidates = {
    "word_tfidf_logreg": Pipeline([
        ("tfidf", TfidfVectorizer(ngram_range=(1, 2), min_df=2, max_features=6000)),
        ("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
    ]),
    "word_tfidf_linear_svc": Pipeline([
        ("tfidf", TfidfVectorizer(ngram_range=(1, 2), min_df=2, max_features=6000)),
        ("clf", LinearSVC(class_weight="balanced")),
    ]),
    "word_tfidf_nb": Pipeline([
        ("tfidf", TfidfVectorizer(ngram_range=(1, 2), min_df=2, max_features=6000)),
        ("clf", MultinomialNB()),
    ]),
    "char_tfidf_logreg": Pipeline([
        ("tfidf", TfidfVectorizer(analyzer="char_wb", ngram_range=(3, 5), min_df=2, max_features=8000)),
        ("clf", LogisticRegression(max_iter=1000, class_weight="balanced")),
    ]),
}

X = severity_corpus["description"].fillna("").astype(str)
y = severity_corpus["base_severity"].astype(str)
cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=7)

scores = []
for name, pipe in model_candidates.items():
    macro_f1 = cross_val_score(pipe, X, y, cv=cv, scoring="f1_macro")
    scores.append({"model": name, "macro_f1_mean": macro_f1.mean(), "macro_f1_std": macro_f1.std()})

scores_df = pd.DataFrame(scores).sort_values("macro_f1_mean", ascending=False)
best_model_name = scores_df.iloc[0]["model"]
best_model = model_candidates[best_model_name]
print("AutoML-style candidate ranking:")
display(scores_df)
print(f"Selected model: {best_model_name}")
AutoML-style candidate ranking:
model macro_f1_mean macro_f1_std
1 word_tfidf_linear_svc 0.719955 0.039877
0 word_tfidf_logreg 0.689548 0.048987
3 char_tfidf_logreg 0.666445 0.005907
2 word_tfidf_nb 0.657928 0.045354
Selected model: word_tfidf_linear_svc

Audio explanation for original cell 19

Transcript: This code compares several text classification pipelines with cross-validated macro F1. It teaches AutoML in miniature: do not guess the model family, measure candidate pipelines and select the best performer.

AI mentor analysis for original cell 19: AutoML-style Model Selection for Severity Prediction

This code cell implements an AutoML-style approach to model selection, evaluating different text classification pipelines using cross-validation. It compares models based on their macro F1 scores, selecting the best-performing one for predicting CVE severity. The cell demonstrates the process of evaluating multiple models to identify the most suitable one for a given task, highlighting the importance of empirical evaluation in machine learning. This approach helps in understanding the strengths and weaknesses of different models for text-based vulnerability triage.

Discussion question: What factors should be considered when selecting a machine learning model for text classification tasks?

InĀ [14]:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, stratify=y, random_state=7)
best_model.fit(X_train, y_train)
y_pred = best_model.predict(X_test)
print(classification_report(y_test, y_pred, zero_division=0))

example_descriptions = [
    "Unauthenticated remote attacker can execute arbitrary commands in embedded device management CGI.",
    "Local user can read a temporary file created with insecure permissions.",
    "Malformed protocol packet causes a denial of service in a daemon running as root.",
]
example_predictions = pd.DataFrame({
    "example_description": example_descriptions,
    "predicted_severity": best_model.predict(example_descriptions),
})
display(example_predictions)
              precision    recall  f1-score   support

    CRITICAL       0.74      0.78      0.76        18
        HIGH       0.71      0.59      0.65        17
         LOW       1.00      0.94      0.97        17
      MEDIUM       0.76      0.89      0.82        18

    accuracy                           0.80        70
   macro avg       0.80      0.80      0.80        70
weighted avg       0.80      0.80      0.80        70

example_description predicted_severity
0 Unauthenticated remote attacker can execute arbitrary commands in embedded device management CGI. CRITICAL
1 Local user can read a temporary file created with insecure permissions. MEDIUM
2 Malformed protocol packet causes a denial of service in a daemon running as root. MEDIUM

Audio explanation for original cell 20

Transcript: This code trains the selected model, evaluates it on a held-out split, and predicts severity for example descriptions. It teaches both the value and the limits of text-based triage models.

AI mentor analysis for original cell 20: Evaluating the Best Model for Severity Prediction

This code cell evaluates the selected model's performance on a test dataset, providing precision, recall, and F1-score metrics. It also predicts the severity of example vulnerability descriptions, demonstrating the model's practical application. The cell highlights the importance of evaluating model performance on unseen data to ensure generalizability. By providing example predictions, it illustrates how the model can be used to assist in vulnerability triage, emphasizing the role of machine learning in enhancing cybersecurity operations.

Discussion question: How can model evaluation metrics guide improvements in machine learning models for cybersecurity?

Plotly visual analytics

This Plotly cell visualizes model-selection evidence and classifier mistakes, which is the key teaching point for AutoML governance.

InĀ [15]:
# Plotly visual analytics: AutoML ranking and confusion matrix.
fig = px.bar(
    scores_df,
    x="macro_f1_mean",
    y="model",
    orientation="h",
    error_x="macro_f1_std",
    title="AutoML-Style Model Ranking by Cross-Validated Macro-F1",
    labels={"macro_f1_mean": "Macro-F1 mean", "model": "Candidate pipeline"},
)
fig.update_layout(yaxis={"categoryorder": "total ascending"}, height=420)
fig.show()
labels_sorted = sorted(y.unique())
confusion = pd.crosstab(pd.Series(y_test, name="Actual"), pd.Series(y_pred, name="Predicted"), dropna=False).reindex(index=labels_sorted, columns=labels_sorted, fill_value=0)
fig = px.imshow(
    confusion,
    text_auto=True,
    title="Held-Out Severity Classifier Confusion Matrix",
    color_continuous_scale="Blues",
)
fig.show()

Interpretation: what AutoML is and is not doing¶

The model selection cell is intentionally small but conceptually important. It automates the selection of text representation and classifier family based on cross-validated macro-F1. That is AutoML in miniature.

In production, a vulnerability platform would add calibration, explainability, class imbalance controls, tenant drift detection, model lineage, and rollback. It would not use a text model alone to override a grounded CVSS, vendor advisory, or exploitability signal. It would use ML as one feature in a detective analytics pipeline.

Audio explanation for original cell 21

Transcript: This interpretation cell reinforces that AutoML is not an oracle. It teaches that machine-learning output should support, not replace, grounded CVSS, vendor advisory, exploitability, and asset-context evidence.

AI mentor analysis for original cell 21: Understanding AutoML in Vulnerability Analysis

This cell provides a conceptual overview of AutoML's role in vulnerability analysis. It highlights that AutoML can automate model selection based on performance metrics like macro-F1, which is a simplified version of its potential in production environments. In real-world applications, AutoML would be part of a broader system that includes model calibration, explainability, and controls for class imbalance. It emphasizes that machine learning should complement, not replace, established security metrics like CVSS scores or vendor advisories, serving as one component in a comprehensive analytics pipeline.

Discussion question: How can AutoML be effectively integrated into existing vulnerability management workflows?

Mermaid concept diagram before original cell 22: Threat Intelligence Fusion

Use this diagram before the fusion cell so students understand what each risk signal contributes.

Mermaid concept diagram before original cell 22: Threat Intelligence Fusion

4. Threat Intelligence Fusion: NVD + KEV + EPSS¶

Concept¶

CVSS measures technical severity. KEV measures known exploitation. EPSS estimates exploitation probability. They answer different questions.

A detective analytics pipeline becomes more useful when it fuses these signals:

  • CVSS: how bad could exploitation be under the modeled conditions?
  • EPSS: how likely is exploitation activity in the near term?
  • KEV: has exploitation already been observed and cataloged?
  • Asset context: is the affected system exposed, critical, reachable, or compensating-controlled?

The code below enriches the OpenSSH CPE CVE set with EPSS and KEV flags.

Audio explanation for original cell 22

Transcript: This threat-intelligence fusion concept cell explains the different jobs of CVSS, EPSS, KEV, and asset context. It teaches that prioritization improves when severity, exploitation probability, known exploitation, and exposure are combined.

AI mentor analysis for original cell 22: Threat Intelligence Fusion with NVD, KEV, and EPSS

This cell discusses the integration of different threat intelligence signals—CVSS, KEV, and EPSS—into a unified analytics pipeline. Each signal provides unique insights: CVSS assesses potential severity, EPSS predicts exploitation likelihood, and KEV confirms observed exploitation. By fusing these signals, along with asset context, the pipeline can offer a more nuanced understanding of vulnerabilities. This approach enhances decision-making by considering both technical severity and real-world exploitation data, leading to more informed prioritization and mitigation strategies.

Discussion question: What are the challenges in integrating multiple threat intelligence signals into a single analytics pipeline?

InĀ [16]:
EPSS_URL = "https://api.first.org/data/v1/epss"
triage_base = cpe_cves[["cve_id", "base_score", "base_severity", "description"]].dropna(subset=["cve_id"]).copy()
triage_ids = triage_base["cve_id"].head(30).tolist()

epss_json = get_json(EPSS_URL, params={"cve": ",".join(triage_ids)}, cache_seconds=12 * 3600)
epss_df = pd.DataFrame(epss_json.get("data", []))
if not epss_df.empty:
    epss_df["epss"] = pd.to_numeric(epss_df["epss"], errors="coerce").fillna(0.0)
    epss_df["percentile"] = pd.to_numeric(epss_df["percentile"], errors="coerce").fillna(0.0)
else:
    epss_df = pd.DataFrame(columns=["cve", "epss", "percentile"])

kev_flags = kev_df[["cveID", "knownRansomwareCampaignUse", "dateAdded"]].rename(columns={"cveID": "cve_id"})
kev_flags["in_kev"] = True

kev_triage = (
    triage_base
    .merge(epss_df.rename(columns={"cve": "cve_id"})[["cve_id", "epss", "percentile"]], on="cve_id", how="left")
    .merge(kev_flags, on="cve_id", how="left")
)
kev_triage["epss"] = kev_triage["epss"].fillna(0.0)
kev_triage["percentile"] = kev_triage["percentile"].fillna(0.0)
kev_triage["in_kev"] = kev_triage["in_kev"].fillna(False)
kev_triage["knownRansomwareCampaignUse"] = kev_triage["knownRansomwareCampaignUse"].fillna("Unknown")
kev_triage["base_score"] = pd.to_numeric(kev_triage["base_score"], errors="coerce").fillna(0.0)
kev_triage["triage_score"] = (
    kev_triage["base_score"]
    + kev_triage["epss"] * 10.0
    + kev_triage["in_kev"].astype(int) * 5.0
    + (kev_triage["knownRansomwareCampaignUse"].str.lower() == "known").astype(int) * 2.0
)
print("Fused prioritization table for the selected CPE CVEs:")
display(kev_triage.sort_values("triage_score", ascending=False).head(15))
Fused prioritization table for the selected CPE CVEs:
cve_id base_score base_severity description epss percentile knownRansomwareCampaignUse dateAdded in_kev triage_score
6 CVE-2023-38408 9.8 CRITICAL The PKCS#11 feature in ssh-agent in OpenSSH before 9.3p2 has an insufficiently trustworthy search path, leading to remote code execution... 0.64352 0.98466 Unknown NaT False 16.2352
10 CVE-2025-26465 6.8 MEDIUM A vulnerability was found in OpenSSH when the VerifyHostKeyDNS option is enabled. A machine-in-the-middle attack can be performed by a m... 0.62453 0.98389 Unknown NaT False 13.0453
7 CVE-2023-48795 5.9 MEDIUM The SSH transport protocol with certain OpenSSH extensions, found in OpenSSH before 9.6 and other products, allows remote attackers to b... 0.54214 0.98062 Unknown NaT False 11.3214
1 CVE-2008-3844 9.3 HIGH Certain Red Hat Enterprise Linux (RHEL) 4 and 5 packages for OpenSSH, as signed in August 2008 using a legitimate Red Hat GPG key, conta... 0.02746 0.86234 Unknown NaT False 9.5746
8 CVE-2023-51385 6.5 MEDIUM In ssh in OpenSSH before 9.6, OS command injection might occur if a user name or host name has shell metacharacters, and this name is re... 0.17234 0.95136 Unknown NaT False 8.2234
12 CVE-2026-35385 7.5 HIGH In OpenSSH before 10.3, a file downloaded by scp may be installed setuid or setgid, an outcome contrary to some users' expectations, if ... 0.00058 0.18419 Unknown NaT False 7.5058
2 CVE-2021-28041 7.1 HIGH ssh-agent in OpenSSH before 8.5 has a double free that may be relevant in a few less-common scenarios, such as unconstrained agent-socke... 0.00256 0.49133 Unknown NaT False 7.1256
4 CVE-2021-41617 7.0 HIGH sshd in OpenSSH 6.2 through 8.x before 8.8, when certain non-default configurations are used, allows privilege escalation because supple... 0.00274 0.50983 Unknown NaT False 7.0274
9 CVE-2023-51767 7.0 HIGH OpenSSH through 10.0, when common types of DRAM are used, might allow row hammer attacks (for authentication bypass) because the integer... 0.00007 0.00489 Unknown NaT False 7.0007
3 CVE-2016-20012 5.3 MEDIUM OpenSSH through 8.7 allows remote attackers, who have a suspicion that a certain combination of username and public key is known to an S... 0.14603 0.94580 Unknown NaT False 6.7603
11 CVE-2025-32728 4.3 MEDIUM In sshd in OpenSSH before 10.0, the DisableForwarding directive does not adhere to the documentation stating that it disables X11 and ag... 0.00226 0.45389 Unknown NaT False 4.3226
0 CVE-2007-2768 4.3 MEDIUM OpenSSH, when using OPIE (One-Time Passwords in Everything) for PAM, allows remote attackers to determine the existence of certain user ... 0.00189 0.40552 Unknown NaT False 4.3189
16 CVE-2026-35414 4.2 MEDIUM OpenSSH before 10.3 mishandles the authorized_keys principals option in uncommon scenarios involving a principals list in conjunction wi... 0.00031 0.09377 Unknown NaT False 4.2031
5 CVE-2021-36368 3.7 LOW An issue was discovered in OpenSSH before 8.9. If a client is using public-key authentication with agent forwarding but without -oLogLev... 0.00432 0.62972 Unknown NaT False 3.7432
13 CVE-2026-35386 3.6 LOW In OpenSSH before 10.3, command execution can occur via shell metacharacters in a username within a command line. This requires a scenar... 0.00034 0.10598 Unknown NaT False 3.6034

Audio explanation for original cell 23

Transcript: This code joins the selected CPE CVEs with FIRST EPSS and CISA KEV signals, then calculates a teaching triage score. It teaches evidence fusion and makes the scoring logic inspectable instead of mysterious.

AI mentor analysis for original cell 23: Fusing EPSS and KEV Data for CVE Prioritization

This code cell demonstrates the process of enriching a CVE dataset with EPSS and KEV data to prioritize vulnerabilities. It retrieves EPSS scores and KEV flags for a set of CVEs, then calculates a 'triage score' by combining base CVSS scores with EPSS likelihood and KEV exploitation status. This approach allows for a more comprehensive prioritization by considering both the technical severity and the likelihood of exploitation. The output is a sorted table of CVEs, highlighting those that require immediate attention based on the fused data.

Discussion question: How does combining EPSS and KEV data improve the prioritization of vulnerabilities?

Plotly visual analytics

This Plotly cell visualizes fused prioritization across technical severity, exploitation probability, and known exploitation.

InĀ [17]:
# Plotly visual analytics: fused risk triage across CVSS, EPSS, and KEV.
triage_plot = kev_triage.copy()
triage_plot["in_kev_label"] = triage_plot["in_kev"].map({True: "In KEV", False: "Not in KEV"})
fig = px.scatter(
    triage_plot,
    x="epss",
    y="base_score",
    size="triage_score",
    color="in_kev_label",
    hover_name="cve_id",
    hover_data=["base_severity", "percentile", "knownRansomwareCampaignUse"],
    title="Fused Vulnerability Triage: CVSS vs EPSS with KEV Signal",
    labels={"epss": "EPSS probability", "base_score": "CVSS base score"},
)
fig.show()
fig = px.bar(
    triage_plot.sort_values("triage_score", ascending=False).head(15),
    x="cve_id",
    y="triage_score",
    color="base_severity",
    title="Top Fused Triage Scores for Selected CPE CVEs",
)
fig.update_layout(xaxis_tickangle=-45)
fig.show()

Mermaid concept diagram before original cell 24: OpenVAS and GVM Architecture

This diagram explains the OpenVAS/GVM layers before looking at XML and report parsing.

Mermaid concept diagram before original cell 24: OpenVAS and GVM Architecture

5. OpenVAS/GVM Integration¶

Concept¶

OpenVAS/GVM is useful pedagogically because it exposes the layers found in many commercial scanners:

  • Scanner engine: performs network checks and vulnerability tests.
  • Vulnerability tests: often written as plugins or scripts. In OpenVAS these are commonly NASL tests.
  • Manager/orchestrator: stores targets, tasks, reports, credentials, schedules, and scan state.
  • Protocol/API: clients automate scans through a management protocol such as GMP.
  • Feed synchronization: scanners continuously ingest vulnerability tests, advisories, product mappings, and detection logic.

Internally, a scanner may combine TCP connect checks, service probes, TLS inspection, protocol-specific safe requests, authenticated package inspection, and proof-of-vulnerability checks. Commercial products use similar ideas with proprietary feeds, passive sensors, agent telemetry, cloud enrichment, and risk scoring.

This section does not scan an external host. It shows the integration contract and parses a local sample report while using the real Greenbone/OpenVAS public repository as source evidence for NASL-style vulnerability tests.

Audio explanation for original cell 24

Transcript: This OpenVAS and GVM concept cell explains the scanner engine, vulnerability tests, manager, API, and feed layers. It teaches that commercial scanners are orchestration systems around evidence collection and plugin logic.

AI mentor analysis for original cell 24: Understanding OpenVAS/GVM Architecture

This cell provides an overview of the OpenVAS/GVM architecture, which is representative of many commercial vulnerability scanners. It explains the components such as the scanner engine, vulnerability tests, and management protocols. The cell emphasizes the importance of feed synchronization and the combination of various scanning techniques like TCP checks and TLS inspection. By understanding these components, practitioners can better appreciate the complexity and capabilities of vulnerability scanners, as well as the challenges in maintaining accuracy and efficiency.

Discussion question: What are the key challenges in maintaining the accuracy and efficiency of a vulnerability scanner like OpenVAS?

InĀ [18]:
GREENBONE_TREE_URL = "https://api.github.com/repos/greenbone/openvas-scanner/git/trees/main"
greenbone_tree = get_json(GREENBONE_TREE_URL, params={"recursive": "1"}, cache_seconds=7 * 24 * 3600)
paths = [item.get("path", "") for item in greenbone_tree.get("tree", [])]
nasl_paths = [p for p in paths if p.endswith(".nasl") or "/nasl/" in p.lower()]
example_paths = pd.DataFrame({"greenbone_repo_path": nasl_paths[:20]})
print(f"Repository paths inspected: {len(paths):,}")
print(f"NASL-related paths found: {len(nasl_paths):,}")
display(example_paths)
Repository paths inspected: 1,660
NASL-related paths found: 820
greenbone_repo_path
0 doc/manual/nasl/built-in-functions
1 doc/manual/nasl/built-in-functions/built-in-plugins
2 doc/manual/nasl/built-in-functions/built-in-plugins/index.md
3 doc/manual/nasl/built-in-functions/built-in-plugins/plugin_run_find_service.md
4 doc/manual/nasl/built-in-functions/built-in-plugins/plugin_run_openvas_tcp_scanner.md
5 doc/manual/nasl/built-in-functions/built-in-plugins/plugin_run_synscan.md
6 doc/manual/nasl/built-in-functions/cert-functions
7 doc/manual/nasl/built-in-functions/cert-functions/cert_close.md
8 doc/manual/nasl/built-in-functions/cert-functions/cert_open.md
9 doc/manual/nasl/built-in-functions/cert-functions/cert_query.md
10 doc/manual/nasl/built-in-functions/cert-functions/index.md
11 doc/manual/nasl/built-in-functions/cryptographic
12 doc/manual/nasl/built-in-functions/cryptographic/DES.md
13 doc/manual/nasl/built-in-functions/cryptographic/HMAC_MD2.md
14 doc/manual/nasl/built-in-functions/cryptographic/HMAC_MD5.md
15 doc/manual/nasl/built-in-functions/cryptographic/HMAC_RIPEMD160.md
16 doc/manual/nasl/built-in-functions/cryptographic/HMAC_SHA1.md
17 doc/manual/nasl/built-in-functions/cryptographic/HMAC_SHA256.md
18 doc/manual/nasl/built-in-functions/cryptographic/HMAC_SHA384.md
19 doc/manual/nasl/built-in-functions/cryptographic/HMAC_SHA512.md

Audio explanation for original cell 25

Transcript: This code inspects the public Greenbone OpenVAS scanner repository and counts NASL-related paths. It teaches that scanner detection logic is often maintained as a large feed of specialized tests.

AI mentor analysis for original cell 25: Exploring Greenbone/OpenVAS NASL Scripts

This code cell retrieves and lists NASL script paths from the Greenbone/OpenVAS public repository, providing insight into the structure and volume of vulnerability tests available. By examining these scripts, practitioners can understand how OpenVAS leverages NASL for vulnerability detection. The cell highlights the importance of script-based tests in identifying vulnerabilities and the role of public repositories in providing up-to-date detection logic. This exploration aids in understanding the mechanics behind vulnerability scanning and the reliance on community-contributed scripts.

Discussion question: How do public repositories like Greenbone's contribute to the effectiveness of vulnerability scanners?

InĀ [19]:
def gmp_create_target_xml(name: str, hosts: list[str], port_list_id: str) -> str:
    root = ET.Element("create_target")
    ET.SubElement(root, "name").text = name
    ET.SubElement(root, "hosts").text = ",".join(hosts)
    ET.SubElement(root, "port_list", id=port_list_id)
    return ET.tostring(root, encoding="unicode")


def gmp_create_task_xml(name: str, target_id: str, scanner_id: str, config_id: str) -> str:
    root = ET.Element("create_task")
    ET.SubElement(root, "name").text = name
    ET.SubElement(root, "target", id=target_id)
    ET.SubElement(root, "scanner", id=scanner_id)
    ET.SubElement(root, "config", id=config_id)
    return ET.tostring(root, encoding="unicode")

print("Example GMP create_target XML:")
print(gmp_create_target_xml("chapter-3-local-lab", ["127.0.0.1"], "port-list-uuid"))
print("\nExample GMP create_task XML:")
print(gmp_create_task_xml("chapter-3-safe-task", "target-uuid", "scanner-uuid", "scan-config-uuid"))
Example GMP create_target XML:
<create_target><name>chapter-3-local-lab</name><hosts>127.0.0.1</hosts><port_list id="port-list-uuid" /></create_target>

Example GMP create_task XML:
<create_task><name>chapter-3-safe-task</name><target id="target-uuid" /><scanner id="scanner-uuid" /><config id="scan-config-uuid" /></create_task>

Audio explanation for original cell 26

Transcript: This code builds example GMP XML requests for targets and tasks. It teaches the integration contract that automation clients use when they create scan targets, select scanners, and launch tasks through a manager API.

AI mentor analysis for original cell 26: Creating GMP XML for OpenVAS Tasks

This code cell demonstrates the creation of XML configurations for OpenVAS tasks using the Greenbone Management Protocol (GMP). It shows how to define targets and tasks by constructing XML strings that specify target hosts, port lists, and scanner configurations. This process is crucial for automating vulnerability scans and integrating them into larger security workflows. By understanding GMP XML, practitioners can customize and automate their scanning processes, enhancing the efficiency and consistency of vulnerability management efforts.

Discussion question: What are the advantages of using GMP XML for automating vulnerability scans in OpenVAS?

InĀ [20]:
sample_gvm_report = """<get_reports_response status="200" status_text="OK">
  <report id="sample-report">
    <results>
      <result id="r1">
        <host>192.0.2.10</host>
        <port>tcp/22</port>
        <severity>8.1</severity>
        <threat>High</threat>
        <nvt oid="1.3.6.1.4.1.25623.1.0.900001">
          <name>OpenSSH example vulnerability detection</name>
          <cves>CVE-2024-6387</cves>
        </nvt>
        <description>Example result used to teach GMP parsing; not a live scan finding.</description>
      </result>
      <result id="r2">
        <host>192.0.2.20</host>
        <port>tcp/80</port>
        <severity>5.3</severity>
        <threat>Medium</threat>
        <nvt oid="1.3.6.1.4.1.25623.1.0.900002">
          <name>Default web console credential check</name>
          <cves></cves>
        </nvt>
        <description>Example result used to teach report normalization.</description>
      </result>
    </results>
  </report>
</get_reports_response>"""

root = ET.fromstring(sample_gvm_report)
gvm_rows = []
for result in root.findall(".//result"):
    nvt = result.find("nvt")
    gvm_rows.append({
        "result_id": result.get("id"),
        "host": result.findtext("host"),
        "port": result.findtext("port"),
        "severity": float(result.findtext("severity", "0")),
        "threat": result.findtext("threat"),
        "nvt_oid": nvt.get("oid") if nvt is not None else None,
        "nvt_name": nvt.findtext("name") if nvt is not None else None,
        "cves": nvt.findtext("cves") if nvt is not None else None,
        "description": result.findtext("description"),
    })

gvm_report_df = pd.DataFrame(gvm_rows).sort_values("severity", ascending=False)
display(gvm_report_df)
result_id host port severity threat nvt_oid nvt_name cves description
0 r1 192.0.2.10 tcp/22 8.1 High 1.3.6.1.4.1.25623.1.0.900001 OpenSSH example vulnerability detection CVE-2024-6387 Example result used to teach GMP parsing; not a live scan finding.
1 r2 192.0.2.20 tcp/80 5.3 Medium 1.3.6.1.4.1.25623.1.0.900002 Default web console credential check Example result used to teach report normalization.

Audio explanation for original cell 27

Transcript: This code parses a simulated GVM XML report. It teaches report normalization: host, port, severity, threat, NVT identity, CVEs, and descriptions become structured records for dashboards and tickets.

AI mentor analysis for original cell 27: Parsing a Sample GVM Report

This code cell parses a simulated GVM report to extract and display vulnerability findings. It demonstrates how to navigate XML structures to retrieve details such as host, port, severity, and CVE identifiers. This parsing process is essential for transforming raw scan data into actionable insights. By understanding how to extract and interpret this information, practitioners can better integrate scan results into their security operations, enabling more effective vulnerability management and remediation planning.

Discussion question: How can effective parsing of vulnerability reports enhance security operations?

Plotly visual analytics

This Plotly cell visualizes scanner plugin/source organization and normalized sample report severity.

InĀ [21]:
# Plotly visual analytics: OpenVAS/GVM-style evidence normalization.
if nasl_paths:
    nasl_dirs = pd.Series(["/".join(p.split("/")[:2]) if "/" in p else p for p in nasl_paths], name="repo_area").value_counts().head(12).reset_index()
    nasl_dirs.columns = ["repo_area", "count"]
    fig = px.treemap(nasl_dirs, path=["repo_area"], values="count", title="Greenbone/OpenVAS NASL-Related Repository Areas")
    fig.show()
fig = px.bar(
    gvm_report_df,
    x="nvt_name",
    y="severity",
    color="threat",
    hover_data=["host", "port", "cves"],
    title="Normalized Sample GVM Report Findings by Severity",
)
fig.update_layout(xaxis_tickangle=-25)
fig.show()

Interpretation: commercial scanner pattern¶

Commercial scanners differ in UI, feeds, cloud enrichment, and enterprise workflow, but the underlying cycle is similar:

  1. Define target and credentials.
  2. Select policies or plugins.
  3. Probe services and collect evidence.
  4. Match evidence against plugin logic and vulnerability intelligence.
  5. Normalize findings into reports, tickets, risk scores, and remediation plans.

The hard engineering problems are scale, false-positive control, credential handling, feed freshness, safe probing, tenant isolation, auditability, and prioritization.

Audio explanation for original cell 28

Transcript: This interpretation cell generalizes the commercial scanner workflow. It teaches the recurring pattern of target definition, credential handling, policy selection, evidence collection, finding normalization, and remediation workflow.

AI mentor analysis for original cell 28: Commercial Vulnerability Scanner Workflow

This cell outlines the typical workflow of commercial vulnerability scanners, emphasizing the similarities despite differences in UI and features. It describes the cycle from target definition to report generation, highlighting challenges like scale, false positives, and feed freshness. Understanding this workflow helps practitioners appreciate the complexities involved in vulnerability scanning and the engineering efforts required to maintain accuracy and efficiency. It also underscores the importance of integrating various components to achieve comprehensive vulnerability management.

Discussion question: What are the most significant engineering challenges in developing a commercial vulnerability scanner?

Mermaid concept diagram before original cell 29: Nuclei Template Execution

Use this diagram to explain template-driven scanning before parsing templates and running the local target.

Mermaid concept diagram before original cell 29: Nuclei Template Execution

6. Nuclei Template Scanning¶

Concept¶

Nuclei is a template-driven scanner. Its internal model is easy to teach:

  • A YAML template defines metadata, requests, matchers, extractors, and severity.
  • The engine expands variables, sends requests, evaluates matchers, and emits structured results.
  • A template can detect a CVE, a misconfiguration, a leaked file, a technology fingerprint, or a workflow condition.

Templates are signatures with executable context. The power is speed and community scale. The risk is false positives, unsafe templates, stale checks, and target authorization errors.

This section first analyzes real public Nuclei templates, then runs a local-only template against a local web server created by the notebook.

Audio explanation for original cell 29

Transcript: This Nuclei concept cell explains template-driven scanning. It teaches how YAML metadata, requests, matchers, and extractors become fast executable detection logic, while also naming the risks of unsafe or stale templates.

AI mentor analysis for original cell 29: Nuclei Template Scanning Overview

This cell introduces Nuclei, a template-driven scanner, explaining its structure and functionality. It highlights how YAML templates define scanning parameters and how the engine processes these templates to detect vulnerabilities. The cell emphasizes the speed and scalability of Nuclei due to its community-driven template repository. However, it also notes the risks of false positives and outdated checks. Understanding Nuclei's model helps practitioners leverage its capabilities while being mindful of its limitations, particularly in terms of template management and target authorization.

Discussion question: What are the benefits and risks of using a template-driven scanner like Nuclei?

InĀ [22]:
NUCLEI_2024_API = "https://api.github.com/repos/projectdiscovery/nuclei-templates/contents/http/cves/2024"
nuclei_listing = get_json(NUCLEI_2024_API, cache_seconds=7 * 24 * 3600)
template_items = [item for item in nuclei_listing if item.get("name", "").endswith((".yaml", ".yml"))]
print(f"Template files listed from ProjectDiscovery http/cves/2024: {len(template_items)}")

parsed_templates = []
for item in template_items[:35]:
    try:
        raw_text = get_bytes(item["download_url"], f"nuclei_{item['name']}", cache_seconds=7 * 24 * 3600).decode("utf-8", errors="replace")
        doc = yaml.safe_load(raw_text) or {}
        info = doc.get("info", {}) or {}
        classification = info.get("classification", {}) or {}
        parsed_templates.append({
            "id": doc.get("id"),
            "name": info.get("name"),
            "severity": info.get("severity"),
            "tags": ",".join(info.get("tags", []) if isinstance(info.get("tags"), list) else [str(info.get("tags", ""))]),
            "cve_id": classification.get("cve-id") or classification.get("cve_id"),
            "cvss_score": classification.get("cvss-score") or classification.get("cvss_score"),
            "path": item.get("path"),
        })
    except Exception as exc:
        parsed_templates.append({"id": item.get("name"), "parse_error": str(exc), "path": item.get("path")})

nuclei_templates_df = pd.DataFrame(parsed_templates)
display(nuclei_templates_df.head(12))
Template files listed from ProjectDiscovery http/cves/2024: 602
id name severity tags cve_id cvss_score path
0 CVE-2024-0012 PAN-OS Management Web Interface - Authentication Bypass critical cve,cve2024,paloalto,globalprotect,kev,vkev,vuln CVE-2024-0012 9.8 http/cves/2024/CVE-2024-0012.yaml
1 CVE-2024-0195 SpiderFlow Crawler Platform - Remote Code Execution critical cve,cve2024,spiderflow,crawler,unauth,rce,ssssssss,vuln CVE-2024-0195 9.8 http/cves/2024/CVE-2024-0195.yaml
2 CVE-2024-0200 Github Enterprise Authenticated Remote Code Execution critical cve,cve2024,rce,github,enterprise,vuln CVE-2024-0200 9.8 http/cves/2024/CVE-2024-0200.yaml
3 CVE-2024-0204 Fortra GoAnywhere MFT - Authentication Bypass critical packetstorm,cve,cve2024,auth-bypass,goanywhere,fortra,vkev,vuln CVE-2024-0204 9.8 http/cves/2024/CVE-2024-0204.yaml
4 CVE-2024-0235 EventON (Free < 2.2.8, Premium < 4.5.5) - Information Disclosure medium cve,cve2024,wp,wordpress,wp-plugin,exposure,eventon,wpscan,myeventon,vkev,vuln CVE-2024-0235 5.3 http/cves/2024/CVE-2024-0235.yaml
5 CVE-2024-0250 Analytics Insights for Google Analytics 4 < 6.3 - Open Redirect medium cve,cve2024,wpscan,redirect,wp,wp-plugin,wordpress,analytics-insights,vuln NaN NaN http/cves/2024/CVE-2024-0250.yaml
6 CVE-2024-0305 Ncast busiFacade - Remote Command Execution high cve,cve2024,ncast,rce,ncast_project,vkev,vuln CVE-2024-0305 7.5 http/cves/2024/CVE-2024-0305.yaml
7 CVE-2024-0337 Travelpayouts <= 1.1.16 - Open Redirect medium wpscan,cve,cve2024,wp,wp-plugin,wordpress,redirect,travelpayouts,vuln CVE-2024-0337 NaN http/cves/2024/CVE-2024-0337.yaml
8 CVE-2024-0352 Likeshop < 2.5.7.20210311 - Arbitrary File Upload critical cve,cve2024,rce,file-upload,likeshop,instrusive,intrusive,vkev,vuln CVE-2024-0352 9.8 http/cves/2024/CVE-2024-0352.yaml
9 CVE-2024-0593 WordPress Simple Job Board - Unauthorized Data Access medium cve,cve2024,wp,wordpress,wp-plugin,simple-job-board,exposure,vuln CVE-2024-0593 5.3 http/cves/2024/CVE-2024-0593.yaml
10 CVE-2024-0692 SolarWinds Security Event Manager - Unauthenticated RCE high cve,cve2024,solarwinds,event-manager,cisa,vkev,vuln CVE-2024-0692 8.8 http/cves/2024/CVE-2024-0692.yaml
11 CVE-2024-0705 Stripe Payment Plugin for WooCommerce <= 3.7.9 - Unauthenticated SQL Injection critical cve,cve2024,wp-plugin,wp,wordpress,woocommerce,stripe,sqli,unauth,time-based NaN NaN http/cves/2024/CVE-2024-0705.yaml

Audio explanation for original cell 30

Transcript: This code fetches real ProjectDiscovery Nuclei templates and parses their metadata. It teaches how public template repositories can be treated as datasets for studying signature coverage, severity, tags, and CVE mapping.

AI mentor analysis for original cell 30: Analyzing Nuclei Templates for CVE Detection

This code cell retrieves and parses YAML templates from the Nuclei repository, focusing on CVEs for 2024. It extracts metadata such as severity, tags, and CVE identifiers, providing insight into the structure and content of these templates. By analyzing these templates, practitioners can understand how Nuclei detects specific vulnerabilities and the role of community contributions in maintaining an up-to-date scanning capability. This analysis is crucial for assessing the effectiveness of Nuclei in identifying vulnerabilities and ensuring the accuracy of its detections.

Discussion question: How does community contribution impact the effectiveness of Nuclei's template-driven scanning approach?

InĀ [23]:
cluster_input = nuclei_templates_df.fillna("")
cluster_text = (cluster_input["name"].astype(str) + " " + cluster_input["severity"].astype(str) + " " + cluster_input["tags"].astype(str)).tolist()
if len(cluster_text) >= 4 and any(text.strip() for text in cluster_text):
    vectorizer = TfidfVectorizer(min_df=1, max_features=1000)
    X_templates = vectorizer.fit_transform(cluster_text)
    n_clusters = min(4, len(cluster_text))
    kmeans = KMeans(n_clusters=n_clusters, n_init=10, random_state=7)
    nuclei_templates_df["cluster"] = kmeans.fit_predict(X_templates)
    cluster_terms = []
    terms = np.array(vectorizer.get_feature_names_out())
    for cluster_id in range(n_clusters):
        centroid = kmeans.cluster_centers_[cluster_id]
        top_terms = terms[np.argsort(centroid)[-8:][::-1]].tolist()
        cluster_terms.append({"cluster": cluster_id, "top_terms": ", ".join(top_terms), "count": int((nuclei_templates_df["cluster"] == cluster_id).sum())})
    display(pd.DataFrame(cluster_terms))
    display(nuclei_templates_df[["id", "name", "severity", "tags", "cluster"]].head(15))
else:
    print("Not enough template metadata to cluster.")
cluster top_terms count
0 0 wp, wordpress, plugin, sqli, wpscan, vuln, medium, exposure 10
1 1 upload, file, intrusive, lms, high, monitorr, likeshop, smart 7
2 2 bypass, rce, vkev, critical, authenticated, cve, cve2024, vuln 14
3 3 redirect, open, analytics, travelpayouts, fastchat, medium, oss, wp 4
id name severity tags cluster
0 CVE-2024-0012 PAN-OS Management Web Interface - Authentication Bypass critical cve,cve2024,paloalto,globalprotect,kev,vkev,vuln 2
1 CVE-2024-0195 SpiderFlow Crawler Platform - Remote Code Execution critical cve,cve2024,spiderflow,crawler,unauth,rce,ssssssss,vuln 2
2 CVE-2024-0200 Github Enterprise Authenticated Remote Code Execution critical cve,cve2024,rce,github,enterprise,vuln 2
3 CVE-2024-0204 Fortra GoAnywhere MFT - Authentication Bypass critical packetstorm,cve,cve2024,auth-bypass,goanywhere,fortra,vkev,vuln 2
4 CVE-2024-0235 EventON (Free < 2.2.8, Premium < 4.5.5) - Information Disclosure medium cve,cve2024,wp,wordpress,wp-plugin,exposure,eventon,wpscan,myeventon,vkev,vuln 0
5 CVE-2024-0250 Analytics Insights for Google Analytics 4 < 6.3 - Open Redirect medium cve,cve2024,wpscan,redirect,wp,wp-plugin,wordpress,analytics-insights,vuln 3
6 CVE-2024-0305 Ncast busiFacade - Remote Command Execution high cve,cve2024,ncast,rce,ncast_project,vkev,vuln 2
7 CVE-2024-0337 Travelpayouts <= 1.1.16 - Open Redirect medium wpscan,cve,cve2024,wp,wp-plugin,wordpress,redirect,travelpayouts,vuln 3
8 CVE-2024-0352 Likeshop < 2.5.7.20210311 - Arbitrary File Upload critical cve,cve2024,rce,file-upload,likeshop,instrusive,intrusive,vkev,vuln 1
9 CVE-2024-0593 WordPress Simple Job Board - Unauthorized Data Access medium cve,cve2024,wp,wordpress,wp-plugin,simple-job-board,exposure,vuln 0
10 CVE-2024-0692 SolarWinds Security Event Manager - Unauthenticated RCE high cve,cve2024,solarwinds,event-manager,cisa,vkev,vuln 2
11 CVE-2024-0705 Stripe Payment Plugin for WooCommerce <= 3.7.9 - Unauthenticated SQL Injection critical cve,cve2024,wp-plugin,wp,wordpress,woocommerce,stripe,sqli,unauth,time-based 0
12 CVE-2024-0713 Monitorr Services Configuration - Arbitrary File Upload high cve,cve2024,file-upload,intrusive,monitorr,vuln 1
13 CVE-2024-0799 Arcserve Unified Data Protection - Authentication Bypass critical cve,cve2024,arcserve,auth-bypass,vkev 2
14 CVE-2024-0801 Arcserve Unified Data Protection - Unauthenticated DoS in ASNative.dll high cve,cve2024,arcserve,dos,intrusive,vkev 1

Audio explanation for original cell 31

Transcript: This code clusters Nuclei template metadata without labels. It teaches unsupervised learning: the model groups templates by textual similarity so students can see structure emerge from names, tags, and severities.

AI mentor analysis for original cell 31: Clustering Nuclei Templates

This cell demonstrates clustering of Nuclei vulnerability templates using KMeans. It preprocesses template metadata by filling missing values and concatenating fields like name, severity, and tags. TF-IDF vectorization converts text data into numerical format, suitable for clustering. The number of clusters is determined by the minimum of 4 or the number of templates. Top terms for each cluster are extracted to provide insight into common characteristics. This approach helps identify patterns in vulnerability data, aiding in prioritization and response strategies.

Discussion question: How can clustering improve vulnerability management processes?

Plotly visual analytics

This Plotly cell visualizes template severity mix and unsupervised cluster structure from real Nuclei metadata.

InĀ [24]:
# Plotly visual analytics: Nuclei template metadata and unsupervised clusters.
template_plot = nuclei_templates_df.fillna("")
if "severity" in template_plot.columns:
    severity_counts = template_plot["severity"].replace("", "unknown").value_counts().reset_index()
    severity_counts.columns = ["severity", "count"]
    fig = px.bar(severity_counts, x="severity", y="count", color="severity", title="Sampled Nuclei CVE Template Severity Mix")
    fig.show()
if "cluster" in template_plot.columns:
    fig = px.scatter(
        template_plot.reset_index(),
        x="index",
        y="severity",
        color="cluster" if "cluster" in template_plot.columns else "severity",
        hover_name="id",
        hover_data=["name", "tags"],
        title="Unsupervised Nuclei Template Clusters",
    )
    fig.show()

Mermaid concept diagram before original cell 32: Local-Only Scanner Safety Boundary

This diagram makes the authorization boundary explicit before the notebook runs the real Nuclei engine.

Mermaid concept diagram before original cell 32: Local-Only Scanner Safety Boundary
InĀ [25]:
class LocalFirmwareHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/device":
            body = b"deviceInfo vendor=SEAS8414 model=LocalLab firmwareVersion=V5.5.52 build=teaching-only"
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
        else:
            self.send_response(404)
            self.end_headers()

    def log_message(self, format, *args):
        return

server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), LocalFirmwareHandler)
server_thread = threading.Thread(target=server.serve_forever, daemon=True)
server_thread.start()
target_url = f"http://127.0.0.1:{server.server_port}"

template_path = OUT / "seas8414-local-firmware-disclosure.yaml"
jsonl_path = OUT / "nuclei-local-results.jsonl"
template_path.write_text("\n".join([
    "id: seas8414-local-firmware-disclosure",
    "info:",
    "  name: SEAS 8414 Local Firmware Disclosure Teaching Check",
    "  author: seas8414",
    "  severity: low",
    "  description: Detects a local lab firmware version string served by this notebook.",
    "  tags: local,teaching,firmware",
    "http:",
    "  - method: GET",
    "    path:",
    "      - '{{BaseURL}}/device'",
    "    matchers-condition: and",
    "    matchers:",
    "      - type: word",
    "        words:",
    "          - 'firmwareVersion=V5.5.52'",
    "      - type: status",
    "        status:",
    "          - 200",
]))

nuclei_bin = shutil.which("nuclei")
if nuclei_bin is None:
    print("nuclei is not installed; skipping local engine execution.")
else:
    if jsonl_path.exists():
        jsonl_path.unlink()
    cmd = [nuclei_bin, "-silent", "-duc", "-jsonl", "-ot", "-t", str(template_path), "-it", str(template_path), "-u", target_url, "-timeout", "5", "-retries", "0", "-o", str(jsonl_path)]
    print("Running local-only nuclei command:", " ".join(cmd))
    proc = subprocess.run(cmd, capture_output=True, text=True, timeout=90)
    print("return code:", proc.returncode)
    if proc.stderr.strip():
        print("stderr sample:", proc.stderr.strip()[:1000])
    if proc.stdout.strip():
        print("stdout sample:", proc.stdout.strip()[:1000])
    if jsonl_path.exists():
        results = [json.loads(line) for line in jsonl_path.read_text().splitlines() if line.strip()]
        display(pd.DataFrame(results))
    else:
        print("No JSONL result file was produced.")

server.shutdown()
server.server_close()
Running local-only nuclei command: /opt/homebrew/bin/nuclei -silent -duc -jsonl -ot -t notebook-runtime/seas8414_ch03_master_teaching/outputs/seas8414-local-firmware-disclosure.yaml -it notebook-runtime/seas8414_ch03_master_teaching/outputs/seas8414-local-firmware-disclosure.yaml -u http://127.0.0.1:56543 -timeout 5 -retries 0 -o notebook-runtime/seas8414_ch03_master_teaching/outputs/nuclei-local-results.jsonl
return code: 0
stdout sample: {"template-id":"seas8414-local-firmware-disclosure","template-path":"notebook-runtime/seas8414_ch03_master_teaching/outputs/seas8414-local-firmware-disclosure.yaml","info":{"name":"SEAS 8414 Local Firmware Disclosure Teaching Check","author":["seas8414"],"tags":["local","teaching","firmware"],"description":"Detects a local lab firmware version string served by this notebook.","severity":"low"},"type":"http","host":"127.0.0.1","port":"56543","scheme":"http","url":"http://127.0.0.1:56543","matched-at":"http://127.0.0.1:56543/device","request":"GET /device HTTP/1.1\r\nHost: 127.0.0.1:56543\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/537.36 (KHTML, like Gecko) Version/8.0 Safari/537.36\r\nConnection: close\r\nAccept: */*\r\nAccept-Language: en\r\nAccept-Encoding: gzip\r\n\r\n","response":"HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Length: 85\r\nContent-Type: text/plain\r\nDate: Fri, 29 May 2026 19:20:51 GMT\r\nServer: BaseHTTP/0.6 Python/3.11.15
template-id template-path info type host port scheme url matched-at request response ip timestamp curl-command matcher-status
0 seas8414-local-firmware-disclosure notebook-runtime/seas8414_ch03_master_teaching/outputs/seas8414-local-firmware-disclosure.yaml {'name': 'SEAS 8414 Local Firmware Disclosure Teaching Check', 'author': ['seas8414'], 'tags': ['local', 'teaching', 'firmware'], 'descr... http 127.0.0.1 56543 http http://127.0.0.1:56543 http://127.0.0.1:56543/device GET /device HTTP/1.1\r\nHost: 127.0.0.1:56543\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/537.36 (KHTML, li... HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Length: 85\r\nContent-Type: text/plain\r\nDate: Fri, 29 May 2026 19:20:51 GMT\r\nServer:... 127.0.0.1 2026-05-29T15:20:51.946052-04:00 curl -X 'GET' -d '' -H 'Accept: */*' -H 'Accept-Language: en' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/... True

Audio explanation for original cell 32

Transcript: This code starts a local HTTP service, writes a custom Nuclei template, and runs the real Nuclei engine only against loopback. It teaches safe scanner execution, matcher logic, and JSONL evidence capture without touching external targets.

AI mentor analysis for original cell 32: Simulated Local Firmware Disclosure

This cell sets up a simulated HTTP server to mimic a firmware disclosure scenario. It serves a static response containing firmware information when accessed at a specific endpoint. A Nuclei template is created to detect this disclosure by matching the firmware version string and HTTP status code. This setup is used to teach how to create and test vulnerability detection templates in a controlled environment. It emphasizes the importance of understanding server responses and crafting precise detection rules.

Discussion question: What are the benefits of using simulated environments for vulnerability detection training?

Mermaid concept diagram before original cell 33: Default Credential Check Controls

Use this diagram to reinforce that default credential validation must be scoped and low-impact.

Mermaid concept diagram before original cell 33: Default Credential Check Controls

7. Default Credential Checking¶

Concept¶

Default credential checking is deceptively simple and operationally dangerous. Internally, a platform should:

  1. Identify the product or service family first.
  2. Select a small vendor-specific candidate set.
  3. Verify only in authorized scope.
  4. Rate-limit attempts and respect account lockout policies.
  5. Confirm success through a low-impact authenticated page or endpoint.
  6. Store evidence without exposing secrets.

Brute forcing is not the lesson here. The lesson is controlled evidence validation. This section loads a real public default-credential dataset but performs checks only against a local HTTP Basic server created by the notebook.

Audio explanation for original cell 33

Transcript: This default credential concept cell explains why credential checks need tight controls. It teaches vendor-first candidate selection, authorization boundaries, rate limits, lockout awareness, and evidence collection without exposing secrets.

AI mentor analysis for original cell 33: Default Credential Checking Concept

This markdown cell outlines the principles of default credential checking, emphasizing controlled and ethical practices. It highlights the importance of identifying the correct product, using a vendor-specific credential set, and respecting security policies like rate limits and account lockouts. The focus is on validating evidence of default credentials without resorting to brute force. This section prepares practitioners to understand the ethical boundaries and technical nuances of credential testing.

Discussion question: Why is it important to limit default credential checks to authorized scopes?

InĀ [26]:
SECLISTS_DEFAULT_CREDS = "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Default-Credentials/default-passwords.csv"
creds_bytes = get_bytes(SECLISTS_DEFAULT_CREDS, "seclists_default_passwords.csv", cache_seconds=7 * 24 * 3600)
try:
    default_creds_df = pd.read_csv(io.BytesIO(creds_bytes), on_bad_lines="skip")
except Exception:
    default_creds_df = pd.read_csv(io.BytesIO(creds_bytes), header=None, names=["name", "username", "password"], on_bad_lines="skip")

# Normalize likely username/password columns across dataset variants.
cols_lower = {c.lower(): c for c in default_creds_df.columns}
username_col = cols_lower.get("username") or cols_lower.get("user") or default_creds_df.columns[min(1, len(default_creds_df.columns) - 1)]
password_col = cols_lower.get("password") or cols_lower.get("pass") or default_creds_df.columns[min(2, len(default_creds_df.columns) - 1)]
print(f"Default credential rows loaded: {len(default_creds_df):,}")
display(default_creds_df.head(10))
Default credential rows loaded: 2,875
Vendor Username Password Comments
0 2Wire, Inc. http <BLANK> NaN
1 360 Systems factory factory NaN
2 3COM 3comcso RIP000 Resets all passwords to defaults
3 3COM <BLANK> 12345 NaN
4 3COM <BLANK> 1234admin NaN
5 3COM <BLANK> <BLANK> NaN
6 3COM <BLANK> ANYCOM NaN
7 3COM <BLANK> ILMI NaN
8 3COM <BLANK> PASSWORD NaN
9 3COM <BLANK> admin NaN

Audio explanation for original cell 34

Transcript: This code loads the real SecLists default credential dataset and normalizes username and password columns. It teaches how public credential corpora can support scoped validation, while avoiding brute-force behavior.

AI mentor analysis for original cell 34: Loading Default Credentials Dataset

This cell loads a public dataset of default credentials from SecLists, handling potential CSV format variations. It normalizes column names to ensure consistent access to username and password data. The dataset provides a basis for testing against local services, illustrating the prevalence of default credentials across vendors. This exercise demonstrates the importance of data preprocessing and normalization when dealing with real-world datasets, ensuring accurate and reliable analysis.

Discussion question: How does normalizing dataset columns aid in credential analysis?

InĀ [27]:
EXPECTED_USER = "admin"
EXPECTED_PASS = "admin"
EXPECTED_TOKEN = "Basic " + base64.b64encode(f"{EXPECTED_USER}:{EXPECTED_PASS}".encode()).decode()

class LocalBasicAuthHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        auth = self.headers.get("Authorization", "")
        if auth == EXPECTED_TOKEN:
            body = b"authenticated local lab console"
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
        else:
            self.send_response(401)
            self.send_header("WWW-Authenticate", 'Basic realm="seas8414-local"')
            self.end_headers()

    def log_message(self, format, *args):
        return


def is_loopback_url(url: str) -> bool:
    parsed = urllib.parse.urlparse(url)
    return parsed.hostname in {"127.0.0.1", "localhost", "::1"}


def check_http_basic_local(url: str, username: str, password: str) -> dict[str, Any]:
    if not is_loopback_url(url):
        raise ValueError("Credential checks are restricted to local loopback targets in this notebook.")
    response = requests.get(url, auth=(username, password), timeout=3)
    return {"username": username, "password_supplied": bool(password), "status_code": response.status_code, "success": response.status_code == 200}

basic_server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), LocalBasicAuthHandler)
basic_thread = threading.Thread(target=basic_server.serve_forever, daemon=True)
basic_thread.start()
login_url = f"http://127.0.0.1:{basic_server.server_port}/"

candidate_pairs = [("admin", "admin"), ("admin", "password"), ("root", "root")]
# Add a few real dataset candidates with short values to demonstrate dataset-driven selection.
for _, row in default_creds_df.head(50).iterrows():
    user = str(row.get(username_col, "")).strip()
    password = str(row.get(password_col, "")).strip()
    if user and password and len(user) <= 16 and len(password) <= 16:
        candidate_pairs.append((user, password))
    if len(candidate_pairs) >= 8:
        break

credential_results = [check_http_basic_local(login_url, u, p) for u, p in candidate_pairs]
basic_server.shutdown()
basic_server.server_close()
display(pd.DataFrame(credential_results))
username password_supplied status_code success
0 admin True 200 True
1 admin True 401 False
2 root True 401 False
3 http True 401 False
4 factory True 401 False
5 3comcso True 401 False
6 <BLANK> True 401 False
7 <BLANK> True 401 False

Audio explanation for original cell 35

Transcript: This code creates a local HTTP Basic Auth target and tests a small candidate set only on loopback. It teaches proof validation with an allowlist, clear success criteria, and no external authentication attempts.

AI mentor analysis for original cell 35: Simulated Local Basic Auth Server

This cell implements a local HTTP server with Basic Authentication to simulate credential checking. It checks incoming requests against expected credentials and returns appropriate HTTP responses. The function `check_http_basic_local` is designed to test credentials against this server, ensuring that checks are restricted to local loopback addresses. This setup provides a safe environment to practice credential validation techniques without impacting real systems.

Discussion question: What are the security implications of testing credentials on non-local targets?

Plotly visual analytics

This Plotly cell visualizes the local-only default credential validation outcomes and reinforces the scope boundary.

InĀ [28]:
# Plotly visual analytics: scoped default credential validation outcomes.
cred_plot = pd.DataFrame(credential_results)
fig = px.histogram(
    cred_plot,
    x="status_code",
    color="success",
    barmode="group",
    title="Local-Only Default Credential Check Outcomes",
    labels={"status_code": "HTTP status", "success": "Authentication succeeded"},
)
fig.show()

Mermaid concept diagram before original cell 36: Firmware Integrity Workflow

This diagram explains why firmware analysis is stronger than banner-only vulnerability inference.

Mermaid concept diagram before original cell 36: Firmware Integrity Workflow

8. Firmware Integrity and Entropy Analysis¶

Concept¶

Firmware analysis connects vulnerability management to supply-chain and embedded-system evidence. A platform may inspect:

  • Vendor hashes or signed manifests.
  • Bootloader, kernel, filesystem, and package metadata.
  • Entropy anomalies that suggest compression, encryption, packed blobs, or appended payloads.
  • Known vulnerable components extracted from filesystem images.
  • Runtime observations from devices that cannot be safely scanned actively.

The kernel and interrupt vector table matter because low-level firmware can alter control flow before userland services are visible. A network scanner generally cannot prove the integrity of those layers from a banner. It needs firmware files, hardware attestation, secure boot evidence, or on-device collection.

Audio explanation for original cell 36

Transcript: This firmware concept cell connects vulnerability management to supply-chain and embedded evidence. It teaches why firmware hashes, bootloaders, kernels, filesystems, package manifests, and attestation matter beyond service banners.

AI mentor analysis for original cell 36: Firmware Integrity and Entropy Analysis Concept

This markdown cell introduces the concept of firmware integrity and entropy analysis. It explains how analyzing firmware can reveal vulnerabilities related to supply chain and embedded systems. Key aspects include verifying hashes, detecting entropy anomalies, and examining known vulnerable components. The discussion emphasizes the limitations of network scanners in assessing firmware integrity and the need for direct firmware analysis or secure boot evidence.

Discussion question: Why is entropy analysis important in firmware security assessments?

InĀ [29]:
FIRMWARE_URL = "https://downloads.openwrt.org/releases/23.05.0/targets/ath79/generic/openwrt-23.05.0-ath79-generic-8dev_carambola2-initramfs-kernel.bin"
SHA256SUMS_URL = "https://downloads.openwrt.org/releases/23.05.0/targets/ath79/generic/sha256sums"
firmware_name = Path(urllib.parse.urlparse(FIRMWARE_URL).path).name
firmware_bytes = get_bytes(FIRMWARE_URL, firmware_name, cache_seconds=30 * 24 * 3600)
sha256sums_text = get_bytes(SHA256SUMS_URL, "openwrt_23.05.0_ath79_generic_sha256sums.txt", cache_seconds=30 * 24 * 3600).decode("utf-8", errors="replace")
actual_sha256 = hashlib.sha256(firmware_bytes).hexdigest()
expected_sha256 = None
for line in sha256sums_text.splitlines():
    parts = line.strip().split()
    if len(parts) >= 2 and parts[-1].endswith(firmware_name):
        expected_sha256 = parts[0]
        break

integrity_result = pd.DataFrame([{
    "firmware_file": firmware_name,
    "bytes": len(firmware_bytes),
    "actual_sha256": actual_sha256,
    "expected_sha256": expected_sha256,
    "hash_matches_manifest": actual_sha256 == expected_sha256,
}])
display(integrity_result)
firmware_file bytes actual_sha256 expected_sha256 hash_matches_manifest
0 openwrt-23.05.0-ath79-generic-8dev_carambola2-initramfs-kernel.bin 5608724 5e09d72bcff539a2470340e72d7e55180f05d23ef1121938ddaa1a1e67b7d816 5e09d72bcff539a2470340e72d7e55180f05d23ef1121938ddaa1a1e67b7d816 True

Audio explanation for original cell 37

Transcript: This code downloads an OpenWrt firmware image and the vendor sha256sums manifest, then verifies the hash. It teaches measured firmware integrity checking with a real public image and a real vendor manifest.

AI mentor analysis for original cell 37: Firmware Integrity Verification

This cell verifies the integrity of a downloaded firmware file by comparing its SHA-256 hash against a published manifest. It retrieves both the firmware and the hash manifest from OpenWRT's official release, calculates the hash of the firmware, and checks for a match. This process demonstrates the importance of hash verification in ensuring firmware authenticity and integrity, a critical step in preventing supply chain attacks.

Discussion question: How does hash verification contribute to firmware security?

InĀ [30]:
def shannon_entropy(block: bytes) -> float:
    if not block:
        return 0.0
    counts = np.bincount(np.frombuffer(block, dtype=np.uint8), minlength=256)
    probabilities = counts[counts > 0] / len(block)
    return float(-np.sum(probabilities * np.log2(probabilities)))

window_size = 4096
step_size = 4096
sample_bytes = firmware_bytes[: min(len(firmware_bytes), 1024 * 1024)]
entropy_rows = []
for offset in range(0, len(sample_bytes) - window_size + 1, step_size):
    entropy_rows.append({"offset": offset, "entropy": shannon_entropy(sample_bytes[offset:offset + window_size])})
entropy_df = pd.DataFrame(entropy_rows)
entropy_df["classification"] = np.where(
    entropy_df["entropy"] > 7.5,
    "high entropy: compressed/encrypted/random-like",
    np.where(entropy_df["entropy"] < 1.0, "very low entropy: padding/sparse", "structured code/data"),
)
print("Entropy sample from first MiB of firmware:")
display(entropy_df.head(12))

plt.figure(figsize=(10, 3))
plt.plot(entropy_df["offset"], entropy_df["entropy"], linewidth=1.5)
plt.axhline(7.5, color="red", linestyle="--", linewidth=1, label="high entropy threshold")
plt.title("Firmware Shannon Entropy Profile - first MiB")
plt.xlabel("Byte offset")
plt.ylabel("Entropy bits/byte")
plt.ylim(0, 8.2)
plt.legend()
plt.tight_layout()
plt.show()
Entropy sample from first MiB of firmware:
offset entropy classification
0 0 7.948197 high entropy: compressed/encrypted/random-like
1 4096 7.949938 high entropy: compressed/encrypted/random-like
2 8192 7.954134 high entropy: compressed/encrypted/random-like
3 12288 7.954260 high entropy: compressed/encrypted/random-like
4 16384 7.954951 high entropy: compressed/encrypted/random-like
5 20480 7.954126 high entropy: compressed/encrypted/random-like
6 24576 7.951626 high entropy: compressed/encrypted/random-like
7 28672 7.954324 high entropy: compressed/encrypted/random-like
8 32768 7.953237 high entropy: compressed/encrypted/random-like
9 36864 7.951955 high entropy: compressed/encrypted/random-like
10 40960 7.949364 high entropy: compressed/encrypted/random-like
11 45056 7.954018 high entropy: compressed/encrypted/random-like
No description has been provided for this image

Audio explanation for original cell 38

Transcript: This code computes Shannon entropy over firmware windows and plots the profile. It teaches how entropy highlights compression, encryption, sparse regions, or packed data, while also warning that entropy alone is not a malware verdict.

AI mentor analysis for original cell 38: Shannon Entropy Analysis of Firmware

This cell calculates the Shannon entropy of a firmware file to assess its data structure. High entropy indicates compressed or encrypted data, while low entropy suggests padding or sparse data. The analysis uses a sliding window approach to profile entropy across the firmware, classifying sections based on entropy levels. This technique helps identify potentially suspicious areas within firmware, guiding further investigation.

Discussion question: What insights can entropy analysis provide about firmware data structures?

Plotly visual analytics

This Plotly cell turns firmware entropy into an interactive profile for identifying structural regions that deserve follow-up analysis.

InĀ [31]:
# Plotly visual analytics: interactive firmware entropy profile.
fig = px.line(
    entropy_df,
    x="offset",
    y="entropy",
    color="classification",
    title="Interactive Firmware Entropy Profile - First MiB",
    labels={"offset": "Byte offset", "entropy": "Shannon entropy bits/byte"},
)
fig.add_hline(y=7.5, line_dash="dash", line_color="red", annotation_text="high entropy threshold")
fig.show()

Interpretation: firmware evidence vs scanner signatures¶

Entropy is not a malware detector by itself. High entropy often means compression, not compromise. The useful workflow is to combine entropy with extraction, file signatures, hash manifests, SBOM/package correlation, vendor advisories, and runtime evidence.

This is also where zero-days enter the discussion. A zero-day has no published CVE signature at first. Detection shifts toward behavior, exploit preconditions, protocol anomalies, memory-safety crash signals, and compensating controls.

Audio explanation for original cell 39

Transcript: This interpretation cell explains the limits of firmware signatures. It teaches that high entropy must be combined with extraction, manifests, SBOMs, advisories, runtime evidence, and zero-day-aware behavioral reasoning.

AI mentor analysis for original cell 39: Interpreting Firmware Evidence vs Scanner Signatures

This markdown cell discusses the limitations of using entropy as a standalone indicator of compromise. It emphasizes the need to combine entropy analysis with other evidence types, such as file signatures, hash manifests, and runtime observations, to form a comprehensive security assessment. The discussion also touches on the challenges of detecting zero-day vulnerabilities, which require behavioral analysis and anomaly detection rather than reliance on known signatures.

Discussion question: How can multiple evidence types be integrated to improve firmware security assessments?

Mermaid concept diagram before original cell 40: Protocol Grammar Inference

Use this diagram before the PCAP cells to explain how raw packets become structural protocol evidence.

Mermaid concept diagram before original cell 40: Protocol Grammar Inference

9. Protocol Grammar Inference from PCAP¶

Concept¶

Protocol grammar inference attempts to learn message structure from examples. It supports protocol-aware fuzzing, passive asset identification, anomaly detection, and safe test generation.

The internal workflow is:

  1. Capture frames or load PCAP evidence.
  2. Extract protocol stack, frame lengths, fields, timing, and payload bytes.
  3. Align messages by direction or message type.
  4. Identify fixed fields, length fields, command bytes, delimiters, checksums, and variable payload regions.
  5. Use the inferred grammar to drive safer tests and better detection.

This section uses a real public Wireshark DHCP PCAP and local tshark, then performs a simple byte-position profile directly from the pcap bytes.

Audio explanation for original cell 40

Transcript: This protocol grammar concept cell explains how message structure can be inferred from captures. It teaches the workflow from PCAP evidence to fields, frame geometry, byte alignment, and safer protocol-aware fuzzing.

AI mentor analysis for original cell 40: Protocol Grammar Inference from PCAP

This markdown cell introduces the concept of protocol grammar inference from PCAP files. It outlines the process of extracting protocol structures from captured network traffic, which aids in fuzzing, asset identification, and anomaly detection. The workflow involves analyzing protocol stacks, message alignment, and identifying key fields. This approach enhances understanding of network protocols and supports the development of safer and more effective security tests.

Discussion question: What are the benefits of inferring protocol grammar from network traffic?

InĀ [32]:
PCAP_URL = "https://raw.githubusercontent.com/wireshark/wireshark/master/test/captures/dhcp.pcap"
pcap_bytes = get_bytes(PCAP_URL, "wireshark_dhcp.pcap", cache_seconds=30 * 24 * 3600)
pcap_path = DATA / "wireshark_dhcp.pcap"
pcap_path.write_bytes(pcap_bytes)

tshark_bin = shutil.which("tshark")
if tshark_bin is None:
    print("tshark is not installed; skipping tshark field extraction.")
    tshark_df = pd.DataFrame()
else:
    cmd = [
        tshark_bin, "-r", str(pcap_path), "-T", "fields",
        "-e", "frame.number",
        "-e", "frame.len",
        "-e", "frame.protocols",
        "-e", "udp.srcport",
        "-e", "udp.dstport",
        "-c", "20",
    ]
    proc = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
    if proc.returncode != 0:
        print(proc.stderr)
        tshark_df = pd.DataFrame()
    else:
        rows = []
        for line in proc.stdout.splitlines():
            parts = line.split("\t")
            while len(parts) < 5:
                parts.append("")
            rows.append({"frame": parts[0], "len": parts[1], "protocols": parts[2], "udp_src": parts[3], "udp_dst": parts[4]})
        tshark_df = pd.DataFrame(rows)
        display(tshark_df)
frame len protocols udp_src udp_dst
0 1 314 eth:ethertype:ip:udp:dhcp 68 67
1 2 342 eth:ethertype:ip:udp:dhcp 67 68
2 3 314 eth:ethertype:ip:udp:dhcp 68 67
3 4 342 eth:ethertype:ip:udp:dhcp 67 68

Audio explanation for original cell 41

Transcript: This code downloads a real Wireshark DHCP PCAP and extracts frame fields with local tshark. It teaches that protocol analysis starts by making raw packets into structured field evidence.

AI mentor analysis for original cell 41: PCAP Analysis with Tshark

This cell demonstrates the use of Tshark, a command-line network protocol analyzer, to extract specific fields from a DHCP PCAP file. The script first checks if Tshark is installed, then uses it to parse the PCAP file and extract fields such as frame number, length, protocols, and UDP ports. The extracted data is stored in a DataFrame for further analysis. This approach highlights the importance of automating packet analysis to quickly identify network behaviors and potential vulnerabilities.

Discussion question: How does automating packet analysis with tools like Tshark enhance vulnerability detection?

InĀ [33]:
def parse_classic_pcap_packets(data: bytes, max_packets: int = 25) -> list[dict[str, Any]]:
    if len(data) < 24:
        return []
    magic = data[:4]
    if magic == b"\xd4\xc3\xb2\xa1":
        endian = "<"
    elif magic == b"\xa1\xb2\xc3\xd4":
        endian = ">"
    else:
        raise ValueError(f"Unsupported pcap magic: {magic.hex()}")
    packets = []
    offset = 24
    index = 0
    while offset + 16 <= len(data) and index < max_packets:
        ts_sec, ts_usec, incl_len, orig_len = struct.unpack(endian + "IIII", data[offset:offset + 16])
        offset += 16
        payload = data[offset:offset + incl_len]
        offset += incl_len
        packets.append({"index": index, "ts_sec": ts_sec, "incl_len": incl_len, "orig_len": orig_len, "payload": payload})
        index += 1
    return packets

packets = parse_classic_pcap_packets(pcap_bytes)
packet_summary = pd.DataFrame([
    {"packet_index": p["index"], "incl_len": p["incl_len"], "orig_len": p["orig_len"], "first_16_bytes": p["payload"][:16].hex()}
    for p in packets
])
display(packet_summary.head(12))

max_profile_len = min(64, max((len(p["payload"]) for p in packets), default=0))
profile_rows = []
for pos in range(max_profile_len):
    values = [p["payload"][pos] for p in packets if len(p["payload"]) > pos]
    if not values:
        continue
    counts = Counter(values)
    common_byte, common_count = counts.most_common(1)[0]
    profile_rows.append({
        "byte_position": pos,
        "observed_packets": len(values),
        "unique_values": len(counts),
        "most_common_hex": f"0x{common_byte:02x}",
        "most_common_ratio": common_count / len(values),
    })
byte_profile_df = pd.DataFrame(profile_rows)
print("Byte-position stability profile. Fixed fields have low unique_values and high common_ratio.")
display(byte_profile_df.head(24))
packet_index incl_len orig_len first_16_bytes
0 0 314 314 ffffffffffff000b8201fc4208004500
1 1 342 342 000b8201fc42000874adf19b08004500
2 2 314 314 ffffffffffff000b8201fc4208004500
3 3 342 342 000b8201fc42000874adf19b08004500
Byte-position stability profile. Fixed fields have low unique_values and high common_ratio.
byte_position observed_packets unique_values most_common_hex most_common_ratio
0 0 4 2 0xff 0.50
1 1 4 2 0xff 0.50
2 2 4 2 0xff 0.50
3 3 4 2 0xff 0.50
4 4 4 2 0xff 0.50
5 5 4 2 0xff 0.50
6 6 4 1 0x00 1.00
7 7 4 2 0x0b 0.50
8 8 4 2 0x82 0.50
9 9 4 2 0x01 0.50
10 10 4 2 0xfc 0.50
11 11 4 2 0x42 0.50
12 12 4 1 0x08 1.00
13 13 4 1 0x00 1.00
14 14 4 1 0x45 1.00
15 15 4 1 0x00 1.00
16 16 4 1 0x01 1.00
17 17 4 2 0x2c 0.50
18 18 4 2 0xa8 0.50
19 19 4 4 0x36 0.25
20 20 4 1 0x00 1.00
21 21 4 1 0x00 1.00
22 22 4 2 0xfa 0.50
23 23 4 1 0x11 1.00

Audio explanation for original cell 42

Transcript: This code manually parses classic PCAP bytes and builds a byte-position stability profile. It teaches how fixed headers, variable fields, and candidate grammar boundaries can be inferred from repeated packet observations.

AI mentor analysis for original cell 42: PCAP Packet Parsing and Byte Profiling

This cell introduces a function to parse classic PCAP files, extracting packet metadata and payloads. It identifies the file's endianness using the magic number and iterates through packets, extracting timestamps, lengths, and payloads. The analysis includes a byte-position stability profile, highlighting byte positions with low variability, which may indicate fixed fields. This parsing method is crucial for understanding packet structures and identifying anomalies that could signify security issues.

Discussion question: What are the advantages of profiling byte positions in packet analysis?

Plotly visual analytics

This Plotly cell visualizes packet lengths and byte-position stability for protocol grammar inference.

InĀ [34]:
# Plotly visual analytics: PCAP frame geometry and byte-position stability.
if not packet_summary.empty:
    fig = px.bar(packet_summary, x="packet_index", y="incl_len", title="PCAP Packet Length Geometry", labels={"incl_len": "Captured length"})
    fig.show()
if not byte_profile_df.empty:
    fig = px.line(
        byte_profile_df,
        x="byte_position",
        y="most_common_ratio",
        hover_data=["unique_values", "most_common_hex", "observed_packets"],
        title="Byte-Position Stability Profile for Protocol Grammar Inference",
        labels={"most_common_ratio": "Most-common byte ratio"},
    )
    fig.show()

Mermaid concept diagram before original cell 43: AI-Guided Fuzzing Loop

This diagram explains the safe local fuzzing loop before the toy protocol parser is introduced.

Mermaid concept diagram before original cell 43: AI-Guided Fuzzing Loop

10. LLM-Guided Semantic Mutation Fuzzing, Reinforcement Learning, and Transformers¶

Concept¶

Traditional mutation fuzzing flips bytes and hopes to reach deeper parser states. Protocol fuzzing benefits from semantic mutation: preserve the outer structure while changing length fields, command IDs, checksums, boundary values, and state transitions.

An LLM can assist by reading parser feedback and proposing structured mutations. In a safe production workflow, the LLM should not directly attack third-party targets. It should generate hypotheses, seeds, or templates inside a scoped harness. The harness enforces authorization, rate limits, crash collection, and reproducibility.

This section uses a local toy protocol:

CMD(1 byte) | LEN(1 byte) | DATA(variable) | CHECKSUM(1 byte)

The checksum is sum(frame_without_checksum) mod 256. A special semantic state exists only when the frame is structurally valid and CMD=0x03 with payload CRASH. This is a teaching target, not a real exploit.

Audio explanation for original cell 43

Transcript: This concept cell introduces LLM-guided mutation fuzzing, reinforcement learning, and transformer-based reasoning. It teaches that AI should propose hypotheses inside a scoped harness, not attack arbitrary third-party targets.

AI mentor analysis for original cell 43: LLM-Guided Semantic Mutation Fuzzing

This markdown cell introduces the concept of using large language models (LLMs) to guide semantic mutation fuzzing. Unlike traditional fuzzing, which randomly alters bytes, semantic mutation maintains protocol structure while modifying specific fields. The LLM assists by proposing structured mutations based on parser feedback. This approach is safer and more efficient, as it targets specific protocol states. The cell emphasizes the importance of using a controlled environment to ensure safe fuzzing practices.

Discussion question: How can LLMs improve the efficiency of fuzzing in protocol analysis?

InĀ [35]:
def toy_checksum(data: bytes) -> int:
    return sum(data) % 256


def make_toy_frame(cmd: int, payload: bytes) -> bytes:
    prefix = bytes([cmd & 0xFF, len(payload) & 0xFF]) + payload
    return prefix + bytes([toy_checksum(prefix)])


def parse_toy_frame(frame: bytes) -> dict[str, Any]:
    if len(frame) < 3:
        return {"state": "too_short", "accepted": False}
    cmd = frame[0]
    declared_len = frame[1]
    expected_total = 3 + declared_len
    if len(frame) != expected_total:
        return {"state": "length_mismatch", "accepted": False, "declared_len": declared_len, "actual_len": len(frame)}
    body = frame[:-1]
    expected_crc = toy_checksum(body)
    if frame[-1] != expected_crc:
        return {"state": "bad_checksum", "accepted": False, "expected_crc": expected_crc, "actual_crc": frame[-1]}
    payload = frame[2:-1]
    if cmd not in {1, 2, 3}:
        return {"state": "unknown_command", "accepted": False, "cmd": cmd}
    if cmd == 3 and payload == b"CRASH":
        return {"state": "semantic_crash", "accepted": True, "cmd": cmd, "payload": payload.decode()}
    return {"state": "valid_no_crash", "accepted": True, "cmd": cmd, "payload_len": len(payload)}

seed_frame = make_toy_frame(1, b"PING")
print("Seed frame:", seed_frame.hex(), parse_toy_frame(seed_frame))
Seed frame: 010450494e4733 {'state': 'valid_no_crash', 'accepted': True, 'cmd': 1, 'payload_len': 4}

Audio explanation for original cell 44

Transcript: This code defines the toy protocol, checksum, frame builder, and parser. It teaches the target model for fuzzing: structure, length, payload, checksum, accepted states, rejection states, and a controlled semantic crash state.

AI mentor analysis for original cell 44: Toy Protocol Frame Parsing

This cell defines functions to create and parse frames for a toy protocol, demonstrating checksum calculation and frame validation. The protocol includes a command, length, data, and checksum. The parser checks for length mismatches, invalid checksums, and unknown commands, and identifies a special semantic state when certain conditions are met. This exercise illustrates the importance of robust protocol parsing and validation in detecting malformed or malicious packets.

Discussion question: Why is it important to validate checksums and lengths in protocol parsing?

InĀ [36]:
def mutate_bit_flip(frame: bytes) -> bytes:
    data = bytearray(frame)
    idx = random.randrange(len(data))
    data[idx] ^= 1 << random.randrange(8)
    return bytes(data)


def mutate_length_overflow(frame: bytes) -> bytes:
    data = bytearray(frame)
    if len(data) >= 2:
        data[1] = 0xFF
    return bytes(data)


def mutate_bad_checksum(frame: bytes) -> bytes:
    data = bytearray(frame)
    data[-1] ^= 0xFF
    return bytes(data)


def mutate_command_walk(frame: bytes) -> bytes:
    payload = frame[2:-1] if len(frame) >= 3 else b""
    return make_toy_frame((frame[0] + 1) % 256 if frame else 1, payload)


def mutate_semantic_boundary(frame: bytes) -> bytes:
    return make_toy_frame(3, b"CRASH")

operators = {
    "bit_flip": mutate_bit_flip,
    "length_overflow": mutate_length_overflow,
    "bad_checksum": mutate_bad_checksum,
    "command_walk": mutate_command_walk,
    "semantic_boundary": mutate_semantic_boundary,
}

random_results = []
for i in range(40):
    operator_name = random.choice(["bit_flip", "length_overflow", "bad_checksum", "command_walk"])
    mutated = operators[operator_name](seed_frame)
    feedback = parse_toy_frame(mutated)
    random_results.append({"iteration": i, "operator": operator_name, "mutated_hex": mutated.hex(), **feedback})

semantic_results = []
current = seed_frame
for i in range(6):
    feedback = parse_toy_frame(current)
    if feedback["state"] in {"valid_no_crash", "unknown_command"}:
        operator_name = "semantic_boundary"
    elif feedback["state"] == "bad_checksum":
        operator_name = "command_walk"
    else:
        operator_name = "command_walk"
    current = operators[operator_name](current)
    semantic_results.append({"iteration": i, "operator": operator_name, "mutated_hex": current.hex(), **parse_toy_frame(current)})

print("Random mutator state counts:")
display(pd.DataFrame(random_results)["state"].value_counts().rename_axis("state").to_frame("count"))
print("Semantic mutator trace:")
display(pd.DataFrame(semantic_results))
Random mutator state counts:
count
state
bad_checksum 20
length_mismatch 10
valid_no_crash 10
Semantic mutator trace:
iteration operator mutated_hex state accepted cmd payload
0 0 semantic_boundary 0305435241534879 semantic_crash True 3 CRASH
1 1 command_walk 040543524153487a unknown_command False 4 NaN
2 2 semantic_boundary 0305435241534879 semantic_crash True 3 CRASH
3 3 command_walk 040543524153487a unknown_command False 4 NaN
4 4 semantic_boundary 0305435241534879 semantic_crash True 3 CRASH
5 5 command_walk 040543524153487a unknown_command False 4 NaN

Audio explanation for original cell 45

Transcript: This code compares random mutations with semantic mutations. It teaches why protocol-aware mutation reaches deeper parser states than blind byte flipping when length fields, commands, and checksums must remain coherent.

AI mentor analysis for original cell 45: Mutation Operators for Fuzzing

This cell implements various mutation operators for fuzzing a toy protocol, including bit flipping, length overflow, and checksum alteration. Each operator modifies the frame differently to test the protocol's resilience to malformed inputs. The results are analyzed to determine the effectiveness of each mutation strategy. This approach highlights the role of diverse mutation techniques in uncovering protocol vulnerabilities and improving fuzzing strategies.

Discussion question: How do different mutation strategies affect the outcomes of fuzzing tests?

InĀ [37]:
q_values = defaultdict(float)
counts = defaultdict(int)
observed_states = set()
rl_trace = []
current = seed_frame
operator_names = list(operators.keys())

def choose_operator(epsilon: float = 0.25) -> str:
    if random.random() < epsilon:
        return random.choice(operator_names)
    return max(operator_names, key=lambda name: q_values[name])

for step in range(80):
    op_name = choose_operator()
    mutated = operators[op_name](current)
    feedback = parse_toy_frame(mutated)
    state = feedback["state"]
    reward = 0.0
    if state not in observed_states:
        reward += 2.0
        observed_states.add(state)
    if feedback.get("accepted"):
        reward += 1.0
    if state == "semantic_crash":
        reward += 10.0
    counts[op_name] += 1
    alpha = 1.0 / counts[op_name]
    q_values[op_name] = q_values[op_name] + alpha * (reward - q_values[op_name])
    current = mutated if feedback.get("accepted") else seed_frame
    rl_trace.append({"step": step, "operator": op_name, "state": state, "reward": reward, "q_value": q_values[op_name]})

q_table = pd.DataFrame([{"operator": op, "q_value": q_values[op], "count": counts[op]} for op in operator_names]).sort_values("q_value", ascending=False)
print("Reinforcement-learning operator preferences after local fuzzing:")
display(q_table)
display(pd.DataFrame(rl_trace).tail(12))
Reinforcement-learning operator preferences after local fuzzing:
operator q_value count
4 semantic_boundary 11.032787 61
3 command_walk 1.166667 6
1 length_overflow 0.400000 5
2 bad_checksum 0.333333 6
0 bit_flip 0.000000 2
step operator state reward q_value
68 68 semantic_boundary semantic_crash 11.0 11.037037
69 69 length_overflow length_mismatch 0.0 0.400000
70 70 semantic_boundary semantic_crash 11.0 11.036364
71 71 bad_checksum bad_checksum 0.0 0.333333
72 72 semantic_boundary semantic_crash 11.0 11.035714
73 73 semantic_boundary semantic_crash 11.0 11.035088
74 74 semantic_boundary semantic_crash 11.0 11.034483
75 75 command_walk unknown_command 0.0 1.166667
76 76 semantic_boundary semantic_crash 11.0 11.033898
77 77 semantic_boundary semantic_crash 11.0 11.033333
78 78 semantic_boundary semantic_crash 11.0 11.032787
79 79 bit_flip bad_checksum 0.0 0.000000

Audio explanation for original cell 46

Transcript: This code implements an epsilon-greedy reinforcement-learning loop over mutation operators. It teaches how rewards for novelty, acceptance, and crash discovery can guide future fuzzing actions.

AI mentor analysis for original cell 46: Reinforcement Learning in Fuzzing

This cell applies reinforcement learning to optimize fuzzing strategies by adjusting operator preferences based on feedback. It uses a Q-learning approach to assign rewards for discovering new states or achieving specific outcomes, such as a semantic crash. The Q-values guide the selection of mutation operators, improving the efficiency of the fuzzing process. This method demonstrates how reinforcement learning can enhance fuzzing by dynamically adapting strategies based on past successes.

Discussion question: What are the benefits of using reinforcement learning to guide fuzzing operations?

Plotly visual analytics

This Plotly cell visualizes parser-state coverage and reinforcement-learning operator preferences from local fuzzing feedback.

InĀ [38]:
# Plotly visual analytics: fuzzing state coverage and RL operator values.
random_state_counts = pd.DataFrame(random_results)["state"].value_counts().reset_index()
random_state_counts.columns = ["state", "count"]
fig = px.bar(random_state_counts, x="state", y="count", color="state", title="Random Mutator Parser-State Coverage")
fig.update_layout(xaxis_tickangle=-25)
fig.show()
fig = px.bar(q_table, x="operator", y="q_value", color="count", title="RL Mutation Operator Preference After Feedback")
fig.update_layout(xaxis_tickangle=-25)
fig.show()

Mermaid concept diagram before original cell 47: Transformer Self-Attention

Use this diagram to explain attention as relationships among protocol tokens before the NumPy implementation.

Mermaid concept diagram before original cell 47: Transformer Self-Attention

Transformer and GenAI concept¶

A transformer is useful because self-attention can learn which fields in a sequence influence each other. In protocol analysis, the length byte should attend to the data region, a checksum should attend to every covered byte, and a command byte may attend to a state-specific payload.

The miniature NumPy example below is not a trained LLM. It implements the core attention calculation:

Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V

That mechanism is the foundation used by larger GenAI and LLM systems. In a vulnerability workflow, transformers can classify reports, summarize evidence, extract affected products, suggest fuzzing mutations, or generate graph queries. They still need scoped execution controls and source-grounded outputs.

Audio explanation for original cell 47

Transcript: This transformer concept cell explains self-attention. It teaches why transformers are relevant to protocol analysis: fields such as length bytes, payload bytes, commands, and checksums influence each other across a sequence.

AI mentor analysis for original cell 47: Transformer and GenAI Concept

This markdown cell explains the application of transformers in protocol analysis, focusing on self-attention mechanisms. Transformers can identify dependencies between fields, such as length bytes and data regions, enhancing protocol understanding. The cell outlines potential uses of transformers in vulnerability workflows, including report classification and evidence extraction. This highlights the transformative potential of AI in cybersecurity, particularly in automating complex analytical tasks.

Discussion question: How can transformers improve the analysis of protocol structures in cybersecurity?

InĀ [39]:
tokens = ["CMD_WRITE", "LEN_05", "DATA_C", "DATA_R", "DATA_A", "DATA_S", "DATA_H", "CRC_OK"]
vocab = {token: i for i, token in enumerate(sorted(set(tokens)))}
embed_dim = 8
rng = np.random.default_rng(7)
embedding_table = rng.normal(0, 0.5, size=(len(vocab), embed_dim))
X_tok = np.vstack([embedding_table[vocab[token]] for token in tokens])
Wq = rng.normal(0, 0.5, size=(embed_dim, embed_dim))
Wk = rng.normal(0, 0.5, size=(embed_dim, embed_dim))
Wv = rng.normal(0, 0.5, size=(embed_dim, embed_dim))
Q = X_tok @ Wq
K = X_tok @ Wk
V = X_tok @ Wv
scores = (Q @ K.T) / math.sqrt(embed_dim)
scores = scores - scores.max(axis=1, keepdims=True)
attention = np.exp(scores) / np.exp(scores).sum(axis=1, keepdims=True)
context = attention @ V
attention_df = pd.DataFrame(np.round(attention, 3), index=tokens, columns=tokens)
print("Self-attention matrix over toy protocol tokens:")
display(attention_df)

plt.figure(figsize=(8, 5))
plt.imshow(attention, cmap="viridis")
plt.xticks(range(len(tokens)), tokens, rotation=45, ha="right")
plt.yticks(range(len(tokens)), tokens)
plt.colorbar(label="attention weight")
plt.title("Transformer-Style Self-Attention Over Protocol Tokens")
plt.tight_layout()
plt.show()
Self-attention matrix over toy protocol tokens:
CMD_WRITE LEN_05 DATA_C DATA_R DATA_A DATA_S DATA_H CRC_OK
CMD_WRITE 0.109 0.129 0.088 0.117 0.092 0.182 0.163 0.120
LEN_05 0.141 0.141 0.121 0.147 0.087 0.121 0.101 0.139
DATA_C 0.115 0.096 0.131 0.076 0.239 0.106 0.137 0.100
DATA_R 0.115 0.117 0.188 0.178 0.103 0.103 0.088 0.108
DATA_A 0.104 0.101 0.067 0.048 0.233 0.176 0.186 0.085
DATA_S 0.151 0.125 0.086 0.179 0.051 0.139 0.116 0.154
DATA_H 0.124 0.143 0.118 0.118 0.110 0.148 0.121 0.117
CRC_OK 0.110 0.120 0.126 0.120 0.134 0.149 0.134 0.106
No description has been provided for this image

Audio explanation for original cell 48

Transcript: This code implements the self-attention equation with NumPy over toy protocol tokens. It teaches the core transformer mechanism without requiring a large model or GPU, and connects attention weights to protocol-field relationships.

AI mentor analysis for original cell 48: Self-Attention in Toy Protocol Tokens

This cell demonstrates a basic implementation of self-attention using NumPy, applied to tokens of a toy protocol. It calculates attention scores, showing how each token influences others. The visualization of the attention matrix provides insights into token relationships, such as command and data dependencies. This exercise illustrates the core mechanism of transformers, emphasizing their ability to model complex dependencies in sequences, which is crucial for protocol analysis and vulnerability detection.

Discussion question: What insights can self-attention provide in understanding protocol token dependencies?

Plotly visual analytics

This Plotly cell visualizes transformer-style attention across protocol tokens.

InĀ [40]:
# Plotly visual analytics: transformer-style protocol-token attention.
fig = px.imshow(
    attention_df.astype(float),
    text_auto=True,
    color_continuous_scale="Viridis",
    title="Interactive Self-Attention Heatmap Over Protocol Tokens",
)
fig.update_layout(xaxis_title="Key token", yaxis_title="Query token")
fig.show()

Mermaid concept diagram before original cell 49: Evidence Graph and AR Export

This diagram explains why vulnerability evidence becomes a graph and how that graph can feed later visual interfaces.

Mermaid concept diagram before original cell 49: Evidence Graph and AR Export

11. Evidence Graphs and AR-Ready Export¶

Concept¶

Graph analytics are a natural representation for vulnerability evidence:

  • Asset nodes connect to observed services.
  • Services connect to CPEs.
  • CPEs connect to CVEs.
  • CVEs connect to KEV, EPSS, exploit references, controls, owners, tickets, and compensating detections.

The same graph can support prioritization, blast-radius analysis, attack-path reasoning, and augmented-reality or 3D visualizations. The export below includes 3D coordinates so it can be loaded by a future Streamlit, WebGL, or AR viewer.

Audio explanation for original cell 49

Transcript: This evidence graph concept cell explains why vulnerabilities are naturally graph-shaped. It teaches how assets, services, CPEs, CVEs, KEV signals, EPSS values, and actions connect into an analysis graph.

AI mentor analysis for original cell 49: Evidence Graphs and AR-Ready Export

This markdown cell discusses the use of graph analytics to represent vulnerability evidence, connecting assets, services, CPEs, and CVEs. Graphs facilitate prioritization, attack-path analysis, and visualization, including augmented reality. The cell emphasizes the versatility of graphs in cybersecurity, enabling comprehensive analysis and visualization of complex relationships between vulnerabilities and their contexts. This approach underscores the importance of structured data representation in vulnerability management.

Discussion question: How do graph representations enhance the analysis and visualization of vulnerability data?

InĀ [41]:
G = nx.DiGraph()
asset_id = "asset:lab-openssh-router"
service_id = "service:ssh:22"
cpe_id = f"cpe:{selected_cpe}"
G.add_node(asset_id, label="Lab OpenSSH Router", type="asset")
G.add_node(service_id, label="SSH TCP/22", type="service")
G.add_node(cpe_id, label=selected_cpe, type="cpe")
G.add_edge(asset_id, service_id, relation="exposes")
G.add_edge(service_id, cpe_id, relation="identified_as")

for _, row in kev_triage.sort_values("triage_score", ascending=False).head(10).iterrows():
    cve_node = f"cve:{row['cve_id']}"
    G.add_node(cve_node, label=row["cve_id"], type="cve", base_score=float(row["base_score"]), epss=float(row["epss"]), in_kev=bool(row["in_kev"]))
    G.add_edge(cpe_id, cve_node, relation="affected_by")
    if row["in_kev"]:
        kev_node = "intel:CISA_KEV"
        G.add_node(kev_node, label="CISA KEV", type="intel")
        G.add_edge(cve_node, kev_node, relation="listed_in")
    action_node = f"action:patch-or-mitigate:{row['cve_id']}"
    G.add_node(action_node, label=f"Patch or mitigate {row['cve_id']}", type="action", triage_score=float(row["triage_score"]))
    G.add_edge(cve_node, action_node, relation="drives")

positions = nx.spring_layout(G, dim=3, seed=7)
graph_payload = {
    "metadata": {
        "course": "SEAS-8414",
        "chapter": 3,
        "description": "AR-ready vulnerability evidence graph export with 3D coordinates.",
        "selected_cpe": selected_cpe,
    },
    "nodes": [
        {
            "id": node,
            "label": attrs.get("label", node),
            "type": attrs.get("type", "unknown"),
            "attributes": {k: v for k, v in attrs.items() if k not in {"label", "type"}},
            "position": {"x": float(positions[node][0]), "y": float(positions[node][1]), "z": float(positions[node][2])},
        }
        for node, attrs in G.nodes(data=True)
    ],
    "edges": [
        {"source": u, "target": v, "relation": attrs.get("relation", "related")}
        for u, v, attrs in G.edges(data=True)
    ],
}
graph_path = OUT / "chapter03_evidence_graph_ar_ready.json"
graph_path.write_text(json.dumps(graph_payload, indent=2))
print(f"Graph exported: {graph_path}")
print(f"Nodes: {G.number_of_nodes()}, edges: {G.number_of_edges()}")

type_counts = pd.Series([attrs.get("type", "unknown") for _, attrs in G.nodes(data=True)]).value_counts().rename_axis("node_type").to_frame("count")
display(type_counts)
Graph exported: notebook-runtime/seas8414_ch03_master_teaching/outputs/chapter03_evidence_graph_ar_ready.json
Nodes: 23, edges: 22
count
node_type
cve 10
action 10
asset 1
service 1
cpe 1

Audio explanation for original cell 50

Transcript: This code builds a NetworkX evidence graph and exports AR-ready JSON with 3D coordinates. It teaches how graph analytics can feed future Streamlit, WebGL, or augmented-reality visualizations.

AI mentor analysis for original cell 50: Building and Exporting an Evidence Graph

This cell constructs a directed graph representing vulnerability evidence, linking assets, services, CPEs, and CVEs. It includes nodes for actions and intelligence sources, with edges denoting relationships like exposure and impact. The graph is exported with 3D coordinates for potential use in AR applications. This exercise demonstrates the practical application of graph theory in cybersecurity, enabling detailed analysis and visualization of vulnerability data for better decision-making and communication.

Discussion question: What are the advantages of using 3D graphs for visualizing vulnerability evidence?

Plotly visual analytics

This Plotly cell visualizes the AR-ready evidence graph in 3D and summarizes node types.

InĀ [42]:
# Plotly visual analytics: AR-ready 3D evidence graph coordinates.
graph_nodes = pd.DataFrame(graph_payload["nodes"])
graph_nodes["x"] = graph_nodes["position"].apply(lambda p: p["x"])
graph_nodes["y"] = graph_nodes["position"].apply(lambda p: p["y"])
graph_nodes["z"] = graph_nodes["position"].apply(lambda p: p["z"])
fig = px.scatter_3d(
    graph_nodes,
    x="x",
    y="y",
    z="z",
    color="type",
    hover_name="label",
    title="AR-Ready 3D Vulnerability Evidence Graph Coordinates",
)
fig.show()
node_type_counts = graph_nodes["type"].value_counts().reset_index()
node_type_counts.columns = ["type", "count"]
fig = px.bar(node_type_counts, x="type", y="count", color="type", title="Evidence Graph Node-Type Mix")
fig.show()
InĀ [43]:
plt.figure(figsize=(9, 6))
pos2d = {node: (coords[0], coords[1]) for node, coords in positions.items()}
node_colors = []
color_map = {"asset": "#4c78a8", "service": "#72b7b2", "cpe": "#f58518", "cve": "#e45756", "intel": "#54a24b", "action": "#b279a2"}
for node, attrs in G.nodes(data=True):
    node_colors.append(color_map.get(attrs.get("type", "unknown"), "#bab0ac"))
nx.draw_networkx_nodes(G, pos2d, node_color=node_colors, node_size=450, alpha=0.9)
nx.draw_networkx_edges(G, pos2d, arrows=True, arrowstyle="-|>", width=1.0, alpha=0.5)
labels = {node: attrs.get("label", node).replace("cpe:2.3:a:openbsd:openssh:", "OpenSSH ")[:30] for node, attrs in G.nodes(data=True)}
nx.draw_networkx_labels(G, pos2d, labels=labels, font_size=7)
plt.title("Chapter 3 Evidence Graph")
plt.axis("off")
plt.tight_layout()
plt.show()
No description has been provided for this image

Audio explanation for original cell 51

Transcript: This code plots the evidence graph in two dimensions. It teaches how the same graph can become an instructor-facing visualization of asset, service, vulnerability, intelligence, and remediation relationships.

AI mentor analysis for original cell 51: Visualizing Evidence Graph

This cell generates a visual representation of an evidence graph using NetworkX and Matplotlib. It assigns colors to nodes based on their type, such as assets, services, and vulnerabilities, using a predefined color map. The graph is drawn with nodes, directed edges, and labels, which are truncated for readability. This visualization aids in understanding the relationships and interactions between different cybersecurity elements, providing a clear overview of the network's security posture. It is a simulated/local visualization, not based on live data.

Discussion question: How can visualizing an evidence graph enhance our understanding of network vulnerabilities?

Mermaid concept diagram before original cell 52: Commercial IoT and OT Platform Pattern

Use this diagram to compare scanner, passive, agent, and enrichment layers in commercial platforms.

Mermaid concept diagram before original cell 52: Commercial IoT and OT Platform Pattern

12. Commercial IoT/OT Platform Patterns¶

Concept¶

Commercial IoT/OT security platforms usually combine several detection modes because no single mode is sufficient:

Platform family Common internal pattern Strength Limitation
Greenbone/OpenVAS and Nessus-style scanners Active probes, plugin feeds, authenticated checks, report normalization Direct vulnerability evidence and broad CVE coverage Operational risk if scans are not tuned; false positives from weak version evidence
ProjectDiscovery/Nuclei-style engines YAML templates, matchers, extractors, fast HTTP/protocol checks Rapid community-scale checks and CI integration Template quality and authorization discipline matter
Tenable OT / Microsoft Defender for IoT / Claroty / Armis style platforms Passive network sensors, asset inventory, protocol decoding, threat intel, vulnerability mapping Safer for fragile OT/IoT environments and strong asset visibility May infer versions indirectly; often needs integrations for patch truth
Agent or authenticated inventory platforms Local package, kernel, registry, container, and SBOM evidence Strong patch-state evidence Deployment friction and credential/agent lifecycle management

The high-level architecture is typically:

  1. Discover assets through passive traffic, active scans, agents, cloud APIs, or import connectors.
  2. Identify device type, vendor, model, firmware, OS, services, and software.
  3. Normalize evidence to product identifiers such as CPE, PURL, package coordinates, or proprietary fingerprints.
  4. Enrich with CVE, KEV, EPSS, exploit, malware, and vendor advisory intelligence.
  5. Prioritize using exposure, business role, compensating controls, exploitability, and policy.
  6. Integrate into ticketing, SIEM, SOAR, CMDB, patch management, and exception workflows.

For IoT/OT environments, passive discovery is often the default because active scanning can disrupt legacy devices. Advanced platforms still use active checks, but they scope and schedule them carefully.

Audio explanation for original cell 52

Transcript: This commercial platform concept cell compares scanner-style, template-style, passive IoT and OT, and agent-based platforms. It teaches how vendors combine discovery, fingerprinting, vulnerability intelligence, prioritization, and workflow integration.

AI mentor analysis for original cell 52: Commercial IoT/OT Platform Patterns

This markdown cell outlines common patterns in commercial IoT/OT security platforms, highlighting their detection modes, strengths, and limitations. It categorizes platforms into families like scanners, engines, and passive sensors, each with unique capabilities such as vulnerability evidence, rapid checks, and asset visibility. The cell emphasizes the importance of combining multiple detection methods to cover the diverse security needs of IoT/OT environments. This analysis is crucial for practitioners to understand the trade-offs and integration requirements of different security solutions.

Discussion question: What are the key factors to consider when selecting a security platform for IoT/OT environments?

Mermaid concept diagram before original cell 53: Notebook to Streamlit Path

This diagram explains how notebook outputs become reusable application artifacts.

Mermaid concept diagram before original cell 53: Notebook to Streamlit Path

13. Streamlit Conversion Scaffold¶

The notebook is the teaching source. A Streamlit app can reuse the same cached datasets and outputs. The starter file below loads the exported evidence graph and gives you a clean entry point for turning the notebook into an interactive dashboard later.

Audio explanation for original cell 53

Transcript: This Streamlit scaffold concept cell explains the migration path from notebook to application. It teaches that the notebook is the teaching source, while cached outputs and graph artifacts can power a reusable dashboard.

AI mentor analysis for original cell 53: Streamlit Conversion Scaffold

This markdown cell introduces the concept of converting the notebook into a Streamlit app, which can provide an interactive dashboard for vulnerability analytics. It suggests reusing cached datasets and outputs from the notebook to create a dynamic user interface. This approach allows practitioners to leverage the analytical capabilities of the notebook in a more accessible and user-friendly format, facilitating real-time data exploration and decision-making.

Discussion question: What are the benefits of converting a static notebook into an interactive Streamlit app for cybersecurity analysis?

InĀ [44]:
streamlit_app = OUT / "chapter03_streamlit_app.py"
streamlit_source = "\n".join([
    "from pathlib import Path",
    "import json",
    "import pandas as pd",
    "import streamlit as st",
    "",
    "st.set_page_config(page_title='SEAS-8414 Chapter 3 Vulnerability Analytics', layout='wide')",
    "st.title('SEAS-8414 Chapter 3 Vulnerability Analytics')",
    "base = Path.home() / 'Downloads' / 'seas8414_ch03_master_teaching'",
    "graph_path = base / 'outputs' / 'chapter03_evidence_graph_ar_ready.json'",
    "if not graph_path.exists():",
    "    st.warning('Run the notebook first to generate the evidence graph.')",
    "    st.stop()",
    "graph = json.loads(graph_path.read_text())",
    "st.subheader('Evidence Graph Summary')",
    "st.write(graph.get('metadata', {}))",
    "nodes = pd.DataFrame(graph['nodes'])",
    "edges = pd.DataFrame(graph['edges'])",
    "left, right = st.columns(2)",
    "with left:",
    "    st.metric('Nodes', len(nodes))",
    "    st.dataframe(nodes[['id', 'label', 'type']], use_container_width=True)",
    "with right:",
    "    st.metric('Edges', len(edges))",
    "    st.dataframe(edges, use_container_width=True)",
])
streamlit_app.write_text(streamlit_source)
print(f"Starter Streamlit app written to: {streamlit_app}")
print("Run later with: streamlit run", streamlit_app)
Starter Streamlit app written to: notebook-runtime/seas8414_ch03_master_teaching/outputs/chapter03_streamlit_app.py
Run later with: streamlit run notebook-runtime/seas8414_ch03_master_teaching/outputs/chapter03_streamlit_app.py

Audio explanation for original cell 54

Transcript: This code writes a starter Streamlit app that loads the evidence graph and displays nodes and edges. It teaches how to preserve the notebook's evidence pipeline while turning it into an interactive teaching application.

AI mentor analysis for original cell 54: Creating a Streamlit App for Vulnerability Analytics

This code cell generates a starter Streamlit application that loads and displays an evidence graph. It checks for the existence of the graph file and provides a warning if it is absent. The app displays metadata, nodes, and edges from the graph, offering an interactive way to explore the data. This setup allows practitioners to visualize and analyze vulnerability data in a structured format, enhancing their ability to derive insights from complex datasets.

Discussion question: How does an interactive dashboard improve the analysis and communication of vulnerability data?

14. Closing Synthesis¶

Chapter 3 is not about memorizing tools. It is about understanding evidence pipelines.

  • CPE-to-CVE mapping teaches structured vulnerability correlation.
  • KEV and EPSS teach exploit intelligence enrichment.
  • OpenVAS/GVM and Nuclei teach scanner execution models.
  • Default credential checks teach scoped proof validation and operational safety.
  • Firmware integrity teaches supply-chain and embedded evidence limits.
  • Protocol grammar inference teaches how raw wire data becomes structured tests.
  • LLM-guided mutation, RL, transformers, and graphs teach how AI can assist without replacing source-grounded evidence.

The mature practitioner question is always: what exactly did we observe, how reliable is the mapping, what source supports the claim, and what action follows?

Audio explanation for original cell 55

Transcript: This closing synthesis cell ties the chapter together. It teaches that the mature practitioner focuses on observed evidence, mapping confidence, source support, and the action that follows.

AI mentor analysis for original cell 55: Closing Synthesis on Evidence Pipelines

This markdown cell synthesizes the key learnings from Chapter 3, emphasizing the importance of understanding evidence pipelines rather than memorizing tools. It covers various aspects of vulnerability analysis, such as CPE-to-CVE mapping, exploit intelligence, scanner models, and AI-assisted analysis. The cell encourages practitioners to critically evaluate the reliability and source of evidence, highlighting the need for a mature approach to cybersecurity analysis that focuses on actionable insights.

Discussion question: Why is it important to focus on evidence pipelines rather than individual tools in cybersecurity?

Mermaid concept diagram before original cell 56: Audit Trail and Artifacts

Use this diagram at the end to explain how students verify what the notebook fetched and produced.

Mermaid concept diagram before original cell 56: Audit Trail and Artifacts
InĀ [45]:
print("Recent source events:")
display(show_source_events(20))
print("Notebook artifacts:")
for path in sorted(OUT.glob("*")):
    print(path)
Recent source events:
url file source
0 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10146.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10146.yaml cache
1 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10152.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10152.yaml cache
2 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-1021.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-1021.yaml cache
3 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10400.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10400.yaml cache
4 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10486.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10486.yaml cache
5 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10516.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10516.yaml cache
6 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10571.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10571.yaml cache
7 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-1061.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-1061.yaml cache
8 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10708.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10708.yaml cache
9 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-1071.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-1071.yaml cache
10 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10763.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10763.yaml cache
11 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10783.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10783.yaml cache
12 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10812.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10812.yaml cache
13 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10908.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10908.yaml cache
14 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10914.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10914.yaml cache
15 https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/main/http/cves/2024/CVE-2024-10915.yaml notebook-runtime/seas8414_ch03_master_teaching/data/nuclei_CVE-2024-10915.yaml cache
16 https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Default-Credentials/default-passwords.csv notebook-runtime/seas8414_ch03_master_teaching/data/seclists_default_passwords.csv cache
17 https://downloads.openwrt.org/releases/23.05.0/targets/ath79/generic/openwrt-23.05.0-ath79-generic-8dev_carambola2-initramfs-kernel.bin notebook-runtime/seas8414_ch03_master_teaching/data/openwrt-23.05.0-ath79-generic-8dev_carambola2-initramfs-kernel.bin cache
18 https://downloads.openwrt.org/releases/23.05.0/targets/ath79/generic/sha256sums notebook-runtime/seas8414_ch03_master_teaching/data/openwrt_23.05.0_ath79_generic_sha256sums.txt cache
19 https://raw.githubusercontent.com/wireshark/wireshark/master/test/captures/dhcp.pcap notebook-runtime/seas8414_ch03_master_teaching/data/wireshark_dhcp.pcap cache
Notebook artifacts:
notebook-runtime/seas8414_ch03_master_teaching/outputs/__pycache__
notebook-runtime/seas8414_ch03_master_teaching/outputs/ai_cell_enrichment_manifest.json
notebook-runtime/seas8414_ch03_master_teaching/outputs/chapter03_evidence_graph_ar_ready.json
notebook-runtime/seas8414_ch03_master_teaching/outputs/chapter03_streamlit_app.py
notebook-runtime/seas8414_ch03_master_teaching/outputs/nuclei-local-results.jsonl
notebook-runtime/seas8414_ch03_master_teaching/outputs/seas8414-local-firmware-disclosure.yaml

Audio explanation for original cell 56

Transcript: This final code cell displays recent source events and generated artifacts. It teaches auditability: a teaching notebook should show where its data came from and what outputs it produced.

AI mentor analysis for original cell 56: Displaying Recent Source Events and Artifacts

This code cell prints recent source events and lists notebook artifacts, providing a snapshot of the latest updates and outputs. It uses a function to display recent events, likely from a vulnerability database or template repository, and lists files generated during the notebook's execution. This information helps practitioners keep track of changes and outputs, ensuring they have the latest data for analysis and decision-making. The cell demonstrates the importance of maintaining an up-to-date view of security events and artifacts.

Discussion question: How can tracking recent source events and artifacts enhance vulnerability management processes?

Plotly visual analytics

This Plotly cell visualizes the notebook source retrieval audit trail so students can inspect provenance.

InĀ [46]:
# Plotly visual analytics: source retrieval audit trail.
source_df = show_source_events(200)
if not source_df.empty and "source" in source_df.columns:
    source_counts = source_df["source"].fillna("unknown").value_counts().reset_index()
    source_counts.columns = ["source", "count"]
    fig = px.bar(source_counts, x="source", y="count", color="source", title="Notebook Data Retrieval Audit Trail")
    fig.show()