├── requirements.txt ├── README.md ├── check_range.go ├── gcp.py └── aws.py /requirements.txt: -------------------------------------------------------------------------------- 1 | apache-libcloud 2 | boto3 3 | requests 4 | dnspython 5 | netaddr -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Dependencies: 2 | `pip install -r requirements.txt` 3 | 4 | ## Run 5 | ### GCP 6 | `python3.7 gcp.py "[project]" --region="[region]"` 7 | ### AWS 8 | `python3.7 aws.py [region] -aK [access key] -sK [secret key] -p [number of processes]` 9 | 10 | ref: https://github.com/monoxgas/FlyingAFalseFlag -------------------------------------------------------------------------------- /check_range.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net" 9 | "os" 10 | 11 | "github.com/yl2chen/cidranger" 12 | ) 13 | 14 | func main() { 15 | ipListFileName := flag.String("ip_file", "", "List of ip addresses") 16 | networkListFileName := flag.String("network_file", "", "List of networks") 17 | 18 | flag.Parse() 19 | file, err := os.Open(*ipListFileName) 20 | 21 | if err != nil { 22 | log.Fatalf("failed opening file: %s", err) 23 | } 24 | 25 | scanner := bufio.NewScanner(file) 26 | scanner.Split(bufio.ScanLines) 27 | var ip_list []string 28 | 29 | for scanner.Scan() { 30 | ip_list = append(ip_list, scanner.Text()) 31 | } 32 | 33 | file.Close() 34 | 35 | file, err = os.Open(*networkListFileName) 36 | 37 | if err != nil { 38 | log.Fatalf("failed opening file: %s", err) 39 | } 40 | 41 | scanner = bufio.NewScanner(file) 42 | scanner.Split(bufio.ScanLines) 43 | var networks []string 44 | 45 | for scanner.Scan() { 46 | networks = append(networks, scanner.Text()) 47 | } 48 | 49 | file.Close() 50 | 51 | ranger := cidranger.NewPCTrieRanger() 52 | for _, cidr := range networks { 53 | _, network, _ := net.ParseCIDR(cidr) 54 | ranger.Insert(cidranger.NewBasicRangerEntry(*network)) 55 | } 56 | 57 | for _, ip := range ip_list { 58 | contains, _ := ranger.Contains(net.ParseIP(ip)) 59 | if contains { 60 | fmt.Println(ip) 61 | } 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /gcp.py: -------------------------------------------------------------------------------- 1 | import time 2 | import re 3 | import sys 4 | import argparse 5 | import requests 6 | import random 7 | import string 8 | from libcloud.compute.types import Provider 9 | from libcloud.compute.providers import get_driver 10 | from pprint import pprint 11 | from multiprocessing import Process 12 | 13 | Description = """ 14 | _____ _ _ _____ 15 | | | |___ _ _ _| | __ |___ ___ ___ ___ ___ 16 | | --| | . | | | . | -| .'| _| . | . | | 17 | |_____|_|___|___|___|__|__|__,|___|___|___|_|_| 18 | Cloud IP Hunting - Proof of Concept [GCP] 19 | """ 20 | SearchRegions = ["us-east1", "us-central1", "us-west1", "us-west2", "us-east4"] 21 | Session = None 22 | CSRFToken = None 23 | 24 | 25 | def get_hostnames(address): 26 | global Session 27 | global CSRFToken 28 | if not Session: 29 | # print('[+] Opening session for Security Trails ...') 30 | Session = requests.Session() 31 | response = Session.get("https://securitytrails.com/list/ip/1.1.1.1") 32 | CSRFToken = re.findall(r'csrf_token = "(\S+?)"', response.text)[0] 33 | response = Session.post( 34 | f"https://securitytrails.com/app/api/v1/list?ipv4={address}", 35 | json={"_csrf_token": CSRFToken}, 36 | ) 37 | if response.status_code != 200: 38 | print("[!] SecurityTrails request failed!") 39 | print(response.text) 40 | sys.exit(1) 41 | records = response.json().pop("records", []) 42 | if records: 43 | return [r["hostname"] for r in records] 44 | return [] 45 | 46 | 47 | def start_loop(project_name, number_of_loops, region_name): 48 | driver = get_driver(Provider.GCE) 49 | engine = driver("", "", project=project_name) 50 | 51 | print("[+] Connected to GCP.\n") 52 | previously_seen = [] 53 | for l in range(number_of_loops): 54 | print("[+] (L{}) Allocating {} new address".format(l, 1)) 55 | 56 | name = "".join(random.choice(string.ascii_lowercase) for _ in range(0, 8)) 57 | 58 | ip = engine.ex_create_address(name, region_name) 59 | 60 | if ip.address in previously_seen: 61 | print("\t- {} is duplicate".format(ip.address)) 62 | ip.destroy() 63 | continue 64 | 65 | previously_seen.append(ip.address) 66 | records = get_hostnames(ip.address) 67 | 68 | if not records or all( 69 | True if "googleusercontent.com" in record else False for record in records 70 | ): 71 | print("\t- {}".format(ip.address)) 72 | ip.destroy() 73 | continue 74 | 75 | print("\t++ {}\n".format(ip.address)) 76 | pprint(records) 77 | break 78 | print("\n[+] Done.") 79 | 80 | 81 | def main(arguments): 82 | parser = argparse.ArgumentParser( 83 | description=Description, formatter_class=argparse.RawDescriptionHelpFormatter 84 | ) 85 | parser.add_argument("project", help="GCP project to hold assets under") 86 | parser.add_argument("-l", "--loops", help="Number of loops", default=300) 87 | parser.add_argument( 88 | "-r", 89 | "--region", 90 | help="Region to search", 91 | default="us-east1,us-central1,us-west1,us-west2,us-east4", 92 | ) 93 | args = parser.parse_args(arguments) 94 | 95 | print(Description) 96 | 97 | procs = [] 98 | for _ in range(8): 99 | proc = Process(target=start_loop, args=(args.project, args.loops, args.region)) 100 | procs.append(proc) 101 | proc.start() 102 | 103 | for proc in procs: 104 | proc.join() 105 | 106 | 107 | if __name__ == "__main__": 108 | sys.exit(main(sys.argv[1:])) 109 | -------------------------------------------------------------------------------- /aws.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import sys 3 | import time 4 | from timeit import default_timer as timer 5 | from multiprocessing import Process 6 | 7 | import boto3 8 | import requests 9 | from botocore.config import Config 10 | import tempfile 11 | import subprocess 12 | 13 | config = Config(retries=dict(max_attempts=10)) 14 | 15 | # TODO: remove global variables 16 | # TODO: take ip list filename from args 17 | # TODO: change exception handling 18 | 19 | with open("ip_list") as f: 20 | ip_list = [line.strip() for line in f.readlines()] 21 | 22 | DESCRIPTION = r""" 23 | __ __ 24 | ________ _____/ /___ __ ______/ / 25 | / ___/ _ \/ ___/ / __ \/ / / / __ / 26 | / / / __/ /__/ / /_/ / /_/ / /_/ / 27 | /_/ \___/\___/_/\____/\__,_/\__,_/ 28 | """ 29 | 30 | 31 | AWSRegions = [ 32 | "us-east-2", 33 | "us-east-1", 34 | "us-west-1", 35 | "us-west-2", 36 | "ca-central-1", 37 | "eu-central-1", 38 | "eu-west-1", 39 | "eu-west-2", 40 | "eu-west-3", 41 | "eu-north-1", 42 | ] 43 | 44 | 45 | def check_ip_by_region(region): 46 | url = "https://ip-ranges.amazonaws.com/ip-ranges.json" 47 | data = requests.get(url).json() 48 | cidr_of_region = [k["ip_prefix"] for k in data["prefixes"] if k["region"] == region] 49 | 50 | networks = tempfile.NamedTemporaryFile("w") 51 | networks.writelines([line + "\n" for line in cidr_of_region]) 52 | networks.seek(0) 53 | 54 | r = subprocess.check_output( 55 | [ 56 | "go", 57 | "run", 58 | "check_range.go", 59 | "-network_file", 60 | networks.name, 61 | "-ip_file", 62 | "ip_list", 63 | ] 64 | ) 65 | return r.decode("utf-8").splitlines() 66 | 67 | 68 | def main(args): 69 | 70 | TATAL_CYCLES = 0 71 | CYCLES = 0 72 | TOTAL_TIME = 0 73 | UNIQUE_ADDRESSES = [] 74 | 75 | engine = boto3.client( 76 | "ec2", 77 | aws_access_key_id=args.access_key, 78 | aws_secret_access_key=args.secret_key, 79 | region_name=args.region, 80 | config=config, 81 | ) 82 | print( 83 | "\n[+] Connected to AWS. Hunting in {} ... (max: {})\n".format( 84 | args.region, args.count 85 | ) 86 | ) 87 | 88 | TATAL_CYCLES += args.count 89 | for _ in range(args.count): 90 | start = timer() 91 | 92 | for _ in range(3): 93 | try: 94 | eip = engine.allocate_address(Domain="vpc") 95 | break 96 | except Exception: 97 | time.sleep(1) 98 | print("Allocate exception", end="\r") 99 | 100 | address = eip["PublicIp"] 101 | allocation_id = eip["AllocationId"] 102 | 103 | if address not in UNIQUE_ADDRESSES: 104 | UNIQUE_ADDRESSES.append(address) 105 | if address in ip_list: 106 | print("\t Hooray, the ip is in the list: {}".format(address)) 107 | break 108 | 109 | for _ in range(3): 110 | try: 111 | engine.release_address(AllocationId=allocation_id) 112 | break 113 | except Exception: 114 | time.sleep(1) 115 | print("Release exception", end="\r") 116 | 117 | end = timer() 118 | 119 | TOTAL_TIME += end - start 120 | CYCLES += 1 121 | 122 | print(f"\t- Time: {TOTAL_TIME / CYCLES}, Ratio: {len(UNIQUE_ADDRESSES) / CYCLES}, Progress: {CYCLES}/{TATAL_CYCLES}", end="\r") 123 | 124 | print("\n") 125 | 126 | 127 | if __name__ == "__main__": 128 | print(DESCRIPTION) 129 | 130 | parser = argparse.ArgumentParser( 131 | description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter 132 | ) 133 | parser.add_argument("region", choices=AWSRegions, help="AWS Region to search") 134 | parser.add_argument( 135 | "-c", "--count", type=int, help="Number of IPs to try", default=10000 136 | ) 137 | parser.add_argument( 138 | "-p", "--processes", type=int, help="Amount of processes", default=5 139 | ) 140 | parser.add_argument("-aK", "--access-key", help="AWS access key") 141 | parser.add_argument("-sK", "--secret-key", help="AWS secret key") 142 | 143 | args = parser.parse_args(sys.argv[1:]) 144 | ip_list = check_ip_by_region(args.region) 145 | 146 | print(f"Length of ip list is: {len(ip_list)}") 147 | 148 | procs = [] 149 | for _ in range(args.processes): 150 | proc = Process(target=main, args=(args,)) 151 | procs.append(proc) 152 | proc.start() 153 | time.sleep(1) 154 | 155 | for proc in procs: 156 | proc.join() 157 | --------------------------------------------------------------------------------