Security engineering

Building seg-test: A PCI DSS Segmentation Evidence Runner

seg-test runs approved checks from defined source VLANs, preserves raw Nmap output, and packages evidence without making compliance decisions.

Diagram of authorized source segments testing a protected CDE boundary and producing an evidence bundle
Approved source VLANs test the CDE boundary, then save the results in an evidence bundle.Original CyberKareem editorial artwork opens in a new tab

For an assessment, a segmentation scan needs a record of where it ran, what it targeted, which probes were used, and which raw files were retained. I built seg-test opens in a new tab to run a defined set of reachability checks from each approved source VLAN and package the output for review.

The Bash runner takes an explicit cardholder data environment (CDE) target list and does not expand scope. This article covers the design in the audited source revision opens in a new tab, including its limits and the changes I would make next.

seg-test collects evidence. It does not determine PCI DSS compliance, certify segmentation, or replace the assessor's methodology and judgment.

Why the evidence matters

PCI DSS is a baseline of technical and operational requirements for protecting payment account data. PCI SSC published PCI DSS v4.0.1 as a limited revision that clarified guidance without adding or deleting requirements (PCI SSC: “Just Published: PCI DSS v4.0.1” opens in a new tab).

Where segmentation is used to isolate the CDE, Requirement 11.4.5 in the standard available through PCI SSC's PCI DSS document library opens in a new tab expects segmentation penetration testing at least every 12 months and after changes to segmentation controls or methods. The work must cover the controls in use, follow the entity's methodology, confirm that isolation is effective, and be performed by a qualified resource with organizational independence. The scan is one part of that assessment.

Modern environments may enforce segmentation across cloud networks, service meshes, identities, and ephemeral workloads. PCI SSC's modern network architecture guidance opens in a new tab covers this wider set of controls. Record the actual source and boundary exercised in every run.

Run once from each approved source VLAN

The script, configuration, CDE list, and evidence stay together. Run the kit inside each source VLAN named in the rules of engagement (ROE) so every result has a recorded source position.

The normal preparation flow is:

  1. Obtain signed authorization and a precise ROE before sending a packet.
  2. Scaffold a configuration with --init <client>.
  3. Populate cde-all.txt with only approved CDE targets, one per line.
  4. Define an optional traceroute anchor and any prior-finding hot spots.
  5. Preview the run with --dry-run.
  6. Enter the approved source VLAN and use --expect-cidr <approved-cidr> as a source-position guard.
  7. Execute the run, review the raw output, and preserve the resulting evidence package.

A sanitized command sequence looks like this:

./seg-test.sh --init <client>
./seg-test.sh --client <client> <source-vlan> --dry-run
sudo ./seg-test.sh \
  --client <client> <source-vlan> \
  --expect-cidr <approved-cidr>

--dry-run prints the configuration, target count, source guard, scan families, hot spots, and evidence path without sending packets or requiring root. Review it before the live run.

Run sequence

1. Fail early on obvious setup errors

The runner validates the client and VLAN labels, requires a matching client configuration, checks that the target list exists and is non-empty, and verifies its dependencies. It can report missing tools or install the relevant packages on Debian/Kali through apt. Real SYN and UDP scans require root.

2. Create a timestamped evidence workspace

Each execution receives its own directory under evidence/, divided into baseline, Nmap, manual, packet-capture, and screenshot locations. The runner starts a transcript and records metadata such as the source label, host, operator, arguments, target count, anchor, expected CIDR, and selected ports.

Those empty directories are reserved for manual follow-up; they do not mean a capture or screenshot was collected.

3. Record the source-side baseline

Before scanning, the script records time, interface addresses, routes, neighbor state, and, when configured, a short traceroute to a CDE anchor. It prints the detected IPv4 source addresses prominently.

If no local interface is inside the approved source CIDR, the run stops. --force bypasses that check and must be documented.

4. Use several reachability checks

The first network phase performs Nmap host discovery. The official Nmap host-discovery documentation opens in a new tab explains why a single ping is insufficient: -sn uses discovery probes without a port scan, while -Pn skips discovery and treats targets as online for later scanning. Firewalls can suppress probes or replies, so “did not answer discovery” is not equivalent to “unreachable.”

The substantive port tests therefore use -Pn and attempt every approved target:

  • A TCP SYN scan checks a fixed set of common and assessment-relevant ports.
  • An optional full TCP scan covers all ports with a rate limit.
  • A selected UDP scan checks a bounded list of infrastructure and management ports unless explicitly skipped.
  • Configured TCP and HTTPS hot spots re-test paths documented in an earlier report.

The runner does not silently expand scope. Nmap consumes the supplied target file, and manual hot spots come from the engagement configuration. That design is useful only if both inputs have been independently reviewed against the ROE.

5. Preserve raw artifacts before summarizing

Nmap writes normal, XML, and grepable output. The script extracts open-port lines into a quick-look summary, but the summary is not the evidence of record; the raw files and transcript are. Finally, it archives the run and writes a SHA-256 digest beside the archive.

This produces a convenient integrity check: later, an assessor can detect a change if the separately trusted digest remains available. A bare hash stored beside the archive does not prove who produced the evidence or when. The package is integrity-checkable, but it is not tamper-evident or tamper-proof.

