One first step when attackers try to tinker with your environment is often referred to as “enumeration”. As I am running multiple web services on my own servers, I am of course curious what all this open source software actually does. So let’s start with enumeration of your web servers.
Naturally, I can deep dive into their source code and evaluate their functionality. However, this post shall show you how to find information on your web servers without looking at the code through enumeration.

Port Scanning

Let’s start with a remote test. Currently I am at work and could only scan my home network from outside. Let’s do this.

Remote enumeration of my home network.
Remote enumeration of my home network.

Looking at my home network from outside, we see that there are several ports open. Let’s redo this detection and try to see wether nmap can get some more information on the services it listed. The -A flag tells nmap to try to detect the operating system, the versions of software used, to scan for (nmap-) known vulnerabilities and to perform a traceroute to estimate our server location. Also, this time I added the flag -T4 which basically tells nmap to use a faster template scheme. This speeds up the analysis but may be less successful in detecting everything.

Host is up (0.011s latency).
Not shown: 994 closed ports
PORT     STATE SERVICE    VERSION
23/tcp   open  ssh        OpenSSH 8.3 (protocol 2.0)
53/tcp   open  domain?
443/tcp  open  ssl/http   nginx 1.18.0
| http-robots.txt: 1 disallowed entry
|_/
|_http-server-header: nginx/1.18.0
|_http-title: Jitsi Meet
| ssl-cert: Subject: commonName=meet.yourhostname.or.ip
| Subject Alternative Name: DNS:meet.yourhostname.or.ip
| Not valid before: 2020-08-18T12:17:00
|_Not valid after:  2020-11-16T12:17:00
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|   h2
|_  http/1.1
| tls-nextprotoneg:
|   h2
|_  http/1.1
2000/tcp open  tcpwrapped
5060/tcp open  tcpwrapped
8008/tcp open  http
| fingerprint-strings:
|   FourOhFourRequest:
|     HTTP/1.1 302 Found
|     Location: https://:8015/nice%20ports%2C/Tri%6Eity.txt%2ebak
|     Connection: close
|     X-Frame-Options: SAMEORIGIN
|     X-XSS-Protection: 1; mode=block
|     X-Content-Type-Options: nosniff
|     Content-Security-Policy: frame-ancestors
|   GenericLines, HTTPOptions, RTSPRequest, SIPOptions:
|     HTTP/1.1 302 Found
|     Location: https://:8015
|     Connection: close
|     X-Frame-Options: SAMEORIGIN
|     X-XSS-Protection: 1; mode=block
|     X-Content-Type-Options: nosniff
|     Content-Security-Policy: frame-ancestors
|   GetRequest:
|     HTTP/1.1 302 Found
|     Location: https://:8015/
|     Connection: close
|     X-Frame-Options: SAMEORIGIN
|     X-XSS-Protection: 1; mode=block
|     X-Content-Type-Options: nosniff
|_    Content-Security-Policy: frame-ancestors
|_http-title: Did not follow redirect to https://yourhostname.or.ip:8015/
|_https-redirect: ERROR: Script execution failed (use -d to debug)

This time some things changed. While in the simple portscan in the beginning, nmap guessed port 23 would be hosting a telnet service, this time it shows us, there is a OpenSSH server with version 8.3 although you would normally expect it on port 22. Second, port 53 seems to host a dns service. Also, nmap detected that port 443 is actually hosting a webserver running TLS and also that there is Jitsi Meet running on it. For ports 2000 and 5060, we still do not know what’s that. Finally, port 8008 has another webserver on it, however we do not have further information on it.

Banner Grabbing

In fact, all of this is correct. But how did nmap get information on the changed port of ssh? The answer here is banner grabbing. That’s the same process that also shodan.io uses.
In short, you open a TCP socket to the port and start receiving bytes from it. I have quickly scripted this in python to demonstrate.

#!/usr/bin/env python3
import socket
import argparse

def main(args):
    addr = (args.host, args.port) 
    s = socket.socket()
    s.connect(addr)
    print(s.recv(1024).decode("utf-8"))

if __name__ == '__main__':
    prs = argparse.ArgumentParser("Banner grabbing in python")
    prs.add_argument("--host", type=str, required=True)
    prs.add_argument("--port", type=int, required=True)
    args = prs.parse_args()
    main(args)

If I run this against the skewed service, we get the same information also nmap got.

Banner grabbing on one services.
Banner grabbing on one services.

So, the remaining question is what changes if we have local access to a server and want to check for available services. Long answer short, not much. I scanned my machine with nmap localhost -A also locally and found some more services which are only published locally or in the internal network. I hope this enables you to get started with nmap and trying to find out, what’s actually on your network. By the way you can also scan a whole network with the CIDR notation like

nmap 192.168.178.1/24

Before your start with happy network scanning, please keep in mind that you should only do this on your own machines and networks.