Effective Service Enumeration

In this post we will address one of the tasks that haunts every pentester or red team operator, service enumeration. Its challenging task for every red team operator / pentester, specially when confronted with a Class A network address range for the first time.

A typical service enumeration technique is comprised of:

  • Host discovery: Create a list of live hosts (as close to 100%) as possible.
  • Port Scanning: Find the exposed services on the hosts found by the previous step.
  • Version Scan: Determine software names and versions; this assists the operator in prioritizing what services to test first or to leave behind.

When performing pentesting for a big corporations, pentesters usually face budget and time constraints on their analysis; by contrast, threat actors often don’t have these kind of limitations and can run more detailed analysis without any time constraints, and consolidate more information during that time. The lack of time constraints also enable attackers to use slower, less noisy analysis techniques, in an attempt to avoid detection. Regardless, service enumeration is used at different stages (such as lateral movements and black box analysis), and often performed with tools like nmap, masscan, zmap, to name a few. Our approach presented in this post relies on nmap, as we found it is quite reliable in our usage scenarios.

While asynchronous scanning is a hot topic, we found network latency can have a big impact on its reliability, considering slow responses may lead to false negatives, showing ports that are actually open but slow as closed. As such, our solution relies on synchronous scan, opting instead to distribute the task to multiple workers.


Active discovery is usually the second step taken by operators. It revolves around the following steps:

  • Identify dhcp information;
  • Where is the current host located in the network.
  • What is the ipaddress of the host.

Some of this information can be provided by the operating system, and other can be collected by listening to the network with a specialized tool like netdiscovery. The active discovery is contextualized when an operator is able to determine the potential number of neighbours.

The address range and the latency of the network, as well as bandwidth and possible network filters can be critical to the successful completion of this task. There are several common methods of discovery, such as:


By analyzing the amount of internal names, we can try to establish a range of addresses considering the class of address ranges for Class A or class B; This is commonly achieved by splitting the ranges in subtnets /24.

Ping sweeps

There are many tools capable of performing ping sweeps, including nmap. Nmap uses it to create internal statistics to aid its planing and prediction algorithms. However, in modern networks this might be a source of problems due to icmp blocks or slowdowns that will impact the accuracy and time spent on each target.

Port acknowledge

By manually choosing common ports that systems often have open, such as 22 in Linux or 135 or 3389 in Windows, we can use nmap to quickly identify possible alive hosts by focusing on these ports first. This results in shorter analysis time, at the expense of some accuracy.

Port Scanning

I read this once, “port scanning is the equivalent o geting into a building and knock on every door, to see who lives their”, port scanning is the most common and well-known method, and the most common task for the usage of nmap. It involves iterating every port (from all the possible 65536 ports) and check if it is reachable. It is the most extensive method, but with longer execution time and generates more network traffic. A comprehensive pentest will always include a full TCP/UDP scan, but not all companies perform network scans regularly.

As most operators, we also experimented different approaches at automating these methods, using both available and custom made tools and scripts. An approach we found particularly effective is to split ports and ip addresses in ranges, (usually called “staged scan”), making them more manageable.

An example of this approach would be:

0-20,24,26-79,81-109,112-134,136,138,140-442,444,446-1432,1434-2048,2050-3305, \

The need to keep an eye on the scanner and make sure it wasn’t stopped between stages led us to craft some automation for this approach, called Dscan. Dscan centralizes the results and status of a set of scanner workers (called agents), while allowing resume of scans. This alleviates the need of attention from an operator, and it is particularly suited for regular scans, as they can take longer time and their state is managed automatically.


Dscan is a small distributed application, with two components server and agent. The components interact via TCP with SSL. The server coordinates the execution of the agents that will perform the scan tasks, and aggregate the generated results. The tool behaves well in multiple scenarios, ranging from dedicated servers to raspberry pi boxes, enabling unattended scans on available networks.

The solution allows the configuration of any number of stages, requiring always at least one - the discovery stage. This stage is used by the tool to enumerate live hosts and their addresses. These hosts are then grouped in cidr or block range format.

Both server and agent keep a copy of the scan report. When an agent is disconnected, it is added to an internal unfinished pool. If the server fails catastrophically, there is a .trace file that holds a serialized state of the scan process, allowing it to resume its previous state, including unfinished or failed scans.


Configuration steps:

  • generate a certificate and private key for your setup;
  • copy the certificate file to the agents;
  • edit the appropriate settings in the configuration file;

A configuration file example is shown below. The server can generate a boilerplate configuration file, suitable for most tasks, and ready to be adjusted to a specific scenario.

reports = reports

stats = run
targets = ${stats}/targets.work
live-targets = ${stats}/live-targets.work
trace = ${stats}/current.trace

discovery-ports = -PE -PP -PS 21,22,23,25,80,113,31339 -PA 80,113,443,10042
stage1-ports = 80,443,8080
stage2-ports = 25,135,137,139,445,1433,3306,5432
stage3-ports = 23,21,22,110,111,2049,3389
stage4-ports = 0-20,24,26-79,81-109,112-134,136,138,140-442,444,446-1432,1434-2048,2050-3305,3307-3388,3390-5431,5433-8079,8081-29999
stage5-ports = 30000-65535

discovery = -n -sn ${nmap-ports:discovery-ports}
scan-stage1 = -sS -n ${nmap-ports:discovery-ports} -p ${nmap-ports:stage1-ports}
scan-stage2 = -sS -n ${nmap-ports:discovery-ports} -p ${nmap-ports:stage2-ports}
scan-stage3 = -sS -n ${nmap-ports:discovery-ports} -p ${nmap-ports:stage3-ports}
scan-stage4 = -sS -n ${nmap-ports:discovery-ports} -p ${nmap-ports:stage4-ports}
scan-stage5 = -sS -n ${nmap-ports:discovery-ports} -p ${nmap-ports:stage5-ports}

sslcert = certfile.crt
sslkey = keyfile.key
cert-hostname = dscan

Please have a look at the following video, exemplifying Dscan operation:

You can find the code for this project at Dscan