CloudMapper "public" - Command to identify public hosts and ports

2018.06.13

RSS feed

Update: This post is out-dated. Coverage in CloudMapper has improved along with other improvements.

This CloudMapper post introduces the public command which identifies the network resources in accounts that are exposed publicly and shows which ports these are open to. There are a couple of open-source projects that will try to identify all of the public IPs used by an account, with one of the more exhaustive projects being Arkadiy Tetelman’s https://github.com/arkadiyt/aws_public_ips.

Where CloudMapper improves on this is it will identify not just the IP that is public, but also the port that has been made public. This makes it much faster to nmap scan the open ports, or simply look at the port numbers to identify things like exposed RDP services (TCP port 3389). You can also screenshot the web services or do anything else one might do if you knew all the hosts and ports exposed in an account.

Example

Using the demo data that comes with CloudMapper, we get the following output:

$ python cloudmapper.py public --account demo
{
    "account": "demo",
    "arn": "arn:aws:ec2:us-east-1:123456789012:instance/weblb/subnet-00000001",
    "hostname": "weblb.us-west-2.elb.amazonaws.com",
    "ports": "443",
    "type": "elb"
}
{
    "account": "demo",
    "arn": "arn:aws:ec2:us-east-1:123456789012:instance/weblb/subnet-00000002",
    "hostname": "weblb.us-west-2.elb.amazonaws.com",
    "ports": "443",
    "type": "elb"
}

You can see here that two ELB’s are exposed on port 443. This is actually one ELB that is attached to multiple subnets, so you’ll likely want to apply uniqueness filters on this data. For example:

$ python cloudmapper.py public --account demo | jq -r '[.ports, .hostname]|@tsv' | sort | uniq

443	weblb.us-west-2.elb.amazonaws.com

In the CloudMapper demo, we also have a bastion host open on port 22, but it is only open to two CIDRs, so it is not public and therefore does not show up in this output.

How this works

The public command first runs the CloudMapper prepare command behind the scenes which is used for mapping out an account’s network connections in order to display them. Instead of writing to the web/data.json file, it stores this info in memory and then looks for any edges connected to 0.0.0.0/0 and then looks at the edge info for that to identify the ports, and the node info to identify the IP or hostname.

Using this output

One source of inspiration for using this data is to look at what a bug bounty researcher would do if they knew all the hosts and ports at a company that are pubicly exposed. Jason Haddix has a great presentation from the LevelUp conference called Bug Bounty Hunter Methodology v3 where he describes how he finds the hosts and ports of a company and what he does with that info. As a bug bounty researcher he is approaching this problem with less information than you have as the owner of the AWS account. I’ll discuss a few ideas here.

nmap scanning

One useful thing to do with this output is confirm whether or not the ports are actually in use. If they are not being used, you should close up their Security Groups. You also will want to identify what service is hosted on the port, as you might find high port numbers open which are being used to “secretly” host a service, such as SSH for a bastion, or a test web interface.

nmap is commonly used tool for performing these scans and fingerprinting. nmap does not have functionality to receive a hostname and associated port ranges (see issue #1217). So your options are to either scan all ports on the hosts (which can be slower) or repeatedly run nmap with different hosts and port ranges.

To use nmap, first convert the output, then use the ./utils/nmap_scan.sh to scan these:

python cloudmapper.py public --account demo | jq -r '.hostname+":"+.ports' | sort | uniq > /tmp/host_ports.txt
./utils/nmap_scan.sh /tmp/host_ports.txt

Example output:

# Nmap 7.70 scan initiated Wed Jun 13 09:44:27 2018 as: nmap -sV -oG - -PN -p80 4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud
Host: 35.165.182.7 (ec2-35-165-182-7.us-west-2.compute.amazonaws.com)	Status: Up
Host: 35.165.182.7 (ec2-35-165-182-7.us-west-2.compute.amazonaws.com)	Ports: 80/open/tcp//http//nginx 1.10.0 (Ubuntu)/
# Nmap done at Wed Jun 13 09:44:34 2018 -- 1 IP address (1 host up) scanned in 6.67 seconds

We can see in the above that the given host (an EC2 running at 35.165.182.7) is running a http server (nginx) on port 80.

Screenshots

In the presentation by Jason Haddix, he shows how to use the tool EyeWitness (see 48:07 in his presentation). As a quick and dirty alternative, we can use headless Firefox by running:

python cloudmapper.py public --account demo | jq -r '.hostname' | sort | uniq | xargs -I{} ./utils/screenshot.sh {}

This will create screenshots of only the services running on port 80 in a directory ./screenshots/ where the filename is the hostname with a .png extension, such as google.com.png.

Limitations

CloudMapper does not support IPv6 currently, and only looks at TCP and UDP ports. It ignores ICMP or any other protocols. It does not consider network ACL rules.

CloudMapper’s public command also does not support some services such as API Gateway, Cloudfront, Lightsail, ElasticSearch, and Redshift, for those, I recommend using https://github.com/arkadiyt/aws_public_ips CloudMapper public currently only supports the services EC2, ELB, ELBv2, and RDS.

Next steps

Try it out by cloning CloudMapper from https://github.com/duo-labs/cloudmapper