> For the complete documentation index, see [llms.txt](https://melbadry9.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://melbadry9.gitbook.io/blog/dangling-dns/aws/ddns-ec2-current-state.md).

# Dangling DNS: Amazon EC2 IPs (Current State)

## Note

* Shortly after writing my last blog [post](/blog/dangling-dns/aws/ddns-ec2.md), I kept getting messages about creating Proof-of-concept (PoC) for this kind of issue, And I thought that I had covered most of the cases, but it turned out I was wrong. So in the post, I’ll try to tear down the technical details for this issue from a bug hunter perspective and then work on automating the process so that bug hunter can apply on a scale.
* The IP address `3.5.140.229` will be used as an example during this blog post.
* Most of the scripts are written in Python or Bash.

## EC2-Based Subdomain Takeover

Let’s start with how *EC2-based* subdomain takeover differs from common subdomain takeover issues (If subdomain takeover is a new term for you, I recommend *Patrik Hudak* [Blog](https://0xpatrik.com/subdomain-takeover-basics/)).

In standard subdomain takeover, we hunt. `CNAME`, `MX`or `NS` records, while in *EC2-based* subdomain takeover, we hunt `A` record.

## Fingerprinting Phase

So how we detect if the subdomain is *EC2-based*? There is three possible ways for fingerprinting part:

1. Subdomain has `CNAME` record which match one of the following regex

{% tabs %}
{% tab title="Python" %}

```python
r'ec2-[-\d]+\.compute[-\d]*\.amazonaws\.com'
r'ec2-[-\d]+\.[\w\d\-]+\.compute[-\d]*\.amazonaws\.com'
```

{% endtab %}
{% endtabs %}

If you are a fan of [Nuclei](https://nuclei.projectdiscovery.io) templates like me, I have built a template for fingerprinting *EC2-based* subdomains using `CNAME` records

{% tabs %}
{% tab title="YAML" %}
{% code title="ec2-based-detection.yaml" %}

```yaml
id: ec2-based-detector

info:
  name: amazon ec2-based subdomain detection
  author: melbadry9
  severity: info
  tags: dns

dns:
  - name: "{{FQDN}}"
    type: CNAME
    class: inet
    recursion: true
    retries: 2

    matchers:
      - type: regex
        regex:
            - "ec2-[-\\d]+\\.compute[-\\d]*\\.amazonaws\\.com"
            - "ec2-[-\\d]+\\.[\\w\\d\\-]+\\.compute[-\\d]*\\.amazonaws\\.com"
```

{% endcode %}
{% endtab %}
{% endtabs %}

2\. Subdomain has `A` record and with reverse IP lookup we get hostname which matches previous regex. We can use `host`command to perform reverse lookup or using Python.

{% tabs %}
{% tab title="Bash" %}

```bash
host 3.5.140.229
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Python" %}
{% code title="reverse\_lookup.py" %}

```python
from dns import resolver,reversename

addr = reversename.from_address("3.5.140.229")
hostname = str(resolver.query(addr,"PTR")[0])
print(hostname)
```

{% endcode %}
{% endtab %}
{% endtabs %}

3\. Subdomain has `A` record which falls within Amazon IP range `ip_prefix` which can be found [here](https://ip-ranges.amazonaws.com/ip-ranges.json).

> Tools which I have used to automate this step [anew](https://github.com/tomnomnom/anew/), [httpie](https://github.com/httpie/httpie), [mapcidr](https://github.com/projectdiscovery/mapcidr) to generate file which contains all possible Amazon IPs and check if IP in file

{% tabs %}
{% tab title="Bash" %}

```bash
http https://ip-ranges.amazonaws.com/ip-ranges.json | jq '.prefixes | .[] | .ip_prefix' -r | mapcidr -silent -o aws_ec2_ips.txt
grep "3.5.140.229" aws_ec2_ips.txt || echo "Not EC2-Based"
```

{% endtab %}
{% endtabs %}

I found this code that checks a list of IPs against a list of CIDRs and prints out IPs which fall within the range.

{% embed url="<https://github.com/0x3c3e/recloud/blob/master/check_range.go>" %}

```bash
go run check_range.go -ip_file /path/to/ips_file -network_file /path/to/cidr_file
```

At this point, we have identified subdomains and IPs, which are *EC2-Based.* Let’s check for issues.

After completing fingerprinting phase, we found a subdomain that is EC2-Based. Now what?

{% tabs %}
{% tab title="JSON" %}
{% code title="DNS Record For EC2-Based subdomain " %}

```javascript
{
  "host": "sub.example.com",
  "resolver": [
    "8.8.8.8:53"
  ],
  "a": [
    "3.5.140.229"
  ],
  "cname": [
    "ec2-3-5-140-229.ap-southeast-1.compute.amazonaws.com"
  ],
  "status_code": "NOERROR"
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Passive or Third-Party Takeover Phase

{% hint style="info" %}
**Objective:**

Finding proof that our subdomain is currently taken over or had been taken over in the past by a third party.
{% endhint %}

### HTTP Method

{% tabs %}
{% tab title="Manuel Mode" %}

* Open `http://sub.example.com/` on your browser and check for:
  * Weird content which can't belong to `example.com`
  * Redirection for a website that doesn't belong to `example.com` (Location header)
  * Directories using brute force In case the response contains a blank HTML page.
    {% endtab %}

{% tab title="Semi-automated Mode" %}
I'll be using [httpx ](https://github.com/projectdiscovery/httpx)for this part to extract `title` and `location`from the HTTP response. This tool is very efficient when checking a huge list with EC2-Based subdomains, and I'll have to check the results manually.

```bash
echo "sub.example.com" | httpx -title -location

httpx -title -location -l ec2_based_subdomains.txt
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
*Frans Rosén* mentioned this technique during a talk "*DNS hijacking using cloud providers*" in 2017
{% endhint %}

{% embed url="<https://youtu.be/HhJv8CU-RIk?t=1090>" %}

### HTTPS Method

{% tabs %}
{% tab title="Manuel Mode" %}

* Open `https://sub.example.com/` on your browser and check for:
  * Weird content which can't belong to `example.com`
  * Redirection for a website that doesn't belong to `example.com` (location header)
  * Directories using brute force In case of the response contains a blank HTML page
* Open SSL certificate data from the browser and check for (Certificate Warning by the browser):
  * Organization name (Org) which doesn't own `example.com`
  * Common Name (CN) which doesn't match or belong to `example.com`
  * Subject Alternative Name (DNS Name) which doesn't match or belong to`example.com`
    {% endtab %}

{% tab title="Semi-automated Mode" %}
I'll be using [httpx](https://github.com/projectdiscovery/httpx) for this part to extract `dns_names, dns_names and organization name` from the SSL certificate. This tool is very efficient when checking a huge list with EC2-Based subdomains.

```bash
echo "sub.example.com" | httpx -json | jq '.tls'

httpx -json -l ec2_based_subdomains.txt | jq '.tls'
```

{% endtab %}

{% tab title="Monitoring Mode" %}
I'll be using [SSLEnum ](https://github.com/melbadry9/SSLEnum)for this part to extract `dns_names`, `dns_names` and `organization name` from the SSL certificate, compare It against the hostname and print out possible vulnerable subdomains. I'll be using [notify](https://github.com/projectdiscovery/notify) to send notifications and [anew](https://github.com/tomnomnom/anew/). This technique may produce false-positive results, so confirm SSL data before reporting.

```bash
# check the current state of subdomains in the list and save it to check for changes later
cat ec2_based_subomains.txt | sslenum -t 10 | tee -a ec2_takeover.txt

# bash script which will run forever and check for changes in ssl certificate 
while true; do
    cat ec2_based_subomains.txt | sslenum -t 10 | jq 'select(.dangling == true)' -c | anew ec2_takeover.txt | notify
done
```

{% endtab %}
{% endtabs %}

### Archived Passive Data

* We can use passive data collected by search engines like `Google`, `Bing`, `Shodan` and `Spyse` I'll be using `Shodan` in the next part.

{% tabs %}
{% tab title="Manuel Mode" %}

* Open [`https://www.shodan.io`](https://www.shodan.io) and search with the following query `net:ip1,ip2, ..`
* For our target, we will use `net:3.5.140.229`
* Check HTTP and SSL certificate data collected before to confirm that our subdomain was under third-party control.
  {% endtab %}

{% tab title="Semi-automated Mode" %}
To automate search query for multiple IPs on `Shodan`. I use the following script to fetch data and then analyze them manually.

{% code title="shodan\_ip\_query.py" %}

```python
import json
import shodan #pip3 install shodan


def fetch_ip_data(ip:str):
    KEY = "shodan_api_key"    #Add Shodan API key 
    api = shodan.Shodan(KEY)
    results = api.search('net:{0}'.format(ip))
    
    # Extract http and ssl data for IP if any exists
    if results['total'] > 0:
        all_ip_data = []
        for match in results['matches']:
            ip_data = { "ip": ip }
            ip_data['ssl'] = match.get("ssl")
            ip_data['http'] = match.get("http")
            all_ip_data.append(ip_data)
        
        #Print out results and collected data as list 
        print(json.dumps(all_ip_data, indent=4))
        return all_ip_data

fetch_ip_data("3.5.140.229")
```

{% endcode %}
{% endtab %}
{% endtabs %}

* We can use the Internet archive [WaybackMachine](https://archive.org/web/) to collect old snapshots for our subdomain and apply [previous ](/blog/readme.md#http-method)techniques.
* We can scan ports on our target `sub.example.com` and check open ports for data to confirm the owner of the current IP.
* We can contact the security team to inquire about ownership of IP, but this is not possible with every company or program.

{% hint style="warning" %}
Passive detection requires creativity, OSINT skills, and monitoring. Chances for a false positive are relatively high.

You can find your method to detect vulnerable subdomains. Personally, I use previous techniques, so feel free to suggest other techniques, and I'll add them.
{% endhint %}

## Active Takeover Phase

{% hint style="info" %}
**Objective:**

Take over subdomain IP and assign It to EC2 Instance network interface.
{% endhint %}

First, we should know how Amazon assigns new IPs to Its customers from the IP pool so that we get our desired IP address `3.5.140.229`

#### How does Amazon allow acquiring new IPs?

* Every time EC2 Instance stops and starts, Amazon will assign a new IP address to your EC2 Instance.
* Amazon allows acquiring public IP addresses using Elastic IP.

### Start-Stop Method

This method was mentioned before in this [blog ](https://enfinlay.github.io/ec2/deadend/2019/10/19/ec2-takeover-attempt.html)post, so I wrote a quick script to take over`3.5.140.229`, which falls within the region `ap-southeast-1`. We can take over this IP and serve our content on the EC2 server to create our PoC.

{% hint style="info" %}
This technique has a medium probability of success and can take an enormous amount of time
{% endhint %}

{% tabs %}
{% tab title="Python" %}
{% code title="ec2\_bruteforce.py" %}

```python
import boto3 #pip3 install boto3


global INST_IDs
INST_IDs = [""]           # created ec2 instance ID
AWSSecretKey = ""         # Amazon console secret key
AWSAccessKeyId = ""       # Amazon console access key
mon_ips = ['3.5.140.229'] # IP address to takeover


# connect to ec2 service with provided keys
ecc2 = boto3.client(
    'ec2',
    aws_access_key_id=AWSAccessKeyId,
    aws_secret_access_key=AWSSecretKey,
    region_name='ap-southeast-1'
)


# extract PublicIp with instance ID
def get_ip(ec2):
    ips = []
    response = ec2.describe_instances(InstanceIds=INST_IDs)
    for inst in response['Reservations']:
        for i in inst['Instances']:
            for ii in i['NetworkInterfaces']:
                ips.append(ii['Association']['PublicIp'])
    return ips


# stop ec2 with instance ID
def stop_ec2(ec2):
    response = ec2.stop_instances(InstanceIds=INST_IDs, Hibernate=False, Force=True)
    print(response)


# start ec2 with instance ID
def start_ec2(ec2):
    response = ec2.start_instances(InstanceIds=INST_IDs)
    print(response)


if __name__ == "__main__":
    found = False
    
    # start and stop ec2 instance until we acquire IP 
    while not found:
        start_ec2(ecc2)
        if get_ip(ecc2)[0] == mon_ips[0]:
            found = True
            print("IP {0} Acquired".format(mon_ips[0]))
        else:
            stop_ec2(ecc2)
```

{% endcode %}
{% endtab %}
{% endtabs %}

I found this [repository ](https://github.com/In3tinct/Taken)very helpful with automating this method using a bash script and [awscli ](https://aws.amazon.com/cli/)command line.

### Elastic-IP Method

This technique is more practical and faster than the stop-start method. This method has been reported before on HackerOne [report](https://hackerone.com/reports/707748/). The following script is used to automate this process. Amazon allows up to **5** Elastic IPs for each account per region, so this script can be optimized using multi-threading.

{% tabs %}
{% tab title="Python" %}
{% code title="aws\_ip\_bruter.py" %}

```python
import time
import boto3

found = False
AWSSecretKey = ""         # Amazon console secret key
AWSAccessKeyId = ""       # Amazon console access key
mon_ips = ['3.5.140.229'] # IP address to takeover

# connect to ec2 service with provided keys
ecc2 = boto3.client(
    'ec2',
    aws_access_key_id=AWSAccessKeyId,
    aws_secret_access_key=AWSSecretKey,
    region_name='ap-southeast-1'
)

# acquiring Elastic IP and release it until we acquire specific IP 
while not found:
    allocation = ecc2.allocate_address(Domain='vpc')
    address = allocation["PublicIp"]
    allocation_id = allocation["AllocationId"]
    if address in mon_ips:
        found = True
        print("Acquired IP {0}".format(address))
    else:
        ecc2.release_address(AllocationId=allocation_id)
        
        # make sure to get new addresses
        time.sleep(60)
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="danger" %}
If you have a limited budget on an Amazon account, you should probably keep an eye on the billing section for extra charges.
{% endhint %}

I found some GitHub repositories which automate active EC2 takeover:

* <https://github.com/In3tinct/Taken>
* <https://github.com/timkoopmans/eipfish>
* <https://github.com/monoxgas/FlyingAFalseFlag>

## Proof-of-Concept Phase

In this part, I'll explain how to create PoC for EC2-Based subdomain takeover.

### Passive Takeover PoC

In this type of takeover, we don't create a traditional PoC. The only kind of PoC we attach when writing a report is the proof, which we found during [Passive Phase](/blog/readme.md#passive-takeover-phase). If we have not found proof, we monitor the subdomain for changes.

### Active Takeover PoC

After successfully acquiring the IP address, we attach that IP to EC2 Instance, if it wasn't already. Then we SSH into Instance and create our `takeover.html` PoC on the web server path `/var/www/html/` Now when we visit `http://sub.example.com/takeover.html` we should see our PoC live. If you can not access your HTTP server, ensure network access is allowed, as mentioned [here](https://aws.amazon.com/premiumsupport/knowledge-center/connect-http-https-ec2/).

## Report Phase

At this point, you're ready to find a vulnerable EC2-Based subdomain takeover and submit a report.&#x20;

{% hint style="warning" %}
If you decide to depend on Passive Takeover, You should avoid managed programs, as they still tend to ask for traditional PoC files.
{% endhint %}

### Disclosed Reports

{% embed url="<https://hackerone.com/reports/1294492>" %}
Passive Takeover
{% endembed %}

{% embed url="<https://hackerone.com/reports/1390093>" %}
Active Takeover
{% endembed %}

{% content-ref url="/pages/-MUq9GH4M78EcjjG9LSI" %}
[About Me](/blog/readme.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://melbadry9.gitbook.io/blog/dangling-dns/aws/ddns-ec2-current-state.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
