README
12 — Noise Lab
A Docker-based test lab that empirically measures actual network packet counts, timing patterns, and protocol breakdowns for 30+ scanning techniques, comparing results against the theoretical OPSEC risk ratings (ZERO/LOW/MEDIUM/HIGH) claimed in 4 pentesting recon scenario documents.
54 techniques tested. 489-line empirical report produced.
Note: This lab lives in the main git repository, but
results/andcaptures/are excluded from version control via.gitignore— they are too large to commit and are regenerated by running the test suite locally. The lab infrastructure (Dockerfiles, scripts, tests) is fully committed; only the generated output data is excluded.
Project Goal
Verify whether the OPSEC risk ratings in the 4 source documents (1. External.md through 4. C2_SOCKS.md) hold up under measurement. The output is 5. Noise_Analysis_Report.md — an annotated report comparing claimed ratings against measured packet counts, PPS, and protocol breakdowns.
Directory Structure
noise-lab/
├── docker-compose.yml
├── target/
│ ├── Dockerfile
│ ├── supervisord.conf
│ ├── smb.conf
│ ├── named.conf.local
│ ├── db.scanlab.local
│ ├── ldap-bootstrap.ldif
│ ├── nginx.conf
│ └── vsftpd.conf
├── pivot/
│ ├── Dockerfile
│ └── sshd_config
├── scripts/
│ ├── capture.sh # Start/stop tcpdump on sniffer container
│ ├── analyze-pcap.sh # Parse pcap → JSON (total_packets, avg_pps, peak_pps, protocols)
│ ├── run-test.sh # Run a single test: capture + scan + analyze
│ ├── run-all.sh # Run all 4 scenario test suites end-to-end
│ ├── aggregate-results.sh # Combine all JSON results → summary.csv + comparison reports
│ └── smoke-test.sh # 7-point infrastructure health check
├── tests/
│ ├── scenario1.sh # 11 tests — External recon (1. External.md)
│ ├── scenario2.sh # 18 tests — Internal no-creds (2. Internal_NoCreds.md)
│ ├── scenario3.sh # 12 tests — Authenticated pivot (3. Authenticated_Pivot.md)
│ ├── scenario4.sh # 7 tests — C2/SOCKS (4. C2_SOCKS.md)
│ ├── scenario5.sh # 10 tests — Alternative scanners (naabu/rustscan/masscan)
│ ├── scenario6.sh # 9 tests — proxychains + SOCKS proxy
│ └── scenario7.sh # 7 tests — SSH -D tunnel (jump server)
├── results/
│ ├── scenario1/ # 11 JSON result files
│ ├── scenario2/ # 18 JSON result files
│ ├── scenario3/ # 12 JSON result files
│ ├── scenario4/ # 7 JSON result files
│ ├── scenario5/ # 10 JSON result files
│ ├── scenario6/ # 9 JSON result files
│ ├── scenario7/ # 7 JSON result files
│ ├── summary.csv # 81 lines (1 header + 80 data rows)
│ ├── technique-ranking.txt
│ └── cross-scenario-comparison.json
└── .sisyphus/
├── plans/noise-lab.md
├── evidence/ # QA evidence files per task
└── boulder.json
Container Network
| Container | IP | Role |
|---|---|---|
| noise-attacker | 172.30.0.10 | Runs all scans |
| noise-pivot | 172.30.0.50 | SSH pivot / jump server (root/toor) |
| noise-target | 172.30.0.100 | Target with HTTP/HTTPS/SMB/FTP/DNS/LDAP/SSH |
| noise-socks-proxy | 172.30.0.60 | microsocks SOCKS5 proxy (built from source) |
| noise-filtered-target | 172.30.0.70 | Firewall-filtered target (most ports dropped) |
| noise-internal-web | 172.30.1.100 | Internal web server (corpnet subnet only) |
| noise-sniffer | (shares target's namespace) | tcpdump packet capture |
The sniffer uses network_mode: "service:target" — it shares the target's network namespace so it can capture all traffic to/from the target directly.
How to Run
Prerequisites
- Docker Desktop (macOS or Linux)
- The sniffer container has
tsharkandcapinfosinstalled via its Dockerfile
Start the lab
cd noise-lab
docker compose up -d
⚠️ After every fresh start: install tools on pivot
docker exec noise-pivot apt-get install -y nmap netcat-openbsd
These are installed in the running container only — they are lost on docker compose down and must be reinstalled each time. This is intentional (keeps the pivot image minimal).
Smoke test (7-point health check)
bash scripts/smoke-test.sh
Expected output: 7/7 PASS
Run a single test
bash scripts/run-test.sh <scenario_name> <test_name> <capture_filter> <scan_command>
# Example:
bash scripts/run-test.sh scenario1 nmap_t4_top1000 "tcp or udp" \
"docker exec noise-attacker nmap -T4 --top-ports 1000 172.30.0.100"
Results are written to results/<scenario_name>/<test_name>.json.
Run all tests for a scenario
bash tests/scenario1.sh # 11 tests
bash tests/scenario2.sh # 18 tests
bash tests/scenario3.sh # 12 tests
bash tests/scenario4.sh # 7 tests
bash tests/scenario5.sh # 10 tests
bash tests/scenario6.sh # 9 tests
bash tests/scenario7.sh # 7 tests
Run everything
bash scripts/run-all.sh
Aggregate results
bash scripts/aggregate-results.sh
Outputs:
results/summary.csv— all 80 results in one tableresults/technique-ranking.txt— techniques ranked by packet countresults/cross-scenario-comparison.json— comparison across scenarios
Access pcap files and JSON results
The captures volume is a Docker named volume, not a host bind mount. Access files via:
docker exec noise-sniffer ls /captures/
docker exec noise-sniffer cat /captures/<file>.json
Key Findings
80 techniques tested across 7 scenarios.
Verdict Breakdown
| Verdict | Count | % |
|---|---|---|
| ✅ CONFIRMED (matches claimed rating) | 14 | 17.5% |
| 🔵 QUIETER_THAN_CLAIMED | 50 | 62.5% |
| 🔴 LOUDER_THAN_CLAIMED | 10 | 12.5% |
| ❓ UNKNOWN_CLAIMED | 6 | 7.5% |
62.5% of techniques are quieter than the docs claim — the source documents are generally conservative (erring toward higher risk ratings). However, 12.5% are louder, with meaningful discrepancies.
Notable Results
| Technique | Claimed | Measured Packets | Verdict |
|---|---|---|---|
passive_sniff_kerberos | ZERO | 0 | ✅ CONFIRMED |
arp_cache | ZERO | 0 | ✅ CONFIRMED |
responder_analyze | ZERO | 0 | ✅ CONFIRMED |
nmap_t4_top1000 | HIGH | 2,009 | ✅ CONFIRMED |
nmap_t1_top100 | LOW | ~60 | 🔵 QUIETER_THAN_CLAIMED |
arp_scan | LOW-MEDIUM | 508 | 🔴 LOUDER (MEDIUM-HIGH) |
c2_60s_sleep_5ports | ZERO | 30 | 🔴 LOUDER (LOW-MEDIUM) |
source_port_53 | LOW | higher than base | 🔴 LOUDER (spoofing ≠ quieter) |
Key Insights
-
arp_scanis louder than claimed. 508 packets for a /24 sweep — the docs rate it LOW-MEDIUM but the measurement supports MEDIUM-HIGH. -
C2 sleep timing is not ZERO noise. Even a 60-second sleep interval with 5-port check-in generates ~30 packets per beacon cycle. ZERO is wrong; LOW-MEDIUM is more accurate.
-
Source-port spoofing does not reduce packet count.
--source-port 53and--source-port 443generate the same or more packets than an unmodified scan. The docs' LOW ratings for these techniques conflate application-layer evasion with network noise reduction. -
Passive techniques are genuinely silent. ARP cache reads, passive Kerberos sniffing (listen-only), and Responder in analyze mode all produce 0 packets. The ZERO ratings are accurate.
-
The pivot's perspective is blind to subnet-wide ARP.
pivot_subnet_smbshows only 3 packets via BPFtcp port 445on the target's eth0 — the HIGH rating in the doc refers to subnet-wide ARP broadcast noise that is not visible from a single target interface.
Source Documents (Read Only)
The 4 scenario documents this lab was built to test live one level up:
../1. External.md
../2. Internal_NoCreds.md
../3. Authenticated_Pivot.md
../4. C2_SOCKS.md
The final empirical report:
../5. Noise_Analysis_Report.md
Do not modify the 4 source documents.
Technical Discoveries & Gotchas
These were found during development and are recorded here to save future debugging time.
| # | Discovery | Fix |
|---|---|---|
| 1 | macOS Docker Desktop bridge interfaces live inside a Linux VM — cannot capture on them from the host | Use network_mode: "service:target" on sniffer to share target's network namespace |
| 2 | docker exec -d is broken for tcpdump with BPF filters | Use nohup ... & echo $! > /tmp/tcpdump.pid inside a synchronous docker exec |
| 3 | tcpdump SIGTERM = 0 packets written (TPACKET_V3 ring buffer, libpcap 1.10.6) | Use SIGINT (kill -INT $pid) to flush the ring buffer; also use --immediate-mode |
| 4 | macOS bash is 3.2.57 — no declare -A associative arrays | Use a case statement lookup in aggregate-results.sh |
| 5 | nmap 7.80 proxy: socks5:// is invalid | Use socks4://host:port only |
| 6 | pkill inside docker exec hangs | Use docker exec -d (detached) for kill/cleanup commands |
| 7 | timeout command not available on macOS | Use gtimeout or a _timeout_cmd() helper function |
| 8 | avg_pps / peak_pps always 0 for fast scans | tshark's 1-second bucket is coarser than sub-second scans — total packet count is the reliable metric |
| 9 | pivot_subnet_smb shows only 3 packets | BPF tcp port 445 on target's eth0 misses ARP broadcasts to other /24 hosts |
Constraints
- No
kalilinux/kali-rollingimage (too large) - No
--privilegedon any container - No exposed container ports to the host
- No Docker multicast (smcroute etc.) — too fragile
- No real C2 frameworks (Cobalt Strike, Sliver, Metasploit)
- All scripts use
set -euo pipefail - No hardcoded paths — all scripts use
$SCRIPT_DIR/$COMPOSE_DIRrelative paths - Samba configured with
dns proxy = no(avoids port 53 conflict with bind9)