Hunting vanity IP addresses on AWS for fun and profit!

Charlie Anderson
4 min readNov 15, 2018

--

Amazon recently bought the 3.0.0.0/8 IPv4 block, for use within their EC2 service. The initial discussion of this on Hacker News raised the interesting possibility of getting a vanity IP address, i.e. an address with an interesting pattern, such as 3.1.4.1 – the first 4 digits of pi. This is the story of my attempt at this, and the results.

Where are they allocated?

The first step in this journey was to work out what subsections of the address space had been allocated, and where. AWS conveniently publishes this list as a JSON file, which we can quickly search (you can see the full format of the file or just open the link in your browser). Each IP range they use is in this file as a small object:

{
"ip_prefix": "18.208.0.0/13",
"region": "us-east-1",
"service": "AMAZON"
}

Our quick script to download and search this looks something like:

from ipaddress import IPv4Network, IPv4Address
import requests
json = requests.get("https://ip-ranges.amazonaws.com/ip-ranges.json").json()
prefixes = json['prefixes']
for p in prefixes:
if IPv4Address('3.1.4.1') in IPv4Network(p['ip_range']):
print(f"Network {p['ip_range']} in region {p['region']} for use with {p['service']} has a vanity IP")

This downloads the file, parses the JSON, and searches each address range to see if it contains our magic pi address, using the wonderfully easy to use ipaddress module. Running this shows that the 3.0.0.0/15 block is in use for EC2 in the ap-southeast-1 region – ideal! With this information, we can begin to search for our vanity IP.

Getting the IP allocated

AWS has the concept of Elastic IPs, which are IPs you get to keep allocated to you, even if they’re not attached to an instance. They’re free to have if attached to a running instance, and they cost a small amount each month to keep reserved otherwise. The API for getting an EIP is very simple, and allows you to attempt to grab a specific IP:

$ aws ec2 allocate-address --address '3.1.4.1' --region 'ap-southeast-1'

Unfortunately, this comes back with an error! Either someone has already grabbed it, or the API doesn’t allow us to allocate an IP address that it hasn’t previously given us. We can, however, get ourselves a random IP address in the range used in the ap-southeast-1 region:

$ aws ec2 allocate-address --region 'ap-southeast-1'

So while the first few digits of pi aren’t available, we could attempt to find ourselves some sort of nice looking IP. There’s a whole world of possibilities out there — 3.0.0.3, 3.1.1.3 and others could well be available for the taking…

Searching the address range

The algorithm I ended up running was very simple. Allocate a random EIP, check if it was “nice”, for some definition of nice, and if it wasn’t, deallocate it again. First we should define what we think nice is:

interesting_addresses = [
# variations on pi (3.14159265359)
'3.1.4.1',
'3.1.4.15',
'3.1.4.16',
'3.1.4.2',
'3.14.15.9',
'3.14.15.92',
'3.14.15.93',
'3.141.59.27',
'3.141.59.26',
]
def is_nice(addr):
"""True if the address is nice, false otherwise"""
digits = addr.replace(".", "")
octets = [int(x) for x in addr.split(".")]
if addr in interesting_address:
return True
# all digits the same, e.g. 3.3.3.3
if all([a == b for a in digits for b in digits]):
return True
# digits palindromic, e.g. 3.51.15.3
if list(digits) == list(reversed(digits)):
return True
# octets palindromic, e.g. 3.14.14.3
if octets[0] == octets[3] and octets[1] == octets[2]:
return True
# x.x.y.y, e.g. 3.3.4.4
if octets[0] == octets[1] and octets[2] == octets[3]:
return True
# ascending or descending by 1 or 2, e.g. 3.4.5.6
for i in range(-2, 3):
if octets[3] == octets[2] + i \
and octets[2] == octets[1] + i \
and octets[1] == octets[0] + i:
return True
return False

Then we can start allocating ourselves IP addresses, and checking if they’re nice. The key to this is that allocating and deallocating IPs is free — AWS only charge for keeping the EIP unattached or attaching it to an instance (‘remapping’). This allows us to freely search forever until an IP turns up:

import boto3
ec2 = boto3.client('ec2', region_name='ap-southeast-1')
while True:
# We don't want EC2-classic, the default
resp = ec2.allocate_address(Domain='vpc')
ip = resp['PublicIp']

print(f"Allocated address '{ip}'")
if is_nice(ip):
print(f"We found a nice IP {ip}")
else:
ec2.release_address(AllocationId=resp['AllocationId'])

For a more involved script we would want to program in some exponential back-off into the API calls, but we seem to be able to get away with it here.

The Results

For me, absolutely nothing other than a small bill for the technically nice, but ultimately uninteresting 18.136.136.18. However, it was a fun distraction for a few hours, and who knows – maybe you might get lucky…

The full code for the script, with a few additions, is available on GitHub — if you do get something nice, let me know in the comments!

--

--