Nmap against a cloud perimeter: what is worth scanning and what is noise

Nmap still finds the things that matter on a cloud perimeter, but only if you scan the right address space with the right flags. How we build the target list, which scan types earn their time, and how to read the output without wasting a week.

Nmap is thirty years old and still the first tool we reach for when a client asks what their perimeter looks like from outside. Not because port scanning is sophisticated, but because almost every cloud estate has something listening that nobody remembers deploying, and nothing finds it faster.

The hard part is not running Nmap. It is deciding what to point it at.

Build the target list from the cloud, not from DNS

A scan of the client's DNS zone misses everything that was never given a name: load balancers created for a demo, an instance with a public IP attached by a Terraform module default, a NAT gateway that also accepts inbound because a security group says so.

Build the list from the provider's own API instead. On AWS that means the public IPs on EC2 instances, ELBs, NAT gateways, Global Accelerator and RDS instances with PubliclyAccessible set, across every account in the organisation and every enabled region. The regions matter: the instance somebody spun up in ap-southeast-2 during a proof of concept is exactly the one nobody patched.

aws ec2 describe-network-interfaces \
  --query 'NetworkInterfaces[?Association.PublicIp].Association.PublicIp' \
  --output text | tr '\t' '\n' > targets.txt

Do this per account and per region, then add the DNS zone on top. The delta between the two lists is usually the most interesting page of the report: assets that exist but are not in anybody's inventory.

The scan that earns its time

A full TCP scan of every port on every host is a day of scanning to find three services. In practice, two passes work better.

The first is broad and shallow, to find what is alive:

nmap -sS -Pn --top-ports 1000 --open -T4 -iL targets.txt -oA pass1

-Pn because cloud hosts frequently drop ICMP and you will otherwise skip live targets. --open because the closed and filtered results are noise at this stage. -T4 is fine against cloud infrastructure; -T5 starts dropping results.

The second is narrow and deep, against only the hosts that answered:

nmap -sS -sV -sC -p- --open -T4 -iL alive.txt -oA pass2

Full port range this time, with version detection and the default script set. This is where the findings come from: the Elasticsearch on 9200 with no authentication, the Redis on 6379 answering PING, the management interface on 8443 with a self-signed certificate from 2019.

Reading the output without drowning

Three categories are worth separating in the report, because they get different fixes.

Services that should not be reachable from the internet at all. Databases, caches, message brokers, Kubernetes API servers, container registries, anything on a port above 1024 that answers a protocol handshake. The fix is a security group change and, usually, a question about how it got there.

Services that should be reachable but are exposing more than they should. Old TLS versions, server banners with exact software versions, default management paths. --script ssl-enum-ciphers on 443 gives you the TLS finding set in one pass, and it maps directly onto the controls in most compliance frameworks.

Services that answered but should not exist. Test environments on a public IP, the staging copy of the API with production credentials in its environment, the forgotten bastion. These are usually the highest-impact findings and they never appear in an architecture diagram.

What Nmap will not tell you

It will not tell you whether the exposure matters. An open 22 on a bastion behind an identity-aware proxy and an open 22 on an instance with password authentication are the same line of output. The severity comes from what is behind the port, and that needs the configuration review rather than the scan.

It will not find the exposures that are not ports. Public object storage, an over-permissive presigned URL policy, a Lambda function URL with AuthType: NONE — none of these appear in a port scan. Run the posture tooling alongside it; the two sets barely overlap.

And it will not survive the provider's own protections silently. Both AWS and Google Cloud accept scanning of your own assets without prior authorisation, but rate limiting, Shield and any WAF in front of an ALB will distort results. If a host looks uniformly filtered, check whether you are scanning the host or the protection in front of it.

Authorisation, in writing

Scan only address space the client owns or has contractual authority over. Cloud IPs are recycled: an elastic IP released last week belongs to somebody else today, and a scan of it is a scan of a stranger's infrastructure. Re-resolve the target list at the start of every engagement rather than reusing last quarter's file, and keep the authorisation letter with the scope in it next to the scan logs.

That discipline is also what makes the output useful six months later, when somebody asks whether a given host was exposed at the time. The -oA output, the target list and the date are the evidence.

If you want the perimeter mapped and ranked rather than just scanned, that is what our security review does.

ConsultorIA

Want this done on your cloud?

A ten-day read-only assessment is free, and Skyline lets you see your estate on a map before you write to us.

Related articles