├── BugHosts ├── All_Hosts.txt ├── All_IP.txt ├── cloud_hosts.txt └── other_hosts.txt ├── payloads.txt ├── README.md ├── mode_find.py ├── mode_proxy.py ├── mode_ssl.py ├── mode_direct.py └── mode_payload.py /BugHosts/All_Hosts.txt: -------------------------------------------------------------------------------- 1 | example.com -------------------------------------------------------------------------------- /BugHosts/All_IP.txt: -------------------------------------------------------------------------------- 1 | 104.16.120.28 -------------------------------------------------------------------------------- /BugHosts/cloud_hosts.txt: -------------------------------------------------------------------------------- 1 | example.com 2 | -------------------------------------------------------------------------------- /BugHosts/other_hosts.txt: -------------------------------------------------------------------------------- 1 | hostname 2 | -------- 3 | 82.129.223.98 4 | mobile.vodafone.com.eg -------------------------------------------------------------------------------- /payloads.txt: -------------------------------------------------------------------------------- 1 | HOST_HEADER:Host: free.facebook.com 2 | HOST_HEADER:Host: zero.facebook.com 3 | HOST_HEADER:Host: connectivity-test.gstatic.com 4 | HOST_HEADER:Host: www.google.com 5 | HOST_HEADER:Host: www.facebook.com 6 | HOST_HEADER:Host: m.facebook.com 7 | HOST_HEADER:Host: 0.facebook.com 8 | HOST_HEADER:Host: whatsapp.com 9 | HOST_HEADER:Host: telegram.org 10 | HOST_HEADER:Host: twitter.com 11 | HOST_HEADER:Host: instagram.com 12 | HOST_HEADER:Host: bing.com 13 | HOST_HEADER:Host: example.com@www.google.com 14 | HOST_HEADER:Host: www.telecom.com.example.com 15 | HOST_HEADER:Host: youtube.com 16 | HOST_HEADER:X-Online-Host: free.facebook.com 17 | HOST_HEADER:X-Online-Host: zero.facebook.com 18 | HOST_HEADER:X-Online-Host: www.google.com 19 | HOST_HEADER:X-Real-Host: www.facebook.com 20 | HOST_HEADER:X-Forwarded-Host: free.facebook.com 21 | HOST_HEADER:X-Forwarded-Host: www.youtube.com 22 | HOST_HEADER:X-Forwarded-For: 127.0.0.1 23 | HOST_HEADER:X-Forwarded-Proto: https 24 | HOST_HEADER:Proxy-Host: free.facebook.com 25 | HOST_HEADER:Via: 1.1 free.facebook.com 26 | HOST_HEADER:Referer: http://free.facebook.com/ 27 | HOST_HEADER:Host: TARGET_URL 28 | 29 | URL_PREFIX:http://www.google.com@TARGET_URL/ 30 | URL_PREFIX:http://facebook.com@TARGET_URL/ 31 | URL_PREFIX:http://m.facebook.com@TARGET_URL/ 32 | URL_PREFIX:http://0.facebook.com@TARGET_URL/ 33 | URL_PREFIX:http://whatsapp.com@TARGET_URL/ 34 | URL_PREFIX:http://telegram.org@TARGET_URL/ 35 | URL_PREFIX:http://twitter.com@TARGET_URL/ 36 | URL_PREFIX:http://instagram.com@TARGET_URL/ 37 | URL_PREFIX:http://bing.com@TARGET_URL/ 38 | URL_PREFIX:http://youtube.com@TARGET_URL/ 39 | URL_PREFIX:http://127.0.0.1@TARGET_URL/ 40 | URL_PREFIX:http://localhost@TARGET_URL/ 41 | URL_PREFIX:http://TARGET_URL. 42 | URL_PREFIX:http://www.facebook.com%20TARGET_URL 43 | URL_PREFIX:http://facebook.com%20TARGET_URL 44 | 45 | URL_SUFFIX:/? 46 | URL_SUFFIX:/%20 47 | URL_SUFFIX:/%00 48 | URL_SUFFIX:/# 49 | URL_SUFFIX:/. 50 | URL_SUFFIX:// 51 | URL_SUFFIX:/google.com 52 | URL_SUFFIX:/facebook.com 53 | URL_SUFFIX:/login 54 | URL_SUFFIX:/status 55 | URL_SUFFIX:/api/ 56 | URL_SUFFIX:/vpn/ 57 | URL_SUFFIX:?url=http://www.google.com 58 | URL_SUFFIX:?redir=http://www.google.com 59 | 60 | HTTP_REQUEST_LINE_INJECTION:CONNECT TARGET_URL:80 HTTP/1.1[crlf]Host: free.facebook.com[crlf][crlf] 61 | HTTP_REQUEST_LINE_INJECTION:CONNECT TARGET_URL:443 HTTP/1.1[crlf]Host: free.facebook.com[crlf][crlf] 62 | HTTP_REQUEST_LINE_INJECTION:GET http://free.facebook.com/ TARGET_URL HTTP/1.1[crlf]Host: free.facebook.com[crlf][crlf] 63 | HTTP_REQUEST_LINE_INJECTION:GET / HTTP/1.1[crlf]Host: free.facebook.com[crlf]X-Forwarded-Host: www.google.com[crlf]Connection: close[crlf][crlf] 64 | HTTP_REQUEST_LINE_INJECTION:GET / HTTP/1.1[crlf]Host: free.facebook.com[crlf]Content-Length: 0[crlf][crlf] 65 | HTTP_REQUEST_LINE_INJECTION:GET / HTTP/1.1[crlf]Host: free.facebook.com[crlf]Content-Length: 4[crlf][crlf]ABCD 66 | HTTP_REQUEST_LINE_INJECTION:GET / HTTP/1.1[crlf]Host: free.facebook.com[crlf]Transfer-Encoding: chunked[crlf][crlf]0[crlf][crlf] 67 | HTTP_REQUEST_LINE_INJECTION:GET / TARGET_URL HTTP/1.1[crlf]Host: free.facebook.com[crlf]X-Online-Host: www.google.com[crlf][crlf] 68 | 69 | # Note: HTTP_METHOD was integrated into HTTP_REQUEST_LINE_INJECTION for raw socket handling. 70 | # The original HTTP_METHOD:CONNECT payloads are now part of HTTP_REQUEST_LINE_INJECTION 71 | # as they require direct socket manipulation for the CONNECT method. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BugHosts Finder 2 | 3 | ## Overview 4 | 5 | **BugHosts Finder** is a collection of Python scripts designed to help you find "bughosts" within Internet Service Providers. These bughosts can potentially be used to access free internet via SSH VPN apps like **HTTP Custom**, **HTTP Injector**, **HA Tunnel Plus**, and **TLS Tunnel**. 6 | 7 | --- 8 | 9 | ## Quick Start 10 | 11 | ```bash 12 | git clone https://github.com/AhmadAllam/FreeNet.git 13 | cd FreeNet 14 | chmod +x * 15 | ``` 16 | 17 | --- 18 | 19 | ## Requirements 20 | 21 | ```bash 22 | apt update && apt upgrade -y 23 | apt install python3 python3-pip 24 | pip install asyncio-throttle aiohttp aiofiles 25 | ``` 26 | 27 | --- 28 | 29 | ## Scripts & Usage 30 | 31 | ### 1. `mode_find.py`: Find Hosts in IP Ranges 32 | 33 | - **Help and options:** 34 | ```bash 35 | python mode_find.py -h 36 | ``` 37 | - **Scan a /24 subnet (254 possible IP addresses):** 38 | ```bash 39 | python mode_find.py -s vodafone.com -m 1 40 | ``` 41 | - **Scan a /16 subnet (65,536 possible IP addresses):** 42 | ```bash 43 | python mode_find.py -s vodafone.com -m 2 44 | ``` 45 | 46 | **Output:** 47 | - Discovered hostnames: `BugHosts/All_Hosts.txt` 48 | - Corresponding IP addresses: `BugHosts/All_IP.txt` 49 | 50 | --- 51 | 52 | ### 2. `mode_direct.py`: Scan Hosts for Open Ports and Services 53 | 54 | - **Help and options:** 55 | ```bash 56 | python mode_direct.py -h 57 | ``` 58 | - **Scan with default settings (uses `BugHosts/All_Hosts.txt` and ports 80, 443):** 59 | ```bash 60 | python mode_direct.py 61 | ``` 62 | - **Scan a specific file and port:** 63 | ```bash 64 | python mode_direct.py -f my_hosts.txt -p 80 65 | ``` 66 | - **Scan multiple ports concurrently:** 67 | ```bash 68 | python mode_direct.py -p 80,443,8080 69 | ``` 70 | - **Scan using a proxy:** 71 | ```bash 72 | python mode_direct.py -P proxy.example.com:8080 -p 80 73 | ``` 74 | 75 | **Output:** 76 | - "Cloud Hosts" (based on server headers/heuristics): `BugHosts/cloud_hosts.txt` 77 | - "Other Hosts": `BugHosts/other_hosts.txt` 78 | 79 | --- 80 | 81 | ### 3. `mode_payload.py`: Test Hosts with Custom HTTP Payloads 82 | 83 | - **Help and options:** 84 | ```bash 85 | python mode_payload.py -h 86 | ``` 87 | - **Run with default settings (uses `BugHosts/All_Hosts.txt` as input, `payloads.txt` for payloads, and `http://www.google.com/generate_204` as the target URL):** 88 | ```bash 89 | python mode_payload.py 90 | ``` 91 | - **Specify a custom input file and payloads file:** 92 | ```bash 93 | python mode_payload.py -f my_custom_hosts.txt --payloads-file my_custom_payloads.txt 94 | ``` 95 | 96 | **Output:** 97 | - Discovered bug hosts: `BugHosts/payload_bugs.txt` 98 | 99 | --- 100 | 101 | ### 4. `mode_proxy.py`: Hunt for Open Proxies 102 | 103 | - **Help and options:** 104 | ```bash 105 | python mode_proxy.py -h 106 | ``` 107 | - **Run with default settings (uses `BugHosts/All_Hosts.txt` and a list of common proxy ports like 80, 8080, 3128, etc.):** 108 | ```bash 109 | python mode_proxy.py 110 | ``` 111 | - **Specify custom ports to scan for proxies (e.g., only ports 80 and 8080):** 112 | ```bash 113 | python mode_proxy.py -p 80,8080 114 | ``` 115 | - **Use a specific input file for hostnames/IPs:** 116 | ```bash 117 | python mode_proxy.py -f my_ip_list.txt 118 | ``` 119 | 120 | **Output:** 121 | - Discovered open proxies: `BugHosts/open_proxies.txt` 122 | 123 | --- 124 | 125 | ## Important Notes 126 | 127 | - This tool is provided for educational and research purposes only. Use it responsibly and ethically. 128 | - Built with ❤️ for: 129 | ``` 130 | #unlimited_internet_in_egypt 131 | ``` 132 | 133 | --- 134 | 135 | ## Author 136 | 137 | - **Dev. AhmadAllam** 138 | - [Telegram](https://t.me/echo_Allam) 139 | - Don't forget Palestine ❤️ 140 | -------------------------------------------------------------------------------- /mode_find.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import asyncio 3 | import signal 4 | import sys 5 | import argparse 6 | import os 7 | from concurrent.futures import ThreadPoolExecutor 8 | import time 9 | 10 | # --- CONFIGURABLE_SETTINGS --- 11 | # Colors for output display 12 | CN = "\033[K" 13 | Y1 = "\033[33m" 14 | RED = "\033[31m" 15 | GREEN = "\033[32m" 16 | WHITE = "\033[97m" 17 | CC = "\033[0m" 18 | 19 | # DNS servers used by the script 20 | DNS_SERVER_1 = "62.240.110.198" 21 | DNS_SERVER_2 = "62.240.110.197" 22 | 23 | # Default site if none is specified 24 | DEFAULT_SITE = "vodafone.com.eg" 25 | 26 | # Subnet masks 27 | MASK_CHOICE_1 = (255, 255, 255, 0) # /24 28 | MASK_CHOICE_2 = (255, 255, 0, 0) # /16 29 | 30 | # Maximum concurrent requests 31 | MAX_CONCURRENT_REQUESTS = 50 32 | 33 | # Socket timeout in seconds 34 | SOCKET_TIMEOUT_SECONDS = 3 35 | 36 | # Directory for output files 37 | OUTPUT_DIRECTORY = "BugHosts" 38 | 39 | # Output file names 40 | OUTPUT_HOSTS_FILE = "All_Hosts.txt" 41 | OUTPUT_IPS_FILE = "All_IP.txt" 42 | # --- END_CONFIGURABLE_SETTINGS --- 43 | 44 | 45 | def signal_handler(sig, frame): 46 | sys.stdout.write(f'\n{RED}Goodbye!{CC}\n') 47 | sys.stdout.flush() 48 | sys.exit(0) 49 | 50 | signal.signal(signal.SIGINT, signal_handler) 51 | 52 | def set_dns(): 53 | try: 54 | with open('/etc/resolv.conf', 'w') as file: 55 | file.write(f"nameserver {DNS_SERVER_1}\n") 56 | file.write(f"nameserver {DNS_SERVER_2}\n") 57 | except PermissionError: 58 | sys.stdout.write(f"{RED}Warning: No root privileges, continuing without changing DNS...{CC}\n") 59 | sys.stdout.flush() 60 | except Exception as e: 61 | sys.stdout.write(f"{RED}Warning: Failed to set DNS ({e}), continuing anyway...{CC}\n") 62 | sys.stdout.flush() 63 | 64 | async def get_hostname(addr): 65 | try: 66 | hostname = await asyncio.to_thread(socket.gethostbyaddr, addr) 67 | return addr, hostname[0] 68 | except (socket.herror, socket.gaierror, socket.timeout): 69 | return addr, None 70 | except Exception as e: 71 | sys.stdout.write(f"{RED}An unexpected error occurred: {e}{CC}\n") 72 | sys.stdout.flush() 73 | return addr, None 74 | 75 | def get_ip_address_blocking(site): 76 | try: 77 | ip = socket.gethostbyname(site) 78 | return site, ip 79 | except socket.gaierror: 80 | return site, None 81 | except Exception as e: 82 | sys.stdout.write(f"{RED}An unexpected error occurred while getting IP address: {e}{CC}\n") 83 | sys.stdout.flush() 84 | return site, None 85 | 86 | async def get_ip_address(site): 87 | site, ip = await asyncio.to_thread(get_ip_address_blocking, site) 88 | return site, ip 89 | 90 | def print_stats(site, mask, discovered_hostnames_count, start_time): 91 | elapsed_time = time.time() - start_time 92 | mask_str = '.'.join(map(str, mask)) 93 | 94 | label_width = 19 95 | 96 | sys.stdout.write(f"{Y1}{'=' * 50}{CC}\n") 97 | sys.stdout.write(f"{Y1}{' ':>2}{'Scan Results':^46}{' ':>2}{CC}\n") 98 | sys.stdout.write(f"{Y1}{'=' * 50}{CC}\n") 99 | sys.stdout.write(f"{Y1}{' Target Host :':<{label_width}} {site}{CC}\n") 100 | sys.stdout.write(f"{Y1}{' Subnet Mask :':<{label_width}} {mask_str}{CC}\n") 101 | sys.stdout.write(f"{Y1}{' New Host Count :':<{label_width}} {str(discovered_hostnames_count)}{CC}\n") 102 | sys.stdout.write(f"{Y1}{' Elapsed Time :':<{label_width}} {f'{elapsed_time:.2f} seconds'}{CC}\n") 103 | sys.stdout.write(f"{Y1}{'=' * 50}{CC}\n") 104 | sys.stdout.flush() 105 | 106 | async def find_hostnames_in_subnet(ip, mask, max_requests_param): 107 | start_time = time.time() 108 | if ip is None: 109 | sys.stdout.write(f"{RED}Error: IP address is None. Cannot perform subnet scan.{CC}\n") 110 | sys.stdout.flush() 111 | return 112 | 113 | try: 114 | ip_parts = list(map(int, ip.split('.'))) 115 | if len(ip_parts) != 4 or not all(0 <= part <= 255 for part in ip_parts): 116 | raise ValueError("Invalid IP address format or range.") 117 | except ValueError as e: 118 | sys.stdout.write(f"{RED}Error: Invalid IP address '{ip}' - {e}. Cannot perform subnet scan.{CC}\n") 119 | sys.stdout.flush() 120 | return 121 | 122 | sys.stdout.write(f"{Y1}{'-' * 55}{CC}\n") 123 | sys.stdout.write(f"{Y1}{'Host IP':<14} {Y1}{'Hostname':<25}{CC}\n") 124 | sys.stdout.write(f"{Y1}{'-' * 55}{CC}\n") 125 | sys.stdout.flush() 126 | 127 | if not os.path.exists(OUTPUT_DIRECTORY): 128 | os.makedirs(OUTPUT_DIRECTORY) 129 | output_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_HOSTS_FILE) 130 | ip_output_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_IPS_FILE) 131 | 132 | new_discovered_hostnames = set() 133 | all_known_hostnames = set() 134 | all_known_ips = set() 135 | 136 | if os.path.exists(output_path): 137 | with open(output_path, "r") as f_read: 138 | for line in f_read: 139 | all_known_hostnames.add(line.strip()) 140 | 141 | if os.path.exists(ip_output_path): 142 | with open(ip_output_path, "r") as f_read_ip: 143 | for line in f_read_ip: 144 | all_known_ips.add(line.strip()) 145 | 146 | try: 147 | target_addrs = [] 148 | if mask == MASK_CHOICE_1: 149 | subnet_prefix = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}." 150 | for i in range(256): 151 | target_addrs.append(f"{subnet_prefix}{i}") 152 | 153 | elif mask == MASK_CHOICE_2: 154 | base_prefix = f"{ip_parts[0]}.{ip_parts[1]}." 155 | for third_octet in range(256): 156 | for fourth_octet in range(256): 157 | target_addrs.append(f"{base_prefix}{third_octet}.{fourth_octet}") 158 | 159 | semaphore = asyncio.Semaphore(max_requests_param) 160 | 161 | async def get_hostname_with_semaphore(addr): 162 | async with semaphore: 163 | return await get_hostname(addr) 164 | 165 | tasks = [get_hostname_with_semaphore(addr) for addr in target_addrs] 166 | 167 | for future in asyncio.as_completed(tasks): 168 | addr, hostname = await future 169 | 170 | if hostname: 171 | if hostname not in all_known_hostnames: 172 | sys.stdout.write(f"{GREEN}{addr:<14} {GREEN}{hostname:<25}{CC}\n") 173 | sys.stdout.flush() 174 | new_discovered_hostnames.add(hostname) 175 | all_known_hostnames.add(hostname) 176 | else: 177 | sys.stdout.write(f"{GREEN}{addr:<14} {GREEN}{'No hostname':<25}{CC}\n") 178 | sys.stdout.flush() 179 | 180 | if new_discovered_hostnames: 181 | with open(output_path, "a") as f_write: 182 | for hostname in new_discovered_hostnames: 183 | f_write.write(hostname + "\n") 184 | 185 | except Exception as e: 186 | sys.stdout.write(f"{RED}An unexpected error occurred while fetching hostnames: {e}{CC}\n") 187 | sys.stdout.flush() 188 | 189 | sys.stdout.write(f"{Y1}{'=' * 50}{CC}\n") 190 | sys.stdout.write(f"{Y1}{' ':>2}{'Converting Hostnames to IPs':^46}{' ':>2}{CC}\n") 191 | sys.stdout.flush() 192 | 193 | new_discovered_ips = set() 194 | try: 195 | ip_conversion_semaphore = asyncio.Semaphore(max_requests_param) 196 | 197 | async def get_ip_address_with_semaphore(hostname): 198 | async with ip_conversion_semaphore: 199 | return await get_ip_address(hostname) 200 | 201 | ip_conversion_tasks = [get_ip_address_with_semaphore(hostname) for hostname in new_discovered_hostnames] 202 | 203 | for future in asyncio.as_completed(ip_conversion_tasks): 204 | original_hostname, ip_address = await future 205 | 206 | if ip_address: 207 | if ip_address not in all_known_ips: 208 | sys.stdout.write(f"{GREEN}{original_hostname:<14} {GREEN}{ip_address:<25}{CC}\n") 209 | sys.stdout.flush() 210 | new_discovered_ips.add(ip_address) 211 | all_known_ips.add(ip_address) 212 | else: 213 | sys.stdout.write(f"{GREEN}{original_hostname:<14} {GREEN}{'No IP found':<25}{CC}\n") 214 | sys.stdout.flush() 215 | 216 | if new_discovered_ips: 217 | with open(ip_output_path, "a") as ip_file_write: 218 | for ip_addr in new_discovered_ips: 219 | ip_file_write.write(ip_addr + "\n") 220 | 221 | except Exception as e: 222 | sys.stdout.write(f"{RED}An unexpected error occurred while saving IP addresses: {e}{CC}\n") 223 | sys.stdout.flush() 224 | 225 | print_stats(args.site, mask, len(new_discovered_hostnames), start_time) 226 | 227 | async def main_async(): 228 | sys.stdout.write("\033[H\033[J") 229 | sys.stdout.flush() 230 | set_dns() 231 | socket.setdefaulttimeout(SOCKET_TIMEOUT_SECONDS) 232 | 233 | parser = argparse.ArgumentParser( 234 | description=( 235 | "-h, --help show this help\n" 236 | "-s SITE Target site (e.g., www.freesite.com)\n" 237 | "-m {1,2} Subnet mask: 1=/24, 2=/16" 238 | ), 239 | epilog="Example:\n python find.py -s www.vodafone.com -m 2", 240 | formatter_class=argparse.RawTextHelpFormatter, 241 | add_help=False 242 | ) 243 | 244 | parser.add_argument( 245 | '-h', '--help', action='help', default=argparse.SUPPRESS, 246 | help=argparse.SUPPRESS 247 | ) 248 | 249 | parser.add_argument( 250 | '-s', '--site', 251 | default=DEFAULT_SITE, 252 | help=argparse.SUPPRESS 253 | ) 254 | parser.add_argument( 255 | '-m', '--mask', 256 | type=int, 257 | default=1, 258 | choices=[1, 2], 259 | help=argparse.SUPPRESS 260 | ) 261 | 262 | global args 263 | args = parser.parse_args() 264 | 265 | site_for_ip, resolved_ip = await get_ip_address(args.site) 266 | 267 | if resolved_ip: 268 | sys.stdout.write(f"{Y1}Resolved IP for {args.site}: {resolved_ip}{CC}\n") 269 | else: 270 | sys.stdout.write(f"{RED}Failed to resolve IP for {args.site}. It might be 'None'.{CC}\n") 271 | sys.stdout.flush() 272 | 273 | ip = resolved_ip 274 | 275 | if ip and '.' in ip and all(part.isdigit() and 0 <= int(part) <= 255 for part in ip.split('.')): 276 | if args.mask == 1: 277 | mask = MASK_CHOICE_1 278 | elif args.mask == 2: 279 | mask = MASK_CHOICE_2 280 | await find_hostnames_in_subnet(ip, mask, MAX_CONCURRENT_REQUESTS) 281 | else: 282 | sys.stdout.write(f"{RED}Could not resolve a valid IP for site: {args.site}. Exiting.{CC}\n") 283 | sys.stdout.flush() 284 | sys.exit(1) 285 | 286 | if __name__ == "__main__": 287 | asyncio.run(main_async()) 288 | -------------------------------------------------------------------------------- /mode_proxy.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import socket 4 | import argparse 5 | import threading 6 | import time 7 | import signal 8 | import asyncio 9 | import aiohttp 10 | import aiofiles 11 | from asyncio_throttle import Throttler 12 | import concurrent.futures 13 | 14 | import logging 15 | logging.getLogger("aiohttp").setLevel(logging.ERROR) 16 | 17 | # --- CONFIGURABLE_SETTINGS --- 18 | # Colors for output display 19 | CN = "\033[K" 20 | Y1 = "\033[33m" 21 | RED = "\033[31m" 22 | GREEN = "\033[32m" 23 | WHITE = "\033[97m" 24 | CC = "\033[0m" 25 | 26 | # Default input file for hosts (expected to be IPs for proxies) 27 | DEFAULT_HOSTS_FILE = "BugHosts/All_Hosts.txt" 28 | 29 | # Default number of concurrent operations (async tasks) 30 | DEFAULT_THREADS = 100 31 | 32 | # Default target URL to test proxy connectivity 33 | DEFAULT_TARGET_URL = "http://www.google.com/generate_204" 34 | 35 | # Default ports to test for open proxies (comma separated string) 36 | DEFAULT_PROXY_PORTS_STR = "80,8080,3128,8000,8888" 37 | 38 | # Socket timeout in seconds for individual requests 39 | SOCKET_TIMEOUT_SECONDS = 5 40 | 41 | # Directory for output files 42 | OUTPUT_DIRECTORY = "BugHosts" 43 | 44 | # Output file name for open proxies 45 | OUTPUT_OPEN_PROXIES_FILE = "open_proxies.txt" 46 | # --- END_CONFIGURABLE_SETTINGS --- 47 | 48 | lock = threading.RLock() 49 | last_stats_message = "" 50 | shutdown_event = None 51 | 52 | def clear_screen(): 53 | os.system('cls' if os.name == 'nt' else 'clear') 54 | 55 | def log(value): 56 | global last_stats_message 57 | with lock: 58 | sys.stdout.write(f"{CN}\r") 59 | print(f"{value}{CC}") 60 | sys.stdout.flush() 61 | if last_stats_message: 62 | sys.stdout.write(f"{CN}{last_stats_message}{CC}\r") 63 | sys.stdout.flush() 64 | 65 | def log_replace(value): 66 | global last_stats_message 67 | with lock: 68 | last_stats_message = value 69 | sys.stdout.write(f"{CN}{value}{CC}\r") 70 | sys.stdout.flush() 71 | 72 | def signal_handler(sig, frame): 73 | global shutdown_event 74 | print(f'\n{RED}Goodbye! Attempting graceful shutdown...{CC}') 75 | if shutdown_event: 76 | shutdown_event.set() 77 | 78 | signal.signal(signal.SIGINT, signal_handler) 79 | 80 | class ProxyHunter: 81 | def __init__(self): 82 | self.open_proxies = set() 83 | self.scanned_count = 0 84 | self.failed_tests = 0 85 | self.total_proxy_tests = 0 86 | self.output_file_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_OPEN_PROXIES_FILE) 87 | self.headers = { 88 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" 89 | } 90 | self.scan_active = True 91 | self.semaphore = None 92 | 93 | async def _request_aiohttp_proxy(self, url, session, proxy_url, timeout, verify_ssl): 94 | try: 95 | async with session.get(url, proxy=proxy_url, timeout=aiohttp.ClientTimeout(total=timeout), allow_redirects=False, ssl=verify_ssl, headers=self.headers) as response: 96 | await response.read() 97 | return response 98 | except (aiohttp.ClientError, asyncio.TimeoutError, socket.gaierror): 99 | return None 100 | except Exception: 101 | return None 102 | 103 | async def test_proxy_on_host(self, hostname, port, target_url, session, semaphore): 104 | global shutdown_event 105 | await asyncio.sleep(0) 106 | if shutdown_event.is_set(): 107 | return 108 | 109 | async with semaphore: 110 | await asyncio.sleep(0) 111 | if shutdown_event.is_set(): 112 | return 113 | 114 | is_success = False 115 | proxy_type = "UNKNOWN" 116 | status_code = "" 117 | full_proxy_address = f"{hostname}:{port}" 118 | 119 | try: 120 | proxy_url = f"http://{full_proxy_address}" 121 | response = await self._request_aiohttp_proxy(target_url, session, proxy_url, timeout=SOCKET_TIMEOUT_SECONDS, verify_ssl=False) 122 | 123 | if response: 124 | status_code = str(response.status) 125 | # Common success codes for a working proxy 126 | if response.status in [200, 204, 301, 302, 403]: 127 | is_success = True 128 | proxy_type = "HTTP" 129 | else: 130 | status_code = f"HTTP_ERR" 131 | 132 | except Exception: 133 | status_code = f"HTTP_EXC" 134 | 135 | if not is_success: 136 | # Try CONNECT method for SOCKS/HTTPS proxies 137 | sock = None 138 | try: 139 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 140 | await asyncio.wait_for( 141 | asyncio.to_thread(sock.settimeout, SOCKET_TIMEOUT_SECONDS), 142 | timeout=SOCKET_TIMEOUT_SECONDS 143 | ) 144 | 145 | proxy_ip = await asyncio.wait_for( 146 | asyncio.to_thread(socket.gethostbyname, hostname), 147 | timeout=3 148 | ) 149 | await asyncio.wait_for( 150 | asyncio.to_thread(sock.connect, (proxy_ip, port)), 151 | timeout=SOCKET_TIMEOUT_SECONDS 152 | ) 153 | 154 | connect_target_host = target_url.replace("http://", "").replace("https://", "").split('/')[0] 155 | connect_request = f"CONNECT {connect_target_host}:443 HTTP/1.1\r\nHost: {hostname}\r\n\r\n" 156 | await asyncio.wait_for( 157 | asyncio.to_thread(sock.sendall, connect_request.encode()), 158 | timeout=SOCKET_TIMEOUT_SECONDS 159 | ) 160 | 161 | response_data = await asyncio.wait_for( 162 | asyncio.to_thread(sock.recv, 4096), 163 | timeout=SOCKET_TIMEOUT_SECONDS 164 | ) 165 | response_data = response_data.decode(errors='ignore') 166 | 167 | if "200 Connection established" in response_data: 168 | is_success = True 169 | proxy_type = "CONNECT_TUNNEL" 170 | status_code = "200" 171 | elif response_data: 172 | first_line = response_data.split('\r\n')[0] 173 | if len(first_line.split(' ')) > 1: 174 | status_code = first_line.split(' ')[1] 175 | else: 176 | status_code = "Malformed Response" 177 | else: 178 | status_code = "No Response" 179 | 180 | except (socket.error, socket.timeout, asyncio.TimeoutError) as e: 181 | status_code = f"SOCK_ERR" 182 | except Exception as e: 183 | status_code = f"EXC" 184 | finally: 185 | if sock: 186 | await asyncio.to_thread(sock.close) 187 | 188 | with lock: 189 | self.scanned_count += 1 190 | if is_success: 191 | log(f"{GREEN}{full_proxy_address:<25} | {proxy_type:<10} | {status_code:<5}{CC}") 192 | self.open_proxies.add(full_proxy_address) 193 | else: 194 | self.failed_tests += 1 195 | 196 | async def save_open_proxies(self): 197 | if not os.path.exists(OUTPUT_DIRECTORY): 198 | os.makedirs(OUTPUT_DIRECTORY) 199 | async with aiofiles.open(self.output_file_path, "w") as f: 200 | for entry in sorted(list(self.open_proxies)): 201 | await f.write(f"{entry}\n") 202 | 203 | def _live_stats_updater(self): 204 | global total_scan_items 205 | global shutdown_event 206 | 207 | while self.scan_active and not shutdown_event.is_set(): 208 | with lock: 209 | stats_message = ( 210 | f"{Y1}Scanning Progress:{CC} {self.scanned_count}/{self.total_proxy_tests} | " 211 | f"{GREEN}Open Proxies:{len(self.open_proxies)}{CC} | " 212 | f"{RED}Failed:{self.failed_tests}{CC}" 213 | ) 214 | log_replace(stats_message) 215 | time.sleep(0.5) 216 | sys.stdout.write(f"{CN}\r") 217 | sys.stdout.flush() 218 | 219 | def print_stats(self, start_time): 220 | elapsed_time = time.time() - start_time 221 | label_width = 28 222 | 223 | print(f"\n{Y1}{'=' * 40}{CC}") 224 | print(f"{Y1}{'Proxy Scan Summary':^40}{CC}") 225 | print(f"{Y1}{'=' * 40}{CC}") 226 | 227 | print(f"{Y1}{'Open Proxies Found':<28}: {len(self.open_proxies)}{CC}") 228 | print(f"{Y1}{'Total Proxy Tests':<28}: {self.total_proxy_tests}{CC}") 229 | print(f"{Y1}{'Failed Proxy Tests':<28}: {self.failed_tests}{CC}") 230 | 231 | print(f"{Y1}{'Time Elapsed':<28}: {elapsed_time:.2f} seconds{CC}") 232 | print(f"{Y1}{'Saved to':<28}: {os.path.basename(self.output_file_path)}{CC}") 233 | print(f"{Y1}{'=' * 40}{CC}") 234 | 235 | async def start_scan(self, hostnames, proxy_ports, target_url, threads): 236 | start_time = time.time() 237 | if not os.path.exists(OUTPUT_DIRECTORY): 238 | os.makedirs(OUTPUT_DIRECTORY) 239 | 240 | proxy_len = 25 241 | type_len = 10 242 | code_len = 5 243 | log(f"{Y1}{'Proxy':<{proxy_len}} | {'Type':<{type_len}} | {'Code':<{code_len}}{CC}") 244 | log(f"{Y1}{'-'*proxy_len} | {'-'*type_len} | {'-'*code_len}{CC}") 245 | 246 | self.total_proxy_tests = len(hostnames) * len(proxy_ports) 247 | global total_scan_items 248 | total_scan_items = self.total_proxy_tests 249 | 250 | self.semaphore = Throttler(threads) 251 | 252 | stats_thread = threading.Thread(target=self._live_stats_updater) 253 | stats_thread.daemon = True 254 | stats_thread.start() 255 | 256 | session = None 257 | try: 258 | session = aiohttp.ClientSession(headers=self.headers, trust_env=True) 259 | async with session: 260 | scan_tasks = [] 261 | for hostname in hostnames: 262 | for port in proxy_ports: 263 | scan_tasks.append( 264 | self.test_proxy_on_host(hostname, port, target_url, session, self.semaphore) 265 | ) 266 | await asyncio.gather(*scan_tasks, return_exceptions=True) 267 | 268 | except asyncio.CancelledError: 269 | log(f"{Y1}Scan interrupted. Attempting graceful shutdown...{CC}") 270 | finally: 271 | self.scan_active = False 272 | 273 | if stats_thread.is_alive(): 274 | stats_thread.join(timeout=2) 275 | await self.save_open_proxies() 276 | self.print_stats(start_time) 277 | 278 | async def amain(): 279 | global shutdown_event 280 | shutdown_event = asyncio.Event() 281 | 282 | clear_screen() 283 | parser = argparse.ArgumentParser( 284 | description="Proxy Hunter: Scans for open HTTP/CONNECT proxies.", 285 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=52) 286 | ) 287 | parser.add_argument("-f", "--file", help=f"Input file name (default: {DEFAULT_HOSTS_FILE})", type=str, default=DEFAULT_HOSTS_FILE) 288 | parser.add_argument("-t", "--threads", help=f"Number of concurrent operations (async tasks, default: {DEFAULT_THREADS})", type=int, default=DEFAULT_THREADS) 289 | parser.add_argument("--target-url", help=f"Target URL to test connectivity through proxies (default: {DEFAULT_TARGET_URL})", type=str, default=DEFAULT_TARGET_URL) 290 | parser.add_argument("-p", "--proxy-ports", help=f"Target ports for proxy hunting (comma separated, default: {DEFAULT_PROXY_PORTS_STR})", type=str, default=DEFAULT_PROXY_PORTS_STR) 291 | 292 | global args 293 | args = parser.parse_args() 294 | 295 | hunter = ProxyHunter() 296 | 297 | if not os.path.exists(OUTPUT_DIRECTORY): 298 | os.makedirs(OUTPUT_DIRECTORY) 299 | 300 | all_hosts = set() 301 | if os.path.exists(args.file): 302 | async with aiofiles.open(args.file, 'r') as f: 303 | content = await f.read() 304 | all_hosts = set(content.splitlines()) 305 | else: 306 | print(f"{RED}Input hosts file '{args.file}' does not exist. Please create it.{CC}") 307 | sys.exit(1) 308 | 309 | hostnames_to_scan = list(all_hosts) 310 | 311 | if not hostnames_to_scan: 312 | print(f"{RED}No hosts to scan. Please add hosts to your input file.{CC}") 313 | sys.exit(0) 314 | 315 | proxy_ports_list = [] 316 | try: 317 | proxy_ports_list = list(map(int, args.proxy_ports.split(','))) 318 | except ValueError: 319 | print(f"{RED}Invalid port format for --proxy-ports. Please use comma-separated integers (e.g., 8080,3128).{CC}") 320 | sys.exit(1) 321 | 322 | try: 323 | await hunter.start_scan( 324 | hostnames_to_scan, 325 | proxy_ports_list, 326 | args.target_url, 327 | args.threads 328 | ) 329 | finally: 330 | hunter.scan_active = False 331 | current_loop = asyncio.get_running_loop() 332 | pending_tasks = [task for task in asyncio.all_tasks(current_loop) 333 | if task is not asyncio.current_task()] 334 | if pending_tasks: 335 | for task in pending_tasks: 336 | task.cancel() 337 | try: 338 | await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=5) 339 | except asyncio.TimeoutError: 340 | log(f"{RED}Timeout waiting for pending tasks to finish. Some tasks may still be active.{CC}") 341 | except Exception as e: 342 | log(f"{RED}Error during final async task cleanup: {e}{CC}") 343 | 344 | if __name__ == "__main__": 345 | try: 346 | asyncio.run(amain(), debug=False) 347 | except KeyboardInterrupt: 348 | pass 349 | except Exception as e: 350 | print(f"{RED}An unexpected error occurred: {e}{CC}") 351 | finally: 352 | try: 353 | loop = asyncio.get_event_loop() 354 | if hasattr(loop, '_default_executor') and isinstance(loop._default_executor, concurrent.futures.ThreadPoolExecutor): 355 | loop._default_executor.shutdown(wait=True, cancel_futures=True) 356 | except RuntimeError: 357 | pass 358 | except Exception as e: 359 | print(f"{RED}Error during ThreadPoolExecutor shutdown: {e}{CC}") 360 | -------------------------------------------------------------------------------- /mode_ssl.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import ssl 4 | import socket 5 | import argparse 6 | import threading 7 | import time 8 | import signal 9 | import asyncio 10 | import aiohttp 11 | import aiofiles 12 | from asyncio_throttle import Throttler 13 | import concurrent.futures 14 | 15 | import logging 16 | logging.getLogger("aiohttp").setLevel(logging.ERROR) 17 | 18 | # --- CONFIGURABLE_SETTINGS --- 19 | # Colors for output display 20 | CN = "\033[K" 21 | Y1 = "\033[33m" 22 | RED = "\033[31m" 23 | GREEN = "\033[32m" 24 | WHITE = "\033[97m" 25 | CC = "\033[0m" 26 | 27 | # Default input file for hosts 28 | DEFAULT_HOSTS_FILE = "BugHosts/All_Hosts.txt" 29 | 30 | # Default subdomain depth for SSL mode (e.g., example.com for depth 2 from sub.example.com) 31 | DEFAULT_DEEP = 2 32 | 33 | # Default number of concurrent operations (async tasks) 34 | DEFAULT_THREADS = 100 35 | 36 | # Socket timeout in seconds for individual requests 37 | SOCKET_TIMEOUT_SECONDS = 5 38 | 39 | # Directory for output files 40 | OUTPUT_DIRECTORY = "BugHosts" 41 | 42 | # Output file names for SSL mode 43 | OUTPUT_CLOUD_HOSTS_FILE = "cloud_hosts.txt" 44 | OUTPUT_OTHER_HOSTS_FILE = "other_hosts.txt" 45 | # --- END_CONFIGURABLE_SETTINGS --- 46 | 47 | lock = threading.RLock() 48 | last_stats_message = "" 49 | shutdown_event = None 50 | 51 | def clear_screen(): 52 | os.system('cls' if os.name == 'nt' else 'clear') 53 | 54 | def log(value): 55 | global last_stats_message 56 | with lock: 57 | sys.stdout.write(f"{CN}\r") 58 | print(f"{value}{CC}") 59 | sys.stdout.flush() 60 | if last_stats_message: 61 | sys.stdout.write(f"{CN}{last_stats_message}{CC}\r") 62 | sys.stdout.flush() 63 | 64 | def log_replace(value): 65 | global last_stats_message 66 | with lock: 67 | last_stats_message = value 68 | sys.stdout.write(f"{CN}{value}{CC}\r") 69 | sys.stdout.flush() 70 | 71 | def signal_handler(sig, frame): 72 | global shutdown_event 73 | print(f'\n{RED}Goodbye! Attempting graceful shutdown...{CC}') 74 | if shutdown_event: 75 | shutdown_event.set() 76 | 77 | signal.signal(signal.SIGINT, signal_handler) 78 | 79 | class SSLScanner: 80 | def __init__(self): 81 | self.scanned_ssl = set() 82 | self.cloud_hosts = set() 83 | self.other_hosts = set() 84 | self.failed_scans = 0 85 | self.headers = { 86 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" 87 | } 88 | self.scanned_count = 0 89 | self.live_cloud_hosts_count = 0 90 | self.live_other_hosts_count = 0 91 | self.live_failed_scans = 0 92 | self.scan_active = True 93 | self.cloud_hosts_output_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_CLOUD_HOSTS_FILE) 94 | self.other_hosts_output_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_OTHER_HOSTS_FILE) 95 | self.semaphore = None 96 | 97 | async def _resolve_single_async(self, hostname): 98 | try: 99 | cname, _, host_list = await asyncio.wait_for( 100 | asyncio.to_thread(socket.gethostbyname_ex, hostname), 101 | timeout=3 102 | ) 103 | return hostname, [(host, cname if host == host_list[-1] else hostname) for host in host_list] 104 | except (socket.gaierror, socket.herror, asyncio.TimeoutError): 105 | return hostname, [(None, "DNS Failed")] 106 | except Exception: 107 | return hostname, [(None, "DNS Failed")] 108 | 109 | async def resolve(self, hostnames): 110 | results = {} 111 | dns_semaphore = asyncio.Semaphore(50) 112 | tasks = [] 113 | for hostname in hostnames: 114 | async def resolve_with_semaphore(hname): 115 | async with dns_semaphore: 116 | if shutdown_event.is_set(): return hname, [] 117 | return await self._resolve_single_async(hname) 118 | tasks.append(resolve_with_semaphore(hostname)) 119 | 120 | resolved_list = await asyncio.gather(*tasks, return_exceptions=True) 121 | for item in resolved_list: 122 | if isinstance(item, Exception): 123 | continue 124 | hostname, resolved = item 125 | results[hostname] = resolved 126 | return results 127 | 128 | async def get_sni_response(self, hostname, deep): 129 | server_name_indication = ".".join(hostname.split(".")[0 - deep:]) 130 | if server_name_indication in self.scanned_ssl: 131 | return None 132 | 133 | with lock: 134 | self.scanned_ssl.add(server_name_indication) 135 | 136 | sock = None 137 | try: 138 | host_ip = await asyncio.wait_for( 139 | asyncio.to_thread(socket.gethostbyname, server_name_indication), 140 | timeout=3 141 | ) 142 | 143 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 144 | await asyncio.to_thread(sock.settimeout, SOCKET_TIMEOUT_SECONDS) 145 | 146 | wrapped_sock = await asyncio.to_thread( 147 | ssl.create_default_context().wrap_socket, 148 | sock, server_hostname=server_name_indication 149 | ) 150 | await asyncio.wait_for( 151 | asyncio.to_thread(wrapped_sock.connect, (host_ip, 443)), 152 | timeout=SOCKET_TIMEOUT_SECONDS 153 | ) 154 | return {"status": "True", "server_name_indication": server_name_indication} 155 | except (socket.error, socket.timeout, ssl.SSLError, asyncio.TimeoutError): 156 | return {"status": "", "server_name_indication": server_name_indication} 157 | except Exception: 158 | return {"status": "", "server_name_indication": server_name_indication} 159 | finally: 160 | if sock: 161 | await asyncio.to_thread(sock.close) 162 | 163 | def print_result(self, host, hostname, sni=None): 164 | if sni and sni == "True": 165 | color = GREEN 166 | host_display = f"{host if host else hostname:<20}" 167 | status_str = f"{'SSL OK':<15}" 168 | log(f"{color}{host_display} | {status_str}{CC}") 169 | 170 | # Simplified logic for SSL, classifying based on hostname 171 | # You might need a more sophisticated check for "cloud" in SNI response 172 | if "cloud" in hostname.lower() or "cdn" in hostname.lower() or "azure" in hostname.lower() or "amazon" in hostname.lower(): # Basic heuristic 173 | self.cloud_hosts.add(hostname) 174 | with lock: 175 | self.live_cloud_hosts_count = len(self.cloud_hosts) 176 | else: 177 | self.other_hosts.add(hostname) 178 | with lock: 179 | self.live_other_hosts_count = len(self.other_hosts) 180 | 181 | def _live_stats_updater(self): 182 | global total_scan_items 183 | global shutdown_event 184 | 185 | while self.scan_active and not shutdown_event.is_set(): 186 | with lock: 187 | total_successful_connections = self.live_cloud_hosts_count + self.live_other_hosts_count 188 | stats_message = ( 189 | f"{Y1}Scanning Progress:{CC} {self.scanned_count}/{total_scan_items} | " 190 | f"{GREEN}Successful:{total_successful_connections}{CC} | " 191 | f"{RED}Failed:{self.live_failed_scans}{CC}" 192 | ) 193 | log_replace(stats_message) 194 | time.sleep(0.5) 195 | sys.stdout.write(f"{CN}\r") 196 | sys.stdout.flush() 197 | 198 | def print_stats(self, start_time): 199 | elapsed_time = time.time() - start_time 200 | label_width = 28 201 | 202 | print(f"\n{Y1}{'=' * 40}{CC}") 203 | print(f"{Y1}{'SSL Scan Statistics':^40}{CC}") 204 | print(f"{Y1}{'=' * 40}{CC}") 205 | 206 | total_successful_connections = len(self.cloud_hosts) + len(self.other_hosts) 207 | print(f"{Y1}{'Successful Connections':<28}: {total_successful_connections}{CC}") 208 | print(f"{Y1}{'Failed Connections':<28}: {self.failed_scans}{CC}") 209 | 210 | print(f"{Y1}{'Time Elapsed':<28}: {elapsed_time:.2f} seconds{CC}") 211 | print(f"{Y1}{'Saved Cloud Hosts to':<28}: {os.path.basename(self.cloud_hosts_output_path)}{CC}") 212 | print(f"{Y1}{'Saved Other Hosts to':<28}: {os.path.basename(self.other_hosts_output_path)}{CC}") 213 | print(f"{Y1}{'=' * 40}{CC}") 214 | 215 | async def _scan_single_target(self, hostname, deep, resolved_hosts_map): 216 | global shutdown_event 217 | await asyncio.sleep(0) 218 | 219 | if shutdown_event.is_set(): 220 | return [] 221 | 222 | async with self.semaphore: 223 | await asyncio.sleep(0) 224 | if shutdown_event.is_set(): 225 | return [] 226 | 227 | results = [] 228 | resolved_for_host = resolved_hosts_map.get(hostname, [(None, "DNS Failed")]) 229 | 230 | if not resolved_for_host or all(host_ip is None for host_ip, _ in resolved_for_host): 231 | with lock: 232 | self.failed_scans += 1 233 | self.scanned_count += 1 234 | self.live_failed_scans = self.failed_scans 235 | return results 236 | 237 | for host_ip, resolved_hostname in resolved_for_host: 238 | await asyncio.sleep(0) 239 | if shutdown_event.is_set(): return [] 240 | if host_ip is None: continue 241 | 242 | response = await self.get_sni_response(resolved_hostname, deep) 243 | with lock: 244 | self.scanned_count += 1 245 | if response and response["status"] == "True": 246 | self.print_result(host_ip, response["server_name_indication"], sni=response["status"]) 247 | results.append((host_ip, response["server_name_indication"], None, None, None, response["status"])) 248 | else: 249 | self.failed_scans += 1 250 | self.live_failed_scans = self.failed_scans 251 | return results 252 | 253 | async def start_scan(self, hostnames, deep, threads): 254 | start_time = time.time() 255 | if not os.path.exists(OUTPUT_DIRECTORY): 256 | os.makedirs(OUTPUT_DIRECTORY) 257 | 258 | host_len = 20 259 | status_len = 15 260 | 261 | log(f"{Y1}{'Host':<{host_len}} | {'Status':<{status_len}}{CC}") 262 | log(f"{Y1}{'-'*host_len} | {'-'*status_len}{CC}") 263 | 264 | global total_scan_items 265 | total_scan_items = len(hostnames) 266 | 267 | self.semaphore = Throttler(threads) 268 | 269 | stats_thread = threading.Thread(target=self._live_stats_updater) 270 | stats_thread.daemon = True 271 | stats_thread.start() 272 | 273 | try: 274 | resolved_hosts_map = await self.resolve(hostnames) 275 | 276 | scan_tasks = [] 277 | for hostname in hostnames: 278 | scan_tasks.append( 279 | self._scan_single_target(hostname, deep, resolved_hosts_map) 280 | ) 281 | await asyncio.gather(*scan_tasks, return_exceptions=True) 282 | 283 | except asyncio.CancelledError: 284 | log(f"{Y1}Scan interrupted. Attempting graceful shutdown...{CC}") 285 | finally: 286 | self.scan_active = False 287 | 288 | if stats_thread.is_alive(): 289 | stats_thread.join(timeout=2) 290 | 291 | async with aiofiles.open(self.cloud_hosts_output_path, "a+") as f: 292 | await f.seek(0) 293 | existing_content = await f.read() 294 | existing = set(existing_content.splitlines()) 295 | for hostname in self.cloud_hosts: 296 | if hostname not in existing: 297 | await f.write(f"{hostname}\n") 298 | async with aiofiles.open(self.other_hosts_output_path, "a+") as f: 299 | await f.seek(0) 300 | existing_content = await f.read() 301 | existing = set(existing_content.splitlines()) 302 | for hostname in self.other_hosts: 303 | if hostname not in existing: 304 | await f.write(f"{hostname}\n") 305 | 306 | self.print_stats(start_time) 307 | 308 | async def amain(): 309 | global shutdown_event 310 | shutdown_event = asyncio.Event() 311 | 312 | clear_screen() 313 | parser = argparse.ArgumentParser( 314 | description="SSL Host Scanner: Scans hosts for valid SSL connections (SNI).", 315 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=52) 316 | ) 317 | parser.add_argument("-d", "--deep", help=f"Subdomain depth for SNI (e.g., 2 for example.com from sub.example.com, default: {DEFAULT_DEEP})", type=int, default=DEFAULT_DEEP) 318 | parser.add_argument("-f", "--file", help=f"Input file name (default: {DEFAULT_HOSTS_FILE})", type=str, default=DEFAULT_HOSTS_FILE) 319 | parser.add_argument("-t", "--threads", help=f"Number of concurrent operations (async tasks, default: {DEFAULT_THREADS})", type=int, default=DEFAULT_THREADS) 320 | 321 | global args 322 | args = parser.parse_args() 323 | 324 | scanner = SSLScanner() 325 | 326 | if not os.path.exists(OUTPUT_DIRECTORY): 327 | os.makedirs(OUTPUT_DIRECTORY) 328 | 329 | all_hosts = set() 330 | if os.path.exists(args.file): 331 | async with aiofiles.open(args.file, 'r') as f: 332 | content = await f.read() 333 | all_hosts = set(content.splitlines()) 334 | else: 335 | print(f"{RED}Input hosts file '{args.file}' does not exist. Please create it.{CC}") 336 | sys.exit(1) 337 | 338 | hostnames_to_scan = list(all_hosts) 339 | 340 | if not hostnames_to_scan: 341 | print(f"{RED}No hosts to scan. Please add hosts to your input file.{CC}") 342 | sys.exit(0) 343 | 344 | try: 345 | await scanner.start_scan( 346 | hostnames_to_scan, 347 | args.deep, 348 | args.threads 349 | ) 350 | finally: 351 | scanner.scan_active = False 352 | current_loop = asyncio.get_running_loop() 353 | pending_tasks = [task for task in asyncio.all_tasks(current_loop) 354 | if task is not asyncio.current_task()] 355 | if pending_tasks: 356 | for task in pending_tasks: 357 | task.cancel() 358 | try: 359 | await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=5) 360 | except asyncio.TimeoutError: 361 | log(f"{RED}Timeout waiting for pending tasks to finish. Some tasks may still be active.{CC}") 362 | except Exception as e: 363 | log(f"{RED}Error during final async task cleanup: {e}{CC}") 364 | 365 | if __name__ == "__main__": 366 | try: 367 | asyncio.run(amain(), debug=False) 368 | except KeyboardInterrupt: 369 | pass 370 | except Exception as e: 371 | print(f"{RED}An unexpected error occurred: {e}{CC}") 372 | finally: 373 | try: 374 | loop = asyncio.get_event_loop() 375 | if hasattr(loop, '_default_executor') and isinstance(loop._default_executor, concurrent.futures.ThreadPoolExecutor): 376 | loop._default_executor.shutdown(wait=True, cancel_futures=True) 377 | except RuntimeError: 378 | pass 379 | except Exception as e: 380 | print(f"{RED}Error during ThreadPoolExecutor shutdown: {e}{CC}") 381 | -------------------------------------------------------------------------------- /mode_direct.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import socket 4 | import argparse 5 | import threading 6 | import time 7 | import signal 8 | import asyncio 9 | import aiohttp 10 | import aiofiles 11 | from asyncio_throttle import Throttler 12 | import concurrent.futures 13 | 14 | import logging 15 | logging.getLogger("aiohttp").setLevel(logging.ERROR) 16 | 17 | # --- CONFIGURABLE_SETTINGS --- 18 | # Colors for output display 19 | CN = "\033[K" 20 | Y1 = "\033[33m" 21 | RED = "\033[31m" 22 | GREEN = "\033[32m" 23 | WHITE = "\033[97m" 24 | CC = "\033[0m" 25 | 26 | # Default input file for hosts 27 | DEFAULT_HOSTS_FILE = "BugHosts/All_Hosts.txt" 28 | 29 | # Default ports for direct mode (comma separated string) 30 | DEFAULT_PORTS_STR = "80,443" 31 | 32 | # Default HTTP method for direct mode 33 | DEFAULT_METHOD = "HEAD" 34 | 35 | # Default number of concurrent operations (async tasks) 36 | DEFAULT_THREADS = 100 37 | 38 | # Default proxy for direct mode (e.g., proxy.example.com:8080) 39 | DEFAULT_PROXY = None # Set to "your_proxy_ip:port" if you want to use a default proxy 40 | 41 | # Socket timeout in seconds for individual requests 42 | SOCKET_TIMEOUT_SECONDS = 5 43 | 44 | # Directory for output files 45 | OUTPUT_DIRECTORY = "BugHosts" 46 | 47 | # Output file names for direct mode 48 | OUTPUT_CLOUD_HOSTS_FILE = "cloud_hosts.txt" 49 | OUTPUT_OTHER_HOSTS_FILE = "other_hosts.txt" 50 | # --- END_CONFIGURABLE_SETTINGS --- 51 | 52 | lock = threading.RLock() 53 | last_stats_message = "" 54 | shutdown_event = None 55 | 56 | def clear_screen(): 57 | os.system('cls' if os.name == 'nt' else 'clear') 58 | 59 | def log(value): 60 | global last_stats_message 61 | with lock: 62 | sys.stdout.write(f"{CN}\r") 63 | print(f"{value}{CC}") 64 | sys.stdout.flush() 65 | if last_stats_message: 66 | sys.stdout.write(f"{CN}{last_stats_message}{CC}\r") 67 | sys.stdout.flush() 68 | 69 | def log_replace(value): 70 | global last_stats_message 71 | with lock: 72 | last_stats_message = value 73 | sys.stdout.write(f"{CN}{value}{CC}\r") 74 | sys.stdout.flush() 75 | 76 | def signal_handler(sig, frame): 77 | global shutdown_event 78 | print(f'\n{RED}Goodbye! Attempting graceful shutdown...{CC}') 79 | if shutdown_event: 80 | shutdown_event.set() 81 | 82 | signal.signal(signal.SIGINT, signal_handler) 83 | 84 | class DirectScanner: 85 | def __init__(self): 86 | self.scanned_direct = set() 87 | self.cloud_hosts = set() 88 | self.other_hosts = set() 89 | self.failed_scans = 0 90 | self.headers = { 91 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" 92 | } 93 | self.scanned_count = 0 94 | self.live_cloud_hosts_count = 0 95 | self.live_other_hosts_count = 0 96 | self.live_failed_scans = 0 97 | self.scan_active = True 98 | self.cloud_hosts_output_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_CLOUD_HOSTS_FILE) 99 | self.other_hosts_output_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_OTHER_HOSTS_FILE) 100 | self.semaphore = None 101 | 102 | async def _request_aiohttp(self, method, url, session, proxy=None, headers=None, allow_redirects=False, verify_ssl=False, timeout=SOCKET_TIMEOUT_SECONDS): 103 | try: 104 | async with session.request( 105 | method, url, proxy=proxy, headers=headers, allow_redirects=allow_redirects, 106 | ssl=verify_ssl, timeout=aiohttp.ClientTimeout(total=timeout) 107 | ) as response: 108 | await response.read() 109 | return response 110 | except (aiohttp.ClientError, asyncio.TimeoutError, socket.gaierror): 111 | return None 112 | except Exception: 113 | return None 114 | 115 | async def _resolve_single_async(self, hostname): 116 | try: 117 | cname, _, host_list = await asyncio.wait_for( 118 | asyncio.to_thread(socket.gethostbyname_ex, hostname), 119 | timeout=3 120 | ) 121 | return hostname, [(host, cname if host == host_list[-1] else hostname) for host in host_list] 122 | except (socket.gaierror, socket.herror, asyncio.TimeoutError): 123 | return hostname, [(None, "DNS Failed")] 124 | except Exception: 125 | return hostname, [(None, "DNS Failed")] 126 | 127 | async def resolve(self, hostnames): 128 | results = {} 129 | dns_semaphore = asyncio.Semaphore(50) 130 | tasks = [] 131 | for hostname in hostnames: 132 | async def resolve_with_semaphore(hname): 133 | async with dns_semaphore: 134 | if shutdown_event.is_set(): return hname, [] 135 | return await self._resolve_single_async(hname) 136 | tasks.append(resolve_with_semaphore(hostname)) 137 | 138 | resolved_list = await asyncio.gather(*tasks, return_exceptions=True) 139 | for item in resolved_list: 140 | if isinstance(item, Exception): 141 | continue 142 | hostname, resolved = item 143 | results[hostname] = resolved 144 | return results 145 | 146 | async def get_http_response(self, method, hostname, port, session, proxy=None): 147 | url = f"{('https' if port == 443 else 'http')}://{hostname if port == 443 else f'{hostname}:{port}'}" 148 | if proxy: 149 | proxy_url = f"http://{proxy}" 150 | else: 151 | proxy_url = None 152 | 153 | response = await self._request_aiohttp(method, url, session, proxy=proxy_url, headers=self.headers, verify_ssl=False) 154 | if response is None: 155 | return {"status_code": "", "server": ""} 156 | return {"status_code": str(response.status), "server": response.headers.get("server", "")} 157 | 158 | def print_result(self, host, hostname, port=None, status_code=None, server=None): 159 | if status_code and status_code != "": 160 | color = GREEN 161 | 162 | host_display = f"{host if host else hostname:<20}" 163 | status_code_str = f"{status_code if status_code else 'N/A':<4}" 164 | server_str = f"{server if server else 'Unknown':<15}" 165 | log(f"{color}{host_display} | {status_code_str} | {server_str}{CC}") 166 | 167 | if server and "cloud" in server.lower(): 168 | self.cloud_hosts.add(hostname) 169 | with lock: 170 | self.live_cloud_hosts_count = len(self.cloud_hosts) 171 | else: 172 | self.other_hosts.add(hostname) 173 | with lock: 174 | self.live_other_hosts_count = len(self.other_hosts) 175 | 176 | def _live_stats_updater(self): 177 | global total_scan_items 178 | global shutdown_event 179 | 180 | while self.scan_active and not shutdown_event.is_set(): 181 | with lock: 182 | total_successful_connections = self.live_cloud_hosts_count + self.live_other_hosts_count 183 | stats_message = ( 184 | f"{Y1}Scanning Progress:{CC} {self.scanned_count}/{total_scan_items} | " 185 | f"{GREEN}Successful:{total_successful_connections}{CC} | " 186 | f"{RED}Failed:{self.live_failed_scans}{CC}" 187 | ) 188 | log_replace(stats_message) 189 | time.sleep(0.5) 190 | sys.stdout.write(f"{CN}\r") 191 | sys.stdout.flush() 192 | 193 | def print_stats(self, start_time): 194 | elapsed_time = time.time() - start_time 195 | label_width = 28 196 | 197 | print(f"\n{Y1}{'=' * 40}{CC}") 198 | print(f"{Y1}{'Direct Scan Statistics':^40}{CC}") 199 | print(f"{Y1}{'=' * 40}{CC}") 200 | 201 | total_successful_connections = len(self.cloud_hosts) + len(self.other_hosts) 202 | print(f"{Y1}{'Successful Connections':<28}: {total_successful_connections}{CC}") 203 | print(f"{Y1}{'Failed Connections':<28}: {self.failed_scans}{CC}") 204 | 205 | print(f"{Y1}{'Time Elapsed':<28}: {elapsed_time:.2f} seconds{CC}") 206 | print(f"{Y1}{'Saved Cloud Hosts to':<28}: {os.path.basename(self.cloud_hosts_output_path)}{CC}") 207 | print(f"{Y1}{'Saved Other Hosts to':<28}: {os.path.basename(self.other_hosts_output_path)}{CC}") 208 | 209 | print(f"{Y1}{'=' * 40}{CC}") 210 | 211 | async def _scan_single_target(self, hostname, ports, method, proxy, resolved_hosts_map, session): 212 | global shutdown_event 213 | await asyncio.sleep(0) 214 | 215 | if shutdown_event.is_set(): 216 | return [] 217 | 218 | async with self.semaphore: 219 | await asyncio.sleep(0) 220 | if shutdown_event.is_set(): 221 | return [] 222 | 223 | results = [] 224 | resolved_for_host = resolved_hosts_map.get(hostname, [(None, "DNS Failed")]) 225 | 226 | if not resolved_for_host or all(host_ip is None for host_ip, _ in resolved_for_host): 227 | with lock: 228 | self.failed_scans += 1 229 | self.scanned_count += 1 230 | self.live_failed_scans = self.failed_scans 231 | return results 232 | 233 | for host_ip, resolved_hostname in resolved_for_host: 234 | await asyncio.sleep(0) 235 | if shutdown_event.is_set(): return [] 236 | if host_ip is None: continue 237 | 238 | for port in ports: 239 | await asyncio.sleep(0) 240 | if shutdown_event.is_set(): return [] 241 | response = await self.get_http_response(method, resolved_hostname, port, session, proxy) 242 | with lock: 243 | self.scanned_count += 1 244 | if response["status_code"] != "": 245 | self.print_result(host_ip, resolved_hostname, port, response["status_code"], response["server"]) 246 | results.append((host_ip, resolved_hostname, port, response["status_code"], response["server"], None)) 247 | else: 248 | self.failed_scans += 1 249 | self.live_failed_scans = self.failed_scans 250 | return results 251 | 252 | async def start_scan(self, hostnames, ports, method, proxy, threads): 253 | start_time = time.time() 254 | if not os.path.exists(OUTPUT_DIRECTORY): 255 | os.makedirs(OUTPUT_DIRECTORY) 256 | 257 | host_len = 20 258 | code_len = 4 259 | server_len = 15 260 | 261 | log(f"{Y1}{'Host':<{host_len}} | {'Code':<{code_len}} | {'Server':<{server_len}}{CC}") 262 | log(f"{Y1}{'-'*host_len} | {'-'*code_len} | {'-'*server_len}{CC}") 263 | 264 | global total_scan_items 265 | total_scan_items = len(hostnames) * len(ports) 266 | 267 | self.semaphore = Throttler(threads) 268 | 269 | stats_thread = threading.Thread(target=self._live_stats_updater) 270 | stats_thread.daemon = True 271 | stats_thread.start() 272 | 273 | session = None 274 | try: 275 | session = aiohttp.ClientSession(headers=self.headers, trust_env=True) 276 | async with session: 277 | resolved_hosts_map = await self.resolve(hostnames) 278 | 279 | scan_tasks = [] 280 | for hostname in hostnames: 281 | scan_tasks.append( 282 | self._scan_single_target(hostname, ports, method, proxy, resolved_hosts_map, session) 283 | ) 284 | await asyncio.gather(*scan_tasks, return_exceptions=True) 285 | 286 | except asyncio.CancelledError: 287 | log(f"{Y1}Scan interrupted. Attempting graceful shutdown...{CC}") 288 | finally: 289 | self.scan_active = False 290 | 291 | if stats_thread.is_alive(): 292 | stats_thread.join(timeout=2) 293 | 294 | async with aiofiles.open(self.cloud_hosts_output_path, "a+") as f: 295 | await f.seek(0) 296 | existing_content = await f.read() 297 | existing = set(existing_content.splitlines()) 298 | for hostname in self.cloud_hosts: 299 | if hostname not in existing: 300 | await f.write(f"{hostname}\n") 301 | async with aiofiles.open(self.other_hosts_output_path, "a+") as f: 302 | await f.seek(0) 303 | existing_content = await f.read() 304 | existing = set(existing_content.splitlines()) 305 | for hostname in self.other_hosts: 306 | if hostname not in existing: 307 | await f.write(f"{hostname}\n") 308 | 309 | self.print_stats(start_time) 310 | 311 | async def amain(): 312 | global shutdown_event 313 | shutdown_event = asyncio.Event() 314 | 315 | clear_screen() 316 | parser = argparse.ArgumentParser( 317 | description="Direct Host Scanner: Scans hosts for open HTTP/HTTPS ports.", 318 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=52) 319 | ) 320 | parser.add_argument("-f", "--file", help=f"Input file name (default: {DEFAULT_HOSTS_FILE})", type=str, default=DEFAULT_HOSTS_FILE) 321 | parser.add_argument("-p", "--ports", help=f"Target ports (comma separated, default: {DEFAULT_PORTS_STR})", type=str, default=DEFAULT_PORTS_STR) 322 | parser.add_argument("-t", "--threads", help=f"Number of concurrent operations (async tasks, default: {DEFAULT_THREADS})", type=int, default=DEFAULT_THREADS) 323 | parser.add_argument("-M", "--method", help=f"HTTP method (default: {DEFAULT_METHOD})", type=str, default=DEFAULT_METHOD) 324 | parser.add_argument("-P", "--proxy", help=f"Proxy for direct mode (e.g., proxy.example.com:8080, default: {DEFAULT_PROXY})", type=str, default=DEFAULT_PROXY) 325 | 326 | global args 327 | args = parser.parse_args() 328 | 329 | scanner = DirectScanner() 330 | 331 | if not os.path.exists(OUTPUT_DIRECTORY): 332 | os.makedirs(OUTPUT_DIRECTORY) 333 | 334 | all_hosts = set() 335 | if os.path.exists(args.file): 336 | async with aiofiles.open(args.file, 'r') as f: 337 | content = await f.read() 338 | all_hosts = set(content.splitlines()) 339 | else: 340 | print(f"{RED}Input hosts file '{args.file}' does not exist. Please create it.{CC}") 341 | sys.exit(1) 342 | 343 | hostnames_to_scan = list(all_hosts) 344 | 345 | if not hostnames_to_scan: 346 | print(f"{RED}No hosts to scan. Please add hosts to your input file.{CC}") 347 | sys.exit(0) 348 | 349 | ports_list = [] 350 | try: 351 | ports_list = list(map(int, args.ports.split(','))) 352 | except ValueError: 353 | print(f"{RED}Invalid port format for --ports. Please use comma-separated integers (e.g., 80,443).{CC}") 354 | sys.exit(1) 355 | 356 | try: 357 | await scanner.start_scan( 358 | hostnames_to_scan, 359 | ports_list, 360 | args.method.upper(), 361 | args.proxy, 362 | args.threads 363 | ) 364 | finally: 365 | scanner.scan_active = False 366 | current_loop = asyncio.get_running_loop() 367 | pending_tasks = [task for task in asyncio.all_tasks(current_loop) 368 | if task is not asyncio.current_task()] 369 | if pending_tasks: 370 | for task in pending_tasks: 371 | task.cancel() 372 | try: 373 | await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=5) 374 | except asyncio.TimeoutError: 375 | log(f"{RED}Timeout waiting for pending tasks to finish. Some tasks may still be active.{CC}") 376 | except Exception as e: 377 | log(f"{RED}Error during final async task cleanup: {e}{CC}") 378 | 379 | if __name__ == "__main__": 380 | try: 381 | asyncio.run(amain(), debug=False) 382 | except KeyboardInterrupt: 383 | pass 384 | except Exception as e: 385 | print(f"{RED}An unexpected error occurred: {e}{CC}") 386 | finally: 387 | try: 388 | loop = asyncio.get_event_loop() 389 | if hasattr(loop, '_default_executor') and isinstance(loop._default_executor, concurrent.futures.ThreadPoolExecutor): 390 | loop._default_executor.shutdown(wait=True, cancel_futures=True) 391 | except RuntimeError: 392 | pass 393 | except Exception as e: 394 | print(f"{RED}Error during ThreadPoolExecutor shutdown: {e}{CC}") 395 | -------------------------------------------------------------------------------- /mode_payload.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import socket 4 | import argparse 5 | import threading 6 | import time 7 | import signal 8 | import asyncio 9 | import aiohttp 10 | import aiofiles 11 | from asyncio_throttle import Throttler 12 | import concurrent.futures 13 | 14 | import logging 15 | logging.getLogger("aiohttp").setLevel(logging.ERROR) 16 | 17 | # --- CONFIGURABLE_SETTINGS --- 18 | # Colors for output display 19 | CN = "\033[K" 20 | Y1 = "\033[33m" 21 | RED = "\033[31m" 22 | GREEN = "\033[32m" 23 | WHITE = "\033[97m" 24 | CC = "\033[0m" 25 | 26 | # Default input file for hosts 27 | DEFAULT_HOSTS_FILE = "BugHosts/All_Hosts.txt" 28 | 29 | # Default file containing payload definitions 30 | DEFAULT_PAYLOAD_FILE = "payloads.txt" 31 | 32 | # Default target URL to test connectivity through payloads 33 | DEFAULT_TARGET_URL = "http://www.google.com/generate_204" 34 | 35 | # Default number of concurrent operations (async tasks) 36 | DEFAULT_THREADS = 100 37 | 38 | # Socket timeout in seconds for individual requests (payload tests) 39 | SOCKET_TIMEOUT_SECONDS = 5 40 | 41 | # Directory for output files 42 | OUTPUT_DIRECTORY = "BugHosts" 43 | 44 | # Output file name for found bug hosts 45 | OUTPUT_PAYLOAD_BUGS_FILE = "payload_bugs.txt" 46 | 47 | # Output file name for payloads that caused errors (for debugging) 48 | OUTPUT_EXC_PAYLOADS_FILE = "payloads_exc.txt" 49 | # --- END_CONFIGURABLE_SETTINGS --- 50 | 51 | lock = threading.RLock() 52 | last_stats_message = "" 53 | shutdown_event = None 54 | 55 | def clear_screen(): 56 | os.system('cls' if os.name == 'nt' else 'clear') 57 | 58 | def log(value): 59 | global last_stats_message 60 | with lock: 61 | sys.stdout.write(f"{CN}\r") 62 | print(f"{value}{CC}") 63 | sys.stdout.flush() 64 | if last_stats_message: 65 | sys.stdout.write(f"{CN}{last_stats_message}{CC}\r") 66 | sys.stdout.flush() 67 | 68 | def log_replace(value): 69 | global last_stats_message 70 | with lock: 71 | last_stats_message = value 72 | sys.stdout.write(f"{CN}{value}{CC}\r") 73 | sys.stdout.flush() 74 | 75 | def signal_handler(sig, frame): 76 | global shutdown_event 77 | print(f'\n{RED}Goodbye! Attempting graceful shutdown...{CC}') 78 | if shutdown_event: 79 | shutdown_event.set() 80 | 81 | signal.signal(signal.SIGINT, signal_handler) 82 | 83 | class PayloadTester: 84 | def __init__(self): 85 | self.found_bug_hosts = set() 86 | self.scanned_count = 0 87 | self.failed_tests = 0 88 | self.total_payload_tests = 0 89 | self.output_file_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_PAYLOAD_BUGS_FILE) 90 | self.exc_payloads_file_path = os.path.join(OUTPUT_DIRECTORY, OUTPUT_EXC_PAYLOADS_FILE) 91 | self.payloads = [] 92 | self.headers = { 93 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" 94 | } 95 | self.scan_active = True 96 | 97 | async def load_payloads(self, filename): 98 | if not os.path.exists(filename): 99 | log(f"{RED}Payload file '{filename}' not found. Please create it.{CC}") 100 | sys.exit(1) 101 | 102 | async with aiofiles.open(filename, 'r') as f: 103 | async for line in f: 104 | line = line.strip() 105 | if line and not line.startswith('#'): 106 | self.payloads.append(line) 107 | if not self.payloads: 108 | log(f"{RED}No payloads found in '{filename}'. Exiting.{CC}") 109 | sys.exit(1) 110 | 111 | async def log_exc_payload(self, hostname, payload_line, error_message): 112 | if not os.path.exists(OUTPUT_DIRECTORY): 113 | os.makedirs(OUTPUT_DIRECTORY) 114 | async with aiofiles.open(self.exc_payloads_file_path, "a") as f: 115 | await f.write(f"Host: {hostname} | Payload: {payload_line} | Error: {error_message}\n") 116 | 117 | async def _request_aiohttp(self, method, url, session, headers, allow_redirects, verify_ssl, timeout): 118 | try: 119 | async with session.request( 120 | method, url, headers=headers, allow_redirects=allow_redirects, 121 | ssl=verify_ssl, timeout=aiohttp.ClientTimeout(total=timeout) 122 | ) as response: 123 | await response.read() 124 | return response 125 | except (aiohttp.ClientError, asyncio.TimeoutError, socket.gaierror): 126 | return None 127 | except Exception: 128 | return None 129 | 130 | async def test_payload_on_host(self, hostname, payload_line, target_url, session, semaphore): 131 | global shutdown_event 132 | await asyncio.sleep(0) 133 | if shutdown_event.is_set(): 134 | return 135 | 136 | async with semaphore: 137 | await asyncio.sleep(0) 138 | if shutdown_event.is_set(): 139 | return 140 | 141 | current_status_code = "" 142 | is_success = False 143 | 144 | headers = self.headers.copy() 145 | method = "GET" 146 | url_to_request = target_url 147 | 148 | try: 149 | parts = payload_line.split(':', 1) 150 | payload_type = parts[0].strip().upper() 151 | payload_value = parts[1].strip() if len(parts) > 1 else "" 152 | 153 | if payload_type == "HOST_HEADER": 154 | header_parts = payload_value.split(':', 1) 155 | header_name = header_parts[0].strip() 156 | header_value = header_parts[1].strip() 157 | headers[header_name] = header_value 158 | url_to_request = f"http://{hostname}" 159 | elif payload_type == "URL_PREFIX": 160 | url_to_request = payload_value.replace("TARGET_URL", target_url) 161 | elif payload_type == "URL_SUFFIX": 162 | url_to_request = f"{target_url}{payload_value}" 163 | elif payload_type == "CONNECT_METHOD": 164 | raise NotImplementedError("CONNECT_METHOD payloads are not yet supported directly via aiohttp. Requires raw async sockets.") 165 | elif payload_type == "HTTP_REQUEST_LINE_INJECTION": 166 | sock = None 167 | try: 168 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 169 | await asyncio.wait_for( 170 | asyncio.to_thread(sock.settimeout, SOCKET_TIMEOUT_SECONDS), 171 | timeout=SOCKET_TIMEOUT_SECONDS 172 | ) 173 | host_ip = await asyncio.wait_for( 174 | asyncio.to_thread(socket.gethostbyname, hostname), 175 | timeout=3 176 | ) 177 | await asyncio.wait_for( 178 | asyncio.to_thread(sock.connect, (host_ip, 80)), 179 | timeout=SOCKET_TIMEOUT_SECONDS 180 | ) 181 | 182 | full_request = f"{payload_value.replace('TARGET_URL', target_url).replace('[crlf]', '\\r\\n')}\r\nHost: {hostname}\r\n\r\n" 183 | await asyncio.wait_for( 184 | asyncio.to_thread(sock.sendall, full_request.encode()), 185 | timeout=SOCKET_TIMEOUT_SECONDS 186 | ) 187 | 188 | response_data = await asyncio.wait_for( 189 | asyncio.to_thread(sock.recv, 4096), 190 | timeout=SOCKET_TIMEOUT_SECONDS 191 | ) 192 | response_data = response_data.decode(errors='ignore') 193 | 194 | if response_data: 195 | first_line = response_data.split('\r\n')[0] 196 | if len(first_line.split(' ')) > 1: 197 | current_status_code = first_line.split(' ')[1] 198 | if current_status_code in ["200", "301", "302", "403"]: 199 | is_success = True 200 | else: 201 | current_status_code = "Malformed Response" 202 | else: 203 | current_status_code = "No Response" 204 | 205 | except (socket.error, socket.timeout, asyncio.TimeoutError) as e: 206 | current_status_code = f"Socket ERR ({e})" 207 | await self.log_exc_payload(hostname, payload_line, str(e)) 208 | except Exception as e: 209 | current_status_code = f"EXC ({e})" 210 | await self.log_exc_payload(hostname, payload_line, str(e)) 211 | finally: 212 | if sock: 213 | await asyncio.to_thread(sock.close) 214 | 215 | elif payload_type == "REAL_HOST": 216 | headers["Host"] = payload_value 217 | url_to_request = f"http://{hostname}" 218 | else: 219 | headers["X-Custom-Payload"] = payload_value 220 | url_to_request = f"http://{hostname}" 221 | 222 | if payload_type != "HTTP_REQUEST_LINE_INJECTION": 223 | response = await self._request_aiohttp(method, url_to_request, session, headers=headers, allow_redirects=False, verify_ssl=False, timeout=SOCKET_TIMEOUT_SECONDS) 224 | if response: 225 | current_status_code = str(response.status) 226 | if response.status in [200, 301, 302, 403]: 227 | is_success = True 228 | else: 229 | current_status_code = "ERR" 230 | 231 | except NotImplementedError as e: 232 | current_status_code = "N/A" 233 | await self.log_exc_payload(hostname, payload_line, str(e)) 234 | except Exception as e: 235 | current_status_code = "EXC" 236 | await self.log_exc_payload(hostname, payload_line, str(e)) 237 | finally: 238 | with lock: 239 | self.scanned_count += 1 240 | if is_success: 241 | log(f"{GREEN}{hostname:<25} | {current_status_code:<5}{CC}") 242 | self.found_bug_hosts.add(f"Host: {hostname:<25} | Payload: {payload_line:<50} | Status Code: {current_status_code}") 243 | else: 244 | self.failed_tests += 1 245 | 246 | async def save_bug_hosts(self): 247 | if not os.path.exists(OUTPUT_DIRECTORY): 248 | os.makedirs(OUTPUT_DIRECTORY) 249 | async with aiofiles.open(self.output_file_path, "w") as f: 250 | for entry in sorted(list(self.found_bug_hosts)): 251 | await f.write(f"{entry}\n") 252 | 253 | def _live_stats_updater(self): 254 | global total_scan_items 255 | global shutdown_event 256 | 257 | while self.scan_active and not shutdown_event.is_set(): 258 | with lock: 259 | stats_message = ( 260 | f"{Y1}Scanning Progress:{CC} {self.scanned_count}/{self.total_payload_tests} | " 261 | f"{GREEN}Bug Hosts:{len(self.found_bug_hosts)}{CC} | " 262 | f"{RED}Failed:{self.failed_tests}{CC}" 263 | ) 264 | log_replace(stats_message) 265 | time.sleep(0.5) 266 | sys.stdout.write(f"{CN}\r") 267 | sys.stdout.flush() 268 | 269 | def print_stats(self, start_time): 270 | elapsed_time = time.time() - start_time 271 | label_width = 28 272 | 273 | print(f"\n{Y1}{'=' * 40}{CC}") 274 | print(f"{Y1}{'Payload Scan Summary':^40}{CC}") 275 | print(f"{Y1}{'=' * 40}{CC}") 276 | 277 | print(f"{Y1}{'Successful Bug Hosts Found':<28}: {len(self.found_bug_hosts)}{CC}") 278 | print(f"{Y1}{'Total Payload Tests':<28}: {self.total_payload_tests}{CC}") 279 | print(f"{Y1}{'Failed Payload Tests':<28}: {self.failed_tests}{CC}") 280 | 281 | print(f"{Y1}{'Time Elapsed':<28}: {elapsed_time:.2f} seconds{CC}") 282 | print(f"{Y1}{'Saved to':<28}: {os.path.basename(self.output_file_path)}{CC}") 283 | print(f"{Y1}{'=' * 40}{CC}") 284 | 285 | async def start_scan(self, hostnames, payload_file, target_url, threads): 286 | start_time = time.time() 287 | if not os.path.exists(OUTPUT_DIRECTORY): 288 | os.makedirs(OUTPUT_DIRECTORY) 289 | 290 | host_len = 25 291 | code_len = 5 292 | log(f"{Y1}{'Host':<{host_len}} | {'Code':<{code_len}}{CC}") 293 | log(f"{Y1}{'-'*host_len} | {'-'*code_len}{CC}") 294 | 295 | await self.load_payloads(payload_file) 296 | self.total_payload_tests = len(hostnames) * len(self.payloads) 297 | global total_scan_items 298 | total_scan_items = self.total_payload_tests 299 | 300 | self.semaphore = Throttler(threads) 301 | 302 | stats_thread = threading.Thread(target=self._live_stats_updater) 303 | stats_thread.daemon = True 304 | stats_thread.start() 305 | 306 | session = None 307 | try: 308 | session = aiohttp.ClientSession(headers=self.headers, trust_env=True) 309 | async with session: 310 | scan_tasks = [] 311 | for hostname in hostnames: 312 | for payload in self.payloads: 313 | scan_tasks.append( 314 | self.test_payload_on_host(hostname, payload, target_url, session, self.semaphore) 315 | ) 316 | await asyncio.gather(*scan_tasks, return_exceptions=True) 317 | 318 | except asyncio.CancelledError: 319 | log(f"{Y1}Scan interrupted. Attempting graceful shutdown...{CC}") 320 | finally: 321 | self.scan_active = False 322 | 323 | if stats_thread.is_alive(): 324 | stats_thread.join(timeout=2) 325 | await self.save_bug_hosts() 326 | 327 | # --- START_MODIFICATION_1 --- 328 | if session and not session.closed: 329 | await session.close() 330 | # --- END_MODIFICATION_1 --- 331 | 332 | self.print_stats(start_time) 333 | 334 | async def amain(): 335 | global shutdown_event 336 | shutdown_event = asyncio.Event() 337 | 338 | clear_screen() 339 | parser = argparse.ArgumentParser( 340 | description="Payload Tester: Tests hosts with various HTTP payloads.", 341 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=52) 342 | ) 343 | parser.add_argument("-f", "--file", help=f"Input file name (default: {DEFAULT_HOSTS_FILE})", type=str, default=DEFAULT_HOSTS_FILE) 344 | parser.add_argument("-t", "--threads", help=f"Number of concurrent operations (async tasks, default: {DEFAULT_THREADS})", type=int, default=DEFAULT_THREADS) 345 | parser.add_argument("--payloads-file", help=f"File containing payload definitions (default: {DEFAULT_PAYLOAD_FILE})", type=str, default=DEFAULT_PAYLOAD_FILE) 346 | parser.add_argument("--target-url", help=f"Target URL to test connectivity through payloads (default: {DEFAULT_TARGET_URL})", type=str, default=DEFAULT_TARGET_URL) 347 | 348 | global args 349 | args = parser.parse_args() 350 | 351 | tester = PayloadTester() 352 | 353 | if not os.path.exists(OUTPUT_DIRECTORY): 354 | os.makedirs(OUTPUT_DIRECTORY) 355 | 356 | all_hosts = set() 357 | if os.path.exists(args.file): 358 | async with aiofiles.open(args.file, 'r') as f: 359 | content = await f.read() 360 | all_hosts = set(content.splitlines()) 361 | else: 362 | print(f"{RED}Input hosts file '{args.file}' does not exist. Please create it.{CC}") 363 | sys.exit(1) 364 | 365 | hostnames_to_scan = list(all_hosts) 366 | 367 | if not hostnames_to_scan: 368 | print(f"{RED}No hosts to scan. Please add hosts to your input file.{CC}") 369 | sys.exit(0) 370 | 371 | if not os.path.exists(args.payloads_file): 372 | print(f"{RED}Payload file '{args.payloads_file}' not found. Please create it.{CC}") 373 | sys.exit(1) 374 | 375 | try: 376 | await tester.start_scan( 377 | hostnames_to_scan, 378 | args.payloads_file, 379 | args.target_url, 380 | args.threads 381 | ) 382 | finally: 383 | tester.scan_active = False 384 | current_loop = asyncio.get_running_loop() 385 | pending_tasks = [task for task in asyncio.all_tasks(current_loop) 386 | if task is not asyncio.current_task()] 387 | if pending_tasks: 388 | for task in pending_tasks: 389 | task.cancel() 390 | try: 391 | await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=5) 392 | except asyncio.TimeoutError: 393 | log(f"{RED}Timeout waiting for pending tasks to finish. Some tasks may still be active.{CC}") 394 | except Exception as e: 395 | log(f"{RED}Error during final async task cleanup: {e}{CC}") 396 | 397 | if __name__ == "__main__": 398 | try: 399 | asyncio.run(amain(), debug=False) 400 | except KeyboardInterrupt: 401 | pass 402 | except Exception as e: 403 | print(f"{RED}An unexpected error occurred: {e}{CC}") 404 | finally: 405 | # --- START_MODIFICATION_2 --- 406 | try: 407 | loop = asyncio.get_event_loop() 408 | 409 | # Check if the loop is still running or active, and if so, stop it gracefully 410 | if not loop.is_closed(): 411 | # Cancel all outstanding tasks 412 | pending_tasks = [task for task in asyncio.all_tasks(loop) if task is not asyncio.current_task()] 413 | if pending_tasks: 414 | for task in pending_tasks: 415 | task.cancel() 416 | try: 417 | # Give some time for tasks to cancel 418 | loop.run_until_complete(asyncio.gather(*pending_tasks, return_exceptions=True)) 419 | except asyncio.CancelledError: 420 | pass # Expected if tasks are cancelled 421 | except Exception as e: 422 | print(f"{RED}Error during pending task cleanup: {e}{CC}") 423 | 424 | # Close the loop 425 | if not loop.is_running(): 426 | loop.close() 427 | 428 | # Shutdown the default ThreadPoolExecutor if it exists 429 | if hasattr(loop, '_default_executor') and isinstance(loop._default_executor, concurrent.futures.ThreadPoolExecutor): 430 | loop._default_executor.shutdown(wait=True, cancel_futures=True) 431 | except RuntimeError: 432 | pass # Happens if the loop was already closed/stopped by asyncio.run 433 | except Exception as e: 434 | print(f"{RED}Error during final cleanup: {e}{CC}") 435 | # --- END_MODIFICATION_2 --- 436 | --------------------------------------------------------------------------------