What seg-test does not do

  • It does not make pass/fail decisions; that is a human reporting task.
  • It does not capture packet traces automatically; ambiguous results may justify a controlled, interactive capture.
  • It does not brute-force, exploit, or run Nmap vulnerability scripts; those actions are outside this runner's role and must be separately authorized.
  • Assessment probes target entries in cde-all.txt, configured hot-spot lists, and the optional traceroute anchor. Review all three against the ROE before a run.

Interpreting the results

An open TCP port demonstrates that the tested source could complete enough of the protocol exchange for Nmap to classify the port at that time. That is strong evidence of a reachable path requiring investigation. It is not automatically a segmentation failure: the connection may be explicitly authorized, mediated, or required, and the assessor must compare it with the approved data flow and control design.

closed usually means the host was reachable but no service accepted the connection. That still demonstrates network reachability; it does not prove isolation.

filtered, silence, and many UDP results are ambiguous. A firewall may have dropped the request, the return path may differ, the host may be down, an intermediate control may rate-limit, or the service may simply not respond to the selected payload. Those observations need correlation with firewall logs, routing, control configuration, and, when authorized, a packet capture from an appropriate observation point.

Report the observation directly: “From approved source position A, target B returned a SYN/ACK on TCP port C at time D.” Then explain whether the path is expected and how it maps to policy.

Known limitations in this revision

  1. The source guard is IPv4 and interface based. It does not prove the actual egress path, VRF, policy identity, tunnel selection, or IPv6 position used by every packet.
  2. --force can bypass the strongest safety check. An exception can turn a correctly labeled run into evidence from the wrong network.
  3. The target list, traceroute anchor, and hot-spot entries are trust boundaries. The runner does not cryptographically bind them to the signed ROE, enforce that the anchor is in the target list, or prove that every entry is authorized.
  4. The client environment file is sourced as shell code. A modified file executes with the runner's privileges.
  5. Root increases impact. A typo, compromised dependency, or malicious configuration executes in a highly privileged context.
  6. -Pn -sn is not independent reachability proof. Because -Pn treats targets as online and -sn disables port scanning, that artifact must not be read as a successful response from every listed host.
  7. A fixed common-port list is incomplete. Segmentation leakage on an unlisted port will be missed unless the optional full TCP scan is approved and completed.
  8. Selected UDP coverage is inherently narrow and ambiguous. Silent UDP services and filtered responses can produce false reassurance.
  9. The full-scan rate is static. It can be too aggressive for fragile controls or unnecessarily slow elsewhere.
  10. No automatic packet capture exists. The evidence may be insufficient to distinguish a dropped probe, asymmetric return path, proxy response, or scanner-side problem.
  11. A local timestamp is not trusted time. Clock drift or manipulation weakens chronology unless time synchronization is verified and recorded.
  12. A sidecar SHA-256 file authenticates neither origin nor custody. Anyone able to replace the archive can also replace its digest.
  13. A failed command can still leave an archive that looks complete. The script does not use fail-fast execution or a machine-readable completeness manifest for every scan stage.
  14. VLAN checks do not cover every modern control plane. Cloud security groups, service meshes, identity-aware proxies, workloads that appear and disappear, and control-plane paths may not be exercised from a traditional source VLAN.

These limits belong in the test plan and final report.

What I would change next

Bind runs to authorization. Replace free-form targets with a validated manifest containing approved CIDRs, source positions, permitted protocols, change ticket, ROE identifier, and engagement window. Normalize every target before Nmap sees it, reject unexpected syntax, and require hot spots to fall inside the same allowlist. Store hashes of the manifest, configuration, target list, and exact script revision in run metadata.

Record stage completion. Capture each command's start time, end time, exit code, and output files in a machine-readable run manifest. Mark interrupted or failed stages as incomplete and refuse to label their archive as a completed run. Correct the discovery phase so -sn is used for observed discovery and -Pn is reserved for scans that must test every target regardless of discovery response.

Strengthen evidence provenance. Record Nmap and operating-system versions, scanner image or package versions, UTC time, and time-sync status. Sign the final manifest and archive with a detached signature whose public key and verification procedure are maintained outside the engagement kit. Store the signed digest in a separate evidence system so archive and checksum cannot be replaced together without detection.

Keep result states separate. Parse Nmap XML into a structured finding table while retaining all raw artifacts. Use explicit states such as observed-open, observed-closed, filtered, no-response, tool-error, and not-tested. Do not collapse those states into a generated “pass.” Add a reviewer field and require a reason for every allowed reachable path.

Add targeted corroboration. Offer an opt-in packet-capture mode for specific approved targets and ports, with data-minimization limits, capture duration, storage warnings, and a separate authorization gate. Add collection hooks for firewall logs and policy snapshots rather than trying to infer the entire control path from scanner output.

Cover modern controls. Extend the test plan beyond VLANs to IPv6, cloud accounts and regions, security groups, Kubernetes network policies, service-mesh identities, remote-access paths, and ephemeral assets. The runner should consume an assessor-defined coverage matrix and report which source-to-destination control paths were actually exercised.

Role in an assessment

seg-test standardizes evidence collection across approved source VLANs; the assessor remains responsible for authorization, scope, interpretation, exceptions, and the final conclusion.

Source: CyberKareem/seg-test opens in a new tab. Audited revision: db8d0cce2c3a06af20d3974a9cc59611f4415099 opens in a new tab

Further reading

Evidence connected to this article.

Back to article start