In 03-01 we obtained a table of open ports per host: on dev (10.10.10.15) we saw 22, 80, 3306, 8080, and a mysterious 54321; on db-interno (10.10.10.40) 53 and 161/UDP; on the store 80 and 443. But that 3306/tcp mysql is just a label Nmap slaps on based on the port number: anyone can run any service on any port. We don't know which version is running, which web server serves port 80, or what on earth 54321 is.

Enumerating services is answering exactly that: for each open port, what service it is, what version, what operating system hosts it, and what information it exposes. The deliverable of this lesson is an inventory of services with versions —the piece we'll cross-reference against known vulnerabilities in 03-03—. Here we identify and inventory; we still don't assess or prioritize vulnerabilities. And, as always, within scope: enumeration also generates traffic and some probes (resource brute-forcing, massive SNMP queries) can be intrusive, so they're agreed on and documented.

Contents

  1. From open port to identified service
  2. Version detection with Nmap (-sV)
  3. Operating system detection (-O)
  4. Manual banner grabbing
  5. Per-protocol enumeration: overview
  6. HTTP/HTTPS: whatweb, nikto, and NSE
  7. SMB: enum4linux and smbclient
  8. SNMP: snmpwalk
  9. Others: FTP, SSH, SMTP, DNS, MySQL
  10. TechNova's service inventory
  11. Defensive counterpart
  12. Common mistakes and tips
  13. Exercises
  14. Conclusion

  1. From open port to identified service

Port scanning tells you where something is listening; enumeration tells you what is listening and what it says about itself. The difference is enormous for a pentester:

  • "3306 is open" → I don't know if it's exploitable.
  • "3306 is MySQL 5.5.62, no TLS, and accepts connections from the network" → now I can hunt for concrete vulnerabilities in that version.

