Skip to main content
← Back to lab
SEC401 - Vulnerability Management and Response | Printable command sheet
Lab 3.1 - Network Discovery

Lab 3.1 - Network Discovery

Network Security | SEC401 | Apr 2026

Ran Nmap against a /24 lab network: identified 7 live hosts, enumerated services (OpenSSH, MySQL, nginx, php-fpm), attempted OS fingerprinting, baselined results to XML, and used ndiff to catch a newly exposed Python WSGI server on port 8000. Confirmed the finding by SSHing in and reviewing netstat, iptables, and the served page.

Tools: Nmap, ndiff, ssh, netstat, iptables, curl

Commands

1. Lab environment startup

Launched the lab stack from /sec401/labs/3.1. The start script brought up seven Docker containers: webapp, docs, database, old-database, php-fpm, php-nginx, and a student container used as the scanning host.

cd /sec401/labs/3.1/ && ./start_3.1.sh

2. Ping sweep: discover live hosts

Ran a host-discovery-only scan against the /24. Nmap reported 7 hosts up in 1.38 seconds, each with a 172.28.14.x address, matching the containers launched by the start script.

nmap -sn 172.28.14.0/24
-sn: ping scan, no port scan 172.28.14.0/24: 256-address lab subnet

3. Greppable port sweeps

Demonstrated two greppable-output scans: --top-ports 100 and the -F fast scan. Both ran against the default target (none supplied on the CLI), so they returned zero hosts. The point was to capture the exact port list each scan covers in the header comment for documentation.

nmap -v --top-ports 100 -oG -
nmap -v -F -oG -
-v: verbose --top-ports 100: scan the 100 most common TCP ports -F: fast scan (~top 100 from nmap-services) -oG -: greppable output to stdout

4. Service and version detection

Enumerated services on every live host: OpenSSH 8.9p1 on the docs host, MySQL 5.7.41/5.7.44 on the two database hosts, nginx 1.14.2 on php-nginx, an unknown cslistener on php-fpm:9000, and http/https on the webapp. CPE entries (cpe:/o:linux:linux_kernel) confirm a Linux target fleet.

nmap -sV 172.28.14.0/24
-sV: probe open ports for service/version info

5. OS detection: strict match

Ran a strict OS fingerprint. Nmap collected a full TCP/IP signature for each host but reported 'No exact OS matches' because containers don't present a clean kernel fingerprint over the network.

nmap -O 172.28.14.0/24
-O: OS fingerprinting based on TCP/IP stack behavior

6. OS detection: aggressive guess

Re-ran with --osscan-guess. Nmap returned probability-weighted matches: Linux 2.6.32 (96%), Linux 3.2-4.9 (96%), with odd long-tail guesses like AXIS 210A network camera and Synology DiskStation. Useful reminder that OS detection degrades badly against virtualized or containerized hosts.

nmap -O --osscan-guess 172.28.14.0/24
--osscan-guess: print closest matches even when no exact match

7. Baseline scan saved to XML

Saved a second version scan to new_network.xml. XML output is the format ndiff consumes and the format most asset-management pipelines ingest.

nmap -sV -oX new_network.xml 172.28.14.0/24
-oX: XML output file

8. ndiff: detect scan-over-scan change

Compared an older baseline (network.xml, Nov 2023) to new_network.xml (Apr 2026). ndiff surfaced a new listener on the docs host: 8000/tcp open http WSGIServer 0.2 (Python 3.10.12). This is the exact signal an asset inventory wants, a service that wasn't there before, on a host you thought you understood.

ndiff network.xml new_network.xml
ndiff: Nmap-aware diff of two XML scans, lines prefixed with + for added and - for removed

9. SSH on a non-standard port

The docs host was advertising SSH on port 80, not 22. Connected with ssh -p 80 and authenticated into the Ubuntu 22.04 container. Non-standard service ports defeat naive scanners that only check well-known ports, which is exactly why -sV matters.

ssh -p 80 root@172.28.14.23
-p 80: connect to SSH running on port 80

10. Post-compromise: netstat on target

From inside the docs host, listed listening sockets. Confirmed python3 listening on 0.0.0.0:8000, sshd on :80, a loopback resolver on 127.0.0.11:39563, and an established SSH session from the scanning host (172.28.14.1).

netstat -anp
-a: all sockets; -n: numeric addresses; -p: show owning process/PID

11. iptables rules for the new service

Reviewed the INPUT chain. Port 8000 is only reachable from 172.28.14.23 (self) and loopback; every other source gets REJECT tcp-reset. That explains why the WSGI service only became visible once the scan originated from inside the lab subnet, it was firewalled from external sources.

iptables -n -L
-n: numeric output (no DNS/port name lookup) -L: list rules

12. Retrieve the served page

curled localhost:8000 from the docs host. Response was the 'Alpha Developers' internal documentation portal built with mkdocs-material 9.4.14. Confirms what the port, the process, and the ndiff finding all hinted at: an internal docs site that should never have been exposed beyond loopback.

curl localhost:8000

Key Findings

  • 7 live hosts on 172.28.14.0/24 mapped to the lab's container stack
  • SSH running on TCP/80 on the docs host, a classic port-obfuscation pattern
  • php-fpm:9000 exposed as cslistener, a service usually kept internal
  • ndiff surfaced a new WSGIServer on docs:8000 between scans
  • iptables restricted 8000/tcp to the host itself, firewall-layer control working as intended

Security Controls

  • Asset inventory and CMDB accuracy
  • Host-based firewalls (iptables / nftables) restricting service exposure
  • Change-detection pipelines (ndiff, diff-based alerting)
  • Service hardening: don't expose dev/docs sites on 0.0.0.0
  • Non-standard port hygiene: document, don't rely on, port obscurity