added FTP server
This commit is contained in:
parent
90ccb07e94
commit
f080276339
@ -57,6 +57,7 @@ Can be deployed on victim machines to scan the intranet.
|
||||
- dnsserver.py: Create a temporary dns server responding dynamically to basic DNS requests (in-memory)
|
||||
- sshserver.py: Create a temporary ssh server to intercept credentials (TODO: relay) (in-memory)
|
||||
- smtpserver.py: Create a temporary smtp server (in-memory)
|
||||
- ftpserver.py: Create a temporary ftp server (in-memory, thanks to @thanks to [@benzammour](https://github.com/benzammour))
|
||||
- template.py: Creates a template for web exploits, similar to pwnlib's template
|
||||
- pcap_file_extract.py: Lists and extracts files from http connections found in pcap files
|
||||
- find_git_commit.py: Compares a local repository (e.g. downloaded from a remote server) with another git repository to guess the commit hash. Useful to find used versions
|
||||
|
@ -285,8 +285,8 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Spawn a temporary http server")
|
||||
parser.add_argument(
|
||||
"action",
|
||||
choices=["shell", "dump", "proxy", "xss"],
|
||||
help="Choose one of these actions: shell, dump, proxy, xss"
|
||||
choices=["shell", "dump", "proxy", "xss", "start"],
|
||||
help="Choose one of these actions: shell, dump, proxy, xss, start"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@ -297,7 +297,6 @@ if __name__ == "__main__":
|
||||
help="Address to bind on (default: 0.0.0.0)"
|
||||
)
|
||||
|
||||
# Optionales Argument: port
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
type=int,
|
||||
@ -337,5 +336,9 @@ if __name__ == "__main__":
|
||||
file_server.dumpRequest("/exfiltrate")
|
||||
print("Exfiltrate data using:")
|
||||
print(xss)
|
||||
elif args.action == "start":
|
||||
file_server.load_directory(".")
|
||||
print("Serve files in current directory using:")
|
||||
print(file_server.get_full_url("/", ip_addr=ip_address))
|
||||
|
||||
file_server.serve_forever()
|
||||
|
181
ftpserver.py
Normal file
181
ftpserver.py
Normal file
@ -0,0 +1,181 @@
|
||||
import os
|
||||
import logging
|
||||
import argparse
|
||||
import signal
|
||||
from pyftpdlib.handlers import FTPHandler
|
||||
from pyftpdlib.servers import ThreadedFTPServer
|
||||
from pyftpdlib.authorizers import DummyAuthorizer
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setLevel(logging.INFO)
|
||||
|
||||
formatter = logging.Formatter("%(asctime)s - %(message)s")
|
||||
stream_handler.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
MSG_LOGIN = "Login successful"
|
||||
MSG_CLOSE = "Goodbye"
|
||||
|
||||
|
||||
# Class to log every action the user takes
|
||||
class CustomFTPHandler(FTPHandler):
|
||||
def on_login(self, username):
|
||||
logging.info(f"User '{username}' logged in successfully.")
|
||||
|
||||
def on_login_failed(self, username, password):
|
||||
logging.warning(
|
||||
f"Failed login attempt for user '{username}' with password '{password}'."
|
||||
)
|
||||
|
||||
def on_file_received(self, file):
|
||||
logging.info(f"File received: {file}")
|
||||
|
||||
def on_file_sent(self, file):
|
||||
logging.info(f"File sent: {file}")
|
||||
|
||||
def on_file_deleted(self, file):
|
||||
logging.info(f"File deleted: {file}")
|
||||
|
||||
def on_file_renamed(self, old_file, new_file):
|
||||
logging.info(f"File renamed from '{old_file}' to '{new_file}'")
|
||||
|
||||
def on_file_downloaded(self, file):
|
||||
logging.info(f"File downloaded: {file}")
|
||||
|
||||
def on_file_stored(self, file):
|
||||
logging.info(f"File stored: {file}")
|
||||
|
||||
def on_file_retrieved(self, file):
|
||||
logging.info(f"File retrieved: {file}")
|
||||
|
||||
def on_file_aborted(self, file):
|
||||
logging.info(f"File transfer aborted: {file}")
|
||||
|
||||
def on_file_changed(self, file):
|
||||
logging.info(f"File changed: {file}")
|
||||
|
||||
def on_file_moved(self, old_file, new_file):
|
||||
logging.info(f"File moved from '{old_file}' to '{new_file}'")
|
||||
|
||||
def on_file_uploaded(self, file):
|
||||
logging.info(f"File uploaded: {file}")
|
||||
|
||||
def on_connect(self):
|
||||
logger.info(f"Client connected: {self.remote_ip}")
|
||||
|
||||
def on_disconnect(self):
|
||||
logger.info(f"Client disconnected: {self.remote_ip}")
|
||||
|
||||
def on_logout(self, username):
|
||||
logger.info(f"User logged out: {username}")
|
||||
|
||||
def on_incomplete_file_received(self, file):
|
||||
logger.warning(f"Incomplete file received: {file}")
|
||||
|
||||
def on_incomplete_file_sent(self, file):
|
||||
logger.warning(f"Incomplete file received: {file}")
|
||||
|
||||
|
||||
class AnyUserAuthorizer(DummyAuthorizer):
|
||||
"""
|
||||
Authorization class that allows any combination of username/password.
|
||||
"""
|
||||
|
||||
def __init__(self, directory):
|
||||
DummyAuthorizer.__init__(self)
|
||||
self.directory = directory
|
||||
self.default_params = {
|
||||
"pwd": "",
|
||||
"home": self.directory,
|
||||
"perm": "elr",
|
||||
"operms": {},
|
||||
"msg_login": MSG_LOGIN,
|
||||
"msg_quit": MSG_CLOSE,
|
||||
}
|
||||
|
||||
def validate_authentication(self, username, password, handler):
|
||||
logger.info(f"User '{username}' tried logging in with password: '{password}'")
|
||||
|
||||
return True
|
||||
|
||||
def get_home_dir(self, username):
|
||||
return self.directory
|
||||
|
||||
def has_user(self, username):
|
||||
if username in self.user_table:
|
||||
return True
|
||||
|
||||
self.add_user(username, "", self.directory)
|
||||
return True
|
||||
|
||||
def has_perm(self, username, perm, path=None) -> bool:
|
||||
if username not in self.user_table:
|
||||
# add user manually and not via add_user due to infinite recursion
|
||||
self.user_table[username] = self.default_params
|
||||
|
||||
return True
|
||||
|
||||
def get_msg_login(self, username: str) -> str:
|
||||
return MSG_LOGIN
|
||||
|
||||
def get_msg_quit(self, username: str) -> str:
|
||||
return MSG_CLOSE
|
||||
|
||||
|
||||
class FastFTPServer:
|
||||
def __init__(self, directory, port):
|
||||
self.directory = directory
|
||||
self.port = port
|
||||
self.server = None
|
||||
|
||||
def start(self):
|
||||
authorizer = AnyUserAuthorizer(directory=self.directory)
|
||||
|
||||
handler = CustomFTPHandler
|
||||
handler.authorizer = authorizer
|
||||
|
||||
self.server = ThreadedFTPServer(("", self.port), handler)
|
||||
logging.info(f"Starting FTP server on port {self.port}")
|
||||
|
||||
self.server.serve_forever()
|
||||
|
||||
def cleanup(self):
|
||||
logging.info("Shutting down FTP server...")
|
||||
if self.server:
|
||||
self.server.close_all()
|
||||
logging.info("FTP server shut down.")
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
logging.info("Received Ctrl+C, shutting down...")
|
||||
ftp_server.cleanup()
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Temporary FTP Server")
|
||||
parser.add_argument(
|
||||
"--dir", "-d", type=str, default=".", help="Directory to serve files from"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port", "-p", type=int, default=21, help="Port to run the FTP server on"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists(args.dir):
|
||||
print(f"Error: The directory '{args.dir}' does not exist.")
|
||||
return
|
||||
|
||||
global ftp_server
|
||||
ftp_server = FastFTPServer(args.dir, args.port)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
ftp_server.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user