The version is the most valuable piece of data in this phase, because known vulnerabilities are tied to specific versions (we'll see this in 03-03 with CVE and CPE). That's why the central goal is to sharpen each open port down to a service + version.

  1. Version detection with Nmap (-sV)

-sV makes Nmap, after finding an open port, interrogate the service by sending probes and comparing the responses against its signature database (nmap-service-probes). It returns the real service and, often, the exact version.

nmap -sV -p 22,80,3306,8080,54321 10.10.10.15
PORT      STATE SERVICE    VERSION
22/tcp    open  ssh        OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp    open  http       Apache httpd 2.4.29 ((Ubuntu))
3306/tcp  open  mysql      MySQL 5.5.62-0ubuntu0.14.04.1
8080/tcp  open  http       Jetty 9.4.z-SNAPSHOT
54321/tcp open  http       Werkzeug httpd 0.14.1 (Python 3.6.9)

Line by line:

  • 22 ... OpenSSH 7.6p1 Ubuntu: not just "ssh", but the exact version and that the system is Ubuntu (a free OS data point).
  • 80 ... Apache httpd 2.4.29: the web server is Apache 2.4.29.
  • 3306 ... MySQL 5.5.62: the database is no longer a label: it's MySQL 5.5.62, an old version (a lead for 03-03).
  • 8080 ... Jetty 9.4.z: a Java Jetty server, probably a different application.
  • 54321 ... Werkzeug httpd 0.14.1 (Python 3.6.9): the mystery is solved: the odd port is a Python/Flask web app (Werkzeug is its development server). Running the development server on an exposed port is in itself a sign of misconfiguration.

Useful options: --version-intensity 0-9 tunes how many probes it sends (higher intensity = more accurate but noisier); -sV --version-light is a fast compromise.

  1. Operating system detection (-O)

-O tries to guess the OS by analyzing details of the TCP/IP stack (fingerprinting): initial TTL values, window size, TCP options... Each OS has a distinct "fingerprint".

sudo nmap -O 10.10.10.15
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4
OS details: Linux 4.15 - 5.6
Network Distance: 1 hop
  • Running: Linux 4.X|5.X: it's a Linux with a kernel in the 4 or 5 series.
  • OS CPE: cpe:/o:linux:linux_kernel:4: the CPE appears, the standardized identifier we'll use in 03-03 to search for vulnerabilities.
  • Network Distance: 1 hop: it's one hop away (same network), consistent with the internal audit.

A widely used shortcut is -A, which combines -sV -O plus default scripts and traceroute:

sudo nmap -A -p 22,80,3306 10.10.10.15

It's convenient, but noisier; in stealthy scenarios it's preferable to launch -sV and -O separately and controlled.

  1. Manual banner grabbing

You don't always need Nmap. Many services introduce themselves on connection (the banner). Connecting by hand with netcat or openssl confirms versions and sometimes reveals details Nmap summarizes:

# SSH banner: shown as soon as you connect
nc 10.10.10.15 22
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3
# HTTP headers: we request and read the server's response
printf 'HEAD / HTTP/1.0\r\n\r\n' | nc 10.10.10.15 80
HTTP/1.1 200 OK
Server: Apache/2.4.29 (Ubuntu)
X-Powered-By: PHP/7.2.24

The header X-Powered-By: PHP/7.2.24 is gold: it confirms that behind Apache there's PHP 7.2 (the unsupported version we already suspected of the store). For services over TLS, use openssl s_client -connect host:443 instead of nc.

  1. Per-protocol enumeration: overview

From the version onward, each protocol has its own enumeration techniques to extract information. A map of what to look at depending on the port:

Port Service What to enumerate Tools
80/443 HTTP/S Technologies, paths, files, headers whatweb, nikto, nmap NSE, dirb
139/445 SMB Shares, users, policies enum4linux, smbclient, nmap NSE
161/UDP SNMP Interfaces, processes, users, routes snmpwalk, onesixtyone
21 FTP Anonymous access, version, listings nmap NSE, ftp
22 SSH Version, authentication methods nmap NSE, banner
25 SMTP Users (VRFY/EXPN), open relay nmap NSE, smtp-user-enum
53 DNS Records, zone transfer dig, nslookup, nmap NSE
3306 MySQL Version, users, remote access nmap NSE, mysql

Nmap ships with the NSE (Nmap Scripting Engine): categorized scripts. Here we use the enumeration safe category (not vuln, which belongs to 03-03). The scripts live in /usr/share/nmap/scripts/ and are invoked with --script.

  1. HTTP/HTTPS: whatweb, nikto, and NSE

The store is the star target, so its web enumeration is a priority. First whatweb to identify technologies:

whatweb http://tienda.technova.lab
http://tienda.technova.lab [200 OK] Apache[2.4.29], PHP[7.2.24],
Country[RESERVED][ZZ], HTTPServer[Ubuntu][Apache/2.4.29 (Ubuntu)],
JQuery[1.12.4], Script, X-Powered-By[PHP/7.2.24]

It confirms Apache 2.4.29, PHP 7.2.24, and jQuery 1.12.4 (all old versions). Then nikto, a web server scanner that looks for known files and configurations (we use it here as reconnaissance, not as a vulnerability verdict):

nikto -h http://tienda.technova.lab
+ Server: Apache/2.4.29 (Ubuntu)
+ /admin/: Directory indexing found.
+ /config.php.bak: Backup file found, possibly discloses credentials.
+ /phpinfo.php: Output from the phpinfo() function was found.
+ Cookie PHPSESSID created without the httponly flag.

Very useful enumeration findings: an /admin/ directory with listing, a config.php.bak (possible credential leak), and a phpinfo.php (reveals the entire PHP configuration). With NSE we broaden the picture using safe scripts:

nmap --script "http-title,http-headers,http-methods,http-enum" -p 80 tienda.technova.lab
80/tcp open  http
| http-title: TechNova - Tienda Online
| http-methods:
|   Supported Methods: GET HEAD POST OPTIONS
| http-enum:
|   /admin/: Possible admin folder
|   /login.php: Possible admin login

  1. SMB: enum4linux and smbclient

If ports 139/445 show up on a Windows or Samba host, SMB is a very rich source. At TechNova there are workstations and a network controller; let's suppose 10.10.10.55 (a workstation) has them open. enum4linux automates the enumeration:

enum4linux -a 10.10.10.55
[+] Server allows sessions using username '', password ''  (null session)
 ...
[+] Users via RID cycling:
  TECHNOVA\Administrador (RID 500)
  TECHNOVA\jlopez (RID 1103)
  TECHNOVA\mgarcia (RID 1104)
[+] Share Enumeration:
  Sharename   Type    Comment
  ---------   ----    -------
  RRHH        Disk    Documentos RRHH
  IPC$        IPC     IPC Service

A null session (session without credentials) lets us list users (jlopez and mgarcia from recon reappear!) and shares like RRHH. With smbclient we try to access the share (within scope):

smbclient //10.10.10.55/RRHH -N
smb: \> ls
  nominas_2025.xlsx    A    48210
  altas_bajas.docx     A    15320

Being able to list RRHH with -N (no password) is a serious configuration flaw. We document it; exploiting it in depth is the subject of later modules.

  1. SNMP: snmpwalk

In 03-01 we saw 161/UDP (SNMP) open on db-interno. SNMP with the default community (public) exposes an enormous dump of internal information. First we guess the community with onesixtyone, then snmpwalk:

onesixtyone 10.10.10.40 public
snmpwalk -v2c -c public 10.10.10.40
SNMPv2-MIB::sysDescr.0 = STRING: Linux db-interno 4.15.0-112-generic
HOST-RESOURCES-MIB::hrSWRunName.42 = STRING: mysqld
IP-MIB::ipAdEntAddr.10.10.10.40 = IpAddress: 10.10.10.40
TCP-MIB::tcpConnLocalPort = INTEGER: 3306

SNMP hands us: the exact OS (Linux 4.15.0-112), the running processes (mysqld running), the network interfaces, and the local ports. It's internal enumeration for almost free, which is why SNMP with public is a classic finding.

  1. Others: FTP, SSH, SMTP, DNS, MySQL

A sweep of other common protocols with NSE and native clients:

# FTP: check anonymous access and version
nmap --script ftp-anon,ftp-syst -p 21 10.10.10.60

# SMTP: enumerate valid users and check for open relay
nmap --script smtp-commands,smtp-open-relay -p 25 correo.technova.lab

# DNS: server version
dig @10.10.10.40 version.bind chaos txt

# MySQL: version and capabilities (safe/info script)
nmap --script mysql-info -p 3306 10.10.10.15

Example output of mysql-info:

3306/tcp open  mysql
| mysql-info:
|   Protocol: 10
|   Version: 5.5.62-0ubuntu0.14.04.1
|   Capabilities flags: 63487
|   Salt: k5*Lp2...

It confirms MySQL 5.5.62 from the protocol itself (not just from -sV), and that it accepts connections from the network —a database shouldn't be exposed like this—.

  1. TechNova's service inventory

We consolidate everything into the lesson's deliverable: an inventory of services with versions. Sorted, it's the direct input to 03-03.

Host Port Service Version OS Enumeration notes
tienda.technova.lab 80 Apache 2.4.29 Ubuntu PHP 7.2.24, jQuery 1.12.4, /admin/, config.php.bak
tienda.technova.lab 443 Apache (TLS) 2.4.29 Ubuntu Same vhost over HTTPS
10.10.10.15 (dev) 22 OpenSSH 7.6p1 Ubuntu
10.10.10.15 (dev) 80 Apache 2.4.29 Ubuntu PHP app
10.10.10.15 (dev) 3306 MySQL 5.5.62 Ubuntu Exposed to the network
10.10.10.15 (dev) 8080 Jetty 9.4.z Ubuntu Java app
10.10.10.15 (dev) 54321 Werkzeug/Flask 0.14.1 (Py 3.6.9) Ubuntu Exposed development server
10.10.10.40 (db) 161/UDP SNMP v2c community public Linux 4.15 Internal dump accessible
10.10.10.55 (wks.) 445 Samba/SMB Windows Null session, RRHH share

Notice: we go from "open ports" to a who's-who with versions ready to search for CVEs. That's exactly the material the next lesson needs.

  1. Defensive counterpart

All this enumeration rests on services giving away too much information. The corresponding hardening:

  • Hide banners and versions. ServerTokens Prod in Apache, remove X-Powered-By from PHP, neutral banners on SSH/SMTP. It doesn't stop the attack, but it makes mapping harder.
  • Close SMB null sessions (restrict anonymous), disable SMBv1, and don't expose shares like RRHH without authentication.
  • SNMP: change the public community, use SNMPv3 with authentication and encryption, or disable it if unused.
  • Don't expose databases (bind-address 127.0.0.1 in MySQL); never leave development servers (Werkzeug, phpinfo.php, .bak) on reachable systems.
  • Detection: enumeration with -sV and NSE generates full connections and recognizable patterns; a well-tuned IDS and the logging of anomalous access (many requests to nonexistent paths, external SNMP queries) give it away.

  1. Common Mistakes and Tips

  • Trusting the SERVICE by port number. 3306 might not be MySQL. Always confirm with -sV or banner grabbing.
  • Jumping straight to --script vuln. That's 03-03. Here the job is to inventory with safe/enumeration scripts; mixing phases muddies the work.
  • Ignoring UDP and SNMP. SNMP with public writes half the report for you, and many people never look at it because it's UDP.
  • Web enumeration without context. nikto and whatweb spew a lot of noise; note what's relevant (versions, paths, sensitive files) and discard the rest.
  • Undocumented null sessions. If a null session gives you users and shares, it's a finding in itself: record it, don't take it for granted.
  • Being too intrusive. Some probes (aggressive directory brute-forcing, massive SNMP queries) load the service. Tune the intensity and respect the window.
  • Tip: keep a living inventory in a table (host/port/service/version/notes) from the very first command. That document is literally the input to 03-03.

  1. Exercises

Exercise 1. You have dev's 8080/tcp open but Nmap only said "http-proxy" in 03-01. Write the command to identify the service and version of that port and explain what information you'd expect to get and why it's more useful than "http-proxy".

Exercise 2. On 10.10.10.55 an enum4linux -a returns a null session with the user list and an RRHH share accessible with smbclient -N. Classify what is enumeration (proper to this lesson) and what would already be exploitation (later modules), and what you should do here and now.

Exercise 3. Justify, with two concrete examples from TechNova's inventory, why the version is the most valuable data point in this phase looking ahead to the next lesson (03-03).

Solutions

Solution 1.

nmap -sV --version-intensity 7 -p 8080 10.10.10.15

-sV interrogates the service and compares it against Nmap's signatures; with high intensity it sharpens the version. We'd expect something like Jetty 9.4.z-SNAPSHOT (a Java server) instead of the generic "http-proxy" label. It's more useful because "http-proxy" is just a guess by port number, whereas "Jetty 9.4.z" identifies the real software and its version, which is what enables searching for concrete vulnerabilities in 03-03.

Solution 2. Enumeration (this lesson): discovering the null session exists, listing users (jlopez, mgarcia, Administrador), and listing/reading the names in the RRHH share. All of that is inventorying exposed information. Exploitation (later modules): using those users for password attacks (Module 4), or downloading and exfiltrating the RRHH files as part of an access. Here and now: document the flaw (SMB allows a null session and exposes RRHH without authentication) with evidence, and add it to the inventory; don't go beyond what scope authorizes in this phase.

Solution 3. Example A: MySQL 5.5.62 — without the version we'd only know "there's a database"; with the version we can search the NVD/exploit-db for the CVEs specific to MySQL 5.5.62 (03-03). Example B: PHP 7.2.24 / Apache 2.4.29 — concrete unsupported versions whose known CVEs we'll be able to list and prioritize. The version is the search key that connects a service with its list of known vulnerabilities; without it, the next lesson's vulnerability detection would be guesswork.

Conclusion

We've transformed the table of open ports into an inventory of services with versions: we no longer have "3306 open", but "MySQL 5.5.62 exposed to the network on an Ubuntu 4.15 host"; not "80 open", but "Apache 2.4.29 with PHP 7.2.24, with a listable /admin/ and a config.php.bak"; and we've solved mysteries like 54321 (a Werkzeug/Flask development server). Along the way we used -sV, -O, manual banner grabbing, and protocol-specific enumeration (whatweb, nikto, enum4linux, smbclient, snmpwalk, NSE), always identifying and inventorying, without yet assessing severity.

And that's exactly the boundary we cross now. We have a list of software with concrete versions, many of them visibly old (MySQL 5.5, PHP 7.2, jQuery 1.12, an exposed development server, SMB with a null session, SNMP public). The question is no longer "what's there", but "which of this is vulnerable, how severe, and where is it best to start".

In 03-03 Vulnerability Detection, the last lesson of the module, we'll cross this inventory against the known-vulnerability databases (CVE, CPE, NVD, exploit-db), run scanners (Nessus/OpenVAS, nmap --script vuln, nuclei) and, above all, critically validate the results to separate the false positive from the real finding. The result will be a prioritized list of candidate vulnerabilities: the bridge toward the exploitation of Module 4.

© Copyright 2026. All rights reserved