import asyncio import aiohttp import random import os import sys from colorama import Fore, Style, init if sys.platform.startswith("win"): asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) init(autoreset=True) # ================== WARNA & UI ================== gr, red, yl, bl, res = Fore.GREEN, Fore.RED, Fore.YELLOW, Fore.BLUE, Style.RESET_ALL # ================== CONFIGURATION ================== START_CONCURRENCY = 300 MIN_CONCURRENCY = 50 TIMEOUT_SECONDS = 10 BATCH_SIZE = 500 REST_TIME = 2 MAX_RETRY = 3 USER_AGENTS = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36" ] # ================== GLOBAL STATS ================== stats = {"checked": 0, "found": 0, "error": 0} STOP_SCAN = False CURRENT_CONCURRENCY = START_CONCURRENCY # ================== CORE (NO PROXY) ================== async def safe_fetch(session, sem, url): global STOP_SCAN if STOP_SCAN: return async with sem: headers = {"User-Agent": random.choice(USER_AGENTS)} try: async with session.get( url, headers=headers, ssl=False, timeout=TIMEOUT_SECONDS, allow_redirects=True ) as response: stats["checked"] += 1 if response.status == 200: content = await response.text() if any(x in content for x in ["save_before_upload", "uploadOnSave", "\"protocol\": \"sftp\""]): stats["found"] += 1 print(f"{gr}[MATCH]{res} {url}") with open("sftpfound.txt", "a") as f: f.write(f"{url}\n") except (aiohttp.ClientError, asyncio.TimeoutError): stats["error"] += 1 except OSError: stats["error"] += 1 except Exception: stats["error"] += 1 async def process_batch(session, domains, batch_idx, total_batches, sem): tasks = [] for dom in domains: base = dom if dom.startswith("http") else f"http://{dom}" base = base.rstrip('/') tasks.append(safe_fetch(session, sem, f"{base}/sftp-config.json")) tasks.append(safe_fetch(session, sem, f"{base}/.vscode/sftp.json")) await asyncio.gather(*tasks) print(f"{bl}[Batch {batch_idx}/{total_batches}]{res} Done. Found: {gr}{stats['found']}{res} | Total Checked: {stats['checked']}") # ================== MAIN EXECUTION ================== async def main(): global STOP_SCAN os.system('cls' if os.name == 'nt' else 'clear') # 1. Load Data print(f"{yl}Masukkan nama file list (pisahkan dengan koma): {res}") input_files = input(f"{bl}Input your files: {res}") file_list = [f.strip() for f in input_files.split(",")] domains = [] for file_path in file_list: try: with open(file_path, "r", encoding="utf-8", errors="ignore") as f: lines = [line.strip() for line in f if line.strip()] domains.extend(lines) print(f"{gr}[LOADED]{res} {len(lines)} domain dari {file_path}") except FileNotFoundError: print(f"{red}[ERROR]{res} File {file_path} tidak ditemukan!") except Exception as e: print(f"{red}[ERROR]{res} Gagal membaca {file_path}: {e}") if not domains: print(f"{red}Tidak ada domain yang berhasil dimuat.{res}") return total_domains = len(domains) total_batches = (total_domains + BATCH_SIZE - 1) // BATCH_SIZE print(f"{gr}Total Gabungan:{res} {total_domains} domain | {total_batches} batch\n") global_sem = asyncio.Semaphore(START_CONCURRENCY) connector = aiohttp.TCPConnector( limit=0, limit_per_host=5, force_close=False, ttl_dns_cache=300 ) async with aiohttp.ClientSession(connector=connector, trust_env=False) as session: for i in range(0, total_domains, BATCH_SIZE): if STOP_SCAN: break batch_idx = (i // BATCH_SIZE) + 1 batch_domains = domains[i:i + BATCH_SIZE] await process_batch(session, batch_domains, batch_idx, total_batches, global_sem) if i + BATCH_SIZE < total_domains: await asyncio.sleep(REST_TIME) print(f"\n{gr}PROSES SELESAI.{res}") print(f"Total Found: {stats['found']} | Checked: {stats['checked']}") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: print(f"\n{red}Dihentikan paksa oleh user.{res}")