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