├── .gitignore ├── LICENSE ├── README.md └── UpdateCloudIPs.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Chris Maddalena 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UsefulScripts 2 | A collection of useful scripts 3 | -------------------------------------------------------------------------------- /UpdateCloudIPs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """This script performs the necessary actions for collecting the latest IP addresses used by Amazon 5 | Web Services, Google Compute, and Microsoft Azure. At the end, all IP addresses are output to 6 | a CloudIPs.txt file. Each range is printed on a new line following a header naming the provider. 7 | As discussed at https://posts.specterops.io/head-in-the-clouds-bd038bb69e48?gi=c33a4e051d6b 8 | """ 9 | 10 | import requests 11 | import json 12 | import dns.resolver 13 | from bs4 import BeautifulSoup 14 | import xml.etree.ElementTree as ET 15 | 16 | # Setup the DNS resolver without a short timeout 17 | resolver = dns.resolver.Resolver() 18 | resolver.timeout = 1 19 | resolver.lifetime = 1 20 | 21 | # The addresses listed in the providers' documentation for the latest addresses 22 | aws_uri = "https://ip-ranges.amazonaws.com/ip-ranges.json" 23 | azure_uri = "https://www.microsoft.com/en-us/download/confirmation.aspx?id=41653" 24 | compute_uri = "_cloud-netblocks.googleusercontent.com" 25 | 26 | # Lists for holding the IP address ranges as they are collected 27 | aws_addresses = [] 28 | azure_addresses = [] 29 | compute_addresses = [] 30 | 31 | def get_dns_record(domain, record_type): 32 | """Simple function to get the specified DNS record for the target domain.""" 33 | answer = resolver.query(domain, record_type) 34 | return answer 35 | 36 | 37 | # Fetch the JSON for the latest AWS IP ranges 38 | try: 39 | aws_json = requests.get(aws_uri).json() 40 | except: 41 | print("[!] Failed to get the AWS IP addresses!") 42 | 43 | if aws_json: 44 | print("[+] Collected AWS IP ranges last updated on %s" % aws_json['createDate']) 45 | for address in aws_json['prefixes']: 46 | aws_addresses.append(address['ip_prefix']) 47 | 48 | # Find the current address for the latest Azure XML document from Microsoft Download Center 49 | try: 50 | azure_req = requests.get(azure_uri) 51 | except: 52 | print("[!] Failed to get the Azure XML file from Microsoft Download Center!") 53 | 54 | soup = BeautifulSoup(azure_req.text, features="html.parser") 55 | for link in soup.find_all('a', href=True): 56 | if "PublicIPs" in link['href']: 57 | azure_uri = link['href'] 58 | 59 | # Fetch the XML for the latest Azure IP ranges 60 | print("[+] Found Microsoft link for the XML document: %s" % azure_uri) 61 | try: 62 | azure_xml = requests.get(azure_uri).content 63 | # Parse the Azure XML for the IP ranges 64 | tree = ET.fromstring(azure_xml) 65 | # root = tree.getroot() 66 | for child in tree: 67 | for address in child: 68 | azure_addresses.append(address.attrib['Subnet']) 69 | except: 70 | print("[!] Failed to get the Azure XML file from Microsoft Download Center!") 71 | 72 | # Begin the TXT record collection for Google Compute 73 | # First, the hostnames must be collected from the primary _cloud-netblocks subdomain 74 | try: 75 | txt_records = get_dns_record(compute_uri, "TXT") 76 | for rdata in txt_records.response.answer: 77 | for item in rdata.items: 78 | netblock_names = item.to_text().strip('"').strip("v=spf1 include:").strip(" ?all") 79 | except: 80 | netblock_names = None 81 | 82 | # Now the TXT records of each of the netblocks subdomains must be collected 83 | if netblock_names: 84 | netblocks = netblock_names.split(" ") 85 | for hostname in netblocks: 86 | print("[+] Collecting TXT records for %s" % hostname.strip("include:")) 87 | txt_records = get_dns_record(hostname.strip("include:"), "TXT") 88 | 89 | txt_entries = [] 90 | for rdata in txt_records.response.answer: 91 | for item in rdata.items: 92 | txt_entries = item.to_text().strip('"').strip("v=spf1 ").split(" ") 93 | 94 | for entry in txt_entries: 95 | if "include" in entry: 96 | netblocks.append(entry) 97 | 98 | if "ip" in entry: 99 | address = entry.strip("ip4:").strip("ip6:") 100 | compute_addresses.append(address) 101 | 102 | # Output an up-to-date list of all cloud IP address ranges for all three providers 103 | with open("CloudIPs.txt", "w") as output_file: 104 | output_file.write("# Amazon Web Services IPs\n\n") 105 | for address in aws_addresses: 106 | output_file.write(address + "\n") 107 | 108 | output_file.write("\n") 109 | 110 | output_file.write("# Microsft Azure IPs\n\n") 111 | for address in azure_addresses: 112 | output_file.write(address + "\n") 113 | 114 | output_file.write("\n") 115 | 116 | output_file.write("# Google Compute IPs\n\n") 117 | for address in compute_addresses: 118 | output_file.write(address + "\n") 119 | 120 | --------------------------------------------------------------------------------