pax_global_header00006660000000000000000000000064145053364450014523gustar00rootroot0000000000000052 comment=35e8085ccfeb9899aab4a74efd75c06e702ce9e0 raven-1.0.1/000077500000000000000000000000001450533644500126355ustar00rootroot00000000000000raven-1.0.1/LICENSE000066400000000000000000000020511450533644500136400ustar00rootroot00000000000000MIT License Copyright (c) 2023 Tristram Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. raven-1.0.1/README.md000066400000000000000000000121421450533644500141140ustar00rootroot00000000000000# Raven Raven is a Python tool that extends the capabilities of the `http.server` Python module by offering a self-contained file upload web server. While the common practice is to use `python3 -m http.server 80` to serve files for remote client downloads, Raven addresses the need for a similar solution when you need the ability to receive files from remote clients. This becomes especially valuable in scenarios such as penetration testing and incident response procedures when protocols such as SMB may not be a viable option. ### Key Features While the majority of the hard work is already being handled by the http.server module, it presents us with an opportunity to implement additional security and ease of use features without overcomplicating the overall implementation. These features currently include: - **IP Access Restrictions**: Optionally grants the ability to restrict access based on client IP addresses. You can define this access via a single IP, a comma-delimited list or by using CIDR notation. - **Organized Uploads**: Optionally organizes uploaded files into subfolders based on the remote client's IP address in a named or current working directory. Otherwise the default behavior will upload files in the current working directory. - **File Sanitation**: Sanitizes the name of each uploaded file prior to being saved to disk to help prevent potential abuse. - **Clobbering**: Verifies that the file does not already exist before it's written to disk. If it already exists, an incrementing number is appended to the filename to prevent clashes and ensure no data is overwritten. - **Detailed Logging**: Raven provides detailed logging of file uploads and interaction with the http server, including the status codes sent back to a client, its IP address, timestamp, and the saved file's location in the event a file is uploaded. ## Usage Raven is straightforward to use and includes simple command-line arguments to manage the included feature sets: ```bash python3 raven.py [--allowed-ip ] [--upload-folder ] [--organize-uploads] ``` * : The IP address for our http handler to listen on * : The port for our http handler to listen on * --allowed-ip :Restrict access to our http handler by IP address (optional) * --upload-folder : "Designate the directory to save uploaded files to (default: current working directory) * --organize-uploads: Organize file uploads into subfolders by remote client IP ## Installation Install from GitHub 1. Clone the Repository ```bash git clone https://github.com/gh0x0st/raven.git cd raven ``` 2. Install using pip3 ```bash pip3 install . ``` 3. Add /home/USER/./local/bin to your PATH environment variable ```bash echo 'export PATH="/home/kali/.local/bin:$PATH"' >> ~/.zshrc source ~/.zshrc ``` ## Examples Start the HTTP server on all available network interfaces, listening on port 443: `raven 0.0.0.0 443` Start the HTTP server on all on a specific interface (192.168.0.12), listening on port 443 and restrict access to 192.168.0.4: `raven 192.168.0.12 443 --allowed-ip 192.168.0.4` Start the HTTP server on all on a specific interface (192.168.0.12), listening on port 443, restrict access to 192.168.0.4 and save uploaded files to /tmp: `raven 192.168.0.12 443 --allowed-ip 192.168.0.4 --upload-folder /tmp` Start the HTTP server on all on a specific interface (192.168.0.12), listening on port 443, restrict access to 192.168.0.4 and save uploaded files to /tmp organized by remote client ip: `raven 192.168.0.12 443 --allowed-ip 192.168.0.4 --upload-folder /tmp --organize-uploads` ## Scripted Uploads Uploading files using PowerShell: ```powershell # Listener $Uri = "http://192.168.0.12:443/" # Target File $File = Get-Item "C:\Path\To\File" $Content = [System.IO.File]::ReadAllBytes($File.FullName) $Boundary = [System.Guid]::NewGuid().ToString() # Request Headers $Headers = @{ "Content-Type" = "multipart/form-data; boundary=$Boundary" } # Request Body $Body = @" --$Boundary Content-Disposition: form-data; name="file"; filename="$($File.Name)" Content-Type: application/octet-stream $Content --$Boundary-- "@ # Upload File Invoke-WebRequest -UseBasicParsing -Uri $Uri -Method "POST" -Headers $Headers -Body $Body ``` Uploading files using Python3: ```python #!/usr/bin/env python3 import requests import uuid # Listener url = "http://192.168.0.12:443/" # Target File file_path = "/path/to/file" file_name = file_path.split("/")[-1] with open(file_path, "rb") as file: file_content = file.read() boundary = str(uuid.uuid4()) # Request Headers headers = { "Content-Type": f"multipart/form-data; boundary={boundary}" } # Request Body body = ( f"--{boundary}\r\n" f'Content-Disposition: form-data; name="file"; filename="{file_name}"\r\n' "Content-Type: application/octet-stream\r\n\r\n" f"{file_content.decode('ISO-8859-1')}\r\n" f"--{boundary}--\r\n" ) # Upload File requests.post(url, headers=headers, data=body) ``` ## License This project is licensed under the MIT License - see the LICENSE file for details. raven-1.0.1/raven.py000066400000000000000000000001371450533644500143230ustar00rootroot00000000000000#!/usr/bin/env python3 from raven.__main__ import main if __name__ == '__main__': main() raven-1.0.1/raven/000077500000000000000000000000001450533644500137505ustar00rootroot00000000000000raven-1.0.1/raven/__init__.py000066400000000000000000000000011450533644500160500ustar00rootroot00000000000000 raven-1.0.1/raven/__main__.py000066400000000000000000000237551450533644500160560ustar00rootroot00000000000000#!/usr/bin/env python3 import argparse import errno import os import re import sys import http.server import socketserver from datetime import datetime from ipaddress import ip_network, ip_address # Instantiate our FileUploadHandler class class FileUploadHandler(http.server.SimpleHTTPRequestHandler): def __init__(self, *args, **kwargs): self.upload_folder = kwargs.pop('upload_folder', None) self.allowed_ip = kwargs.pop('allowed_ip', None) self.organize_uploads = kwargs.pop('organize_uploads', False) super().__init__(*args, **kwargs) # Define our handler method for restricting access by client ip def restrict_access(self): if not self.allowed_ip: # Access is permitted by default return True # Obtain the client ip client_ip = ip_address(self.client_address[0]) # Cycle through each entry in allowed_ips for permitted access allowed_ips = self.allowed_ip.split(',') for ip in allowed_ips: ip = ip.strip() # Check if the entry is in CIDR notation if '/' in ip: try: network = ip_network(ip, strict=False) if client_ip in network: return True except ValueError: pass elif client_ip == ip_address(ip): return True # The client ip is not permitted access to the handler # Respond back to the client with a 403 status code self.send_response(403) self.end_headers() return False # Define our GET handler method def do_GET(self): if self.path == '/': # Check if we are restricting access if not self.restrict_access(): return # Respond back to the client with a 200 status code self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() # Send an HTML response to the client with the upload form self.wfile.write(b""" Raven File Upload
""") # Define our POST handler method def do_POST(self): if self.path == '/': # Check if we are restricting access if not self.restrict_access(): return # Inspect incoming multipart/form-data content content_type = self.headers['Content-Type'] if content_type.startswith('multipart/form-data'): try: # Extract and parse multipart/form-data content content_length = int(self.headers['Content-Length']) form_data = self.rfile.read(content_length) # Extract the boundary from the content type header boundary = content_type.split('; ')[1].split('=')[1] # Split the form data using the boundary parts = form_data.split(b'--' + boundary.encode()) for part in parts: if b'filename="' in part: # Extract the filename from Content-Disposition header headers, data = part.split(b'\r\n\r\n', 1) content_disposition = headers.decode() filename = re.search(r'filename="(.+)"', content_disposition).group(1) # Sanitize the filename based on our requirements filename = sanitize_filename(filename) # Organize uploads into subfolders by client IP otherwise use the default if self.organize_uploads and self.client_address: client_ip = self.client_address[0] upload_folder = os.path.join(self.upload_folder, client_ip) os.makedirs(upload_folder, exist_ok=True) file_path = os.path.join(upload_folder, filename) else: upload_folder = self.upload_folder file_path = os.path.join(upload_folder, filename) # Generate a unique filename in case the file already exists file_path = prevent_clobber(upload_folder, filename) # Save the uploaded file in binary mode so we don't corrupt any content with open(file_path, 'wb') as f: f.write(data) # Respond back to the client with a 200 status code self.send_response(200) self.end_headers() # Send an HTML response to the client for redirection self.wfile.write(b"""

File uploaded successfully. Redirecting in 3 seconds...

""") # Print the path where the uploaded file was saved to the terminal now = datetime.now().strftime("%d/%b/%Y %H:%M:%S") print(f"{self.client_address[0]} - - [{now}] \"File saved {file_path}\"") return except Exception as e: print(f"Error processing the uploaded file: {str(e)}") # Something bad happened if we get to this point # Error details are provided by http.server on the terminal # Respond back to the client with a 400 status code self.send_response(400) self.end_headers() # Normalizes the filename, then remove any characters that are not letters, numbers, underscores, dots, or hyphens def sanitize_filename(filename): normalized = os.path.normpath(filename) sanitized = re.sub(r'[^\w.-]', '_', normalized) return sanitized # Appends a file name with an incrementing number if it happens to exist already def prevent_clobber(upload_folder, filename): file_path = os.path.join(upload_folder, filename) counter = 1 while os.path.exists(file_path): base_name, file_extension = os.path.splitext(filename) new_filename = f"{base_name}_{counter}{file_extension}" file_path = os.path.join(upload_folder, new_filename) counter += 1 return file_path def main(): # Build the parser parser = argparse.ArgumentParser( description="A lightweight file upload service used for penetration testing and incident response.", usage="python3 raven.py [--allowed-ip ] [--upload-folder ] [--organize-uploads]" ) # Configure our arguments parser.add_argument("host", help="The IP address for our http handler to listen on") parser.add_argument("port", type=int, help="The port for our http handler to listen on") parser.add_argument("--allowed-ip", help="Restrict access to our http handler by IP address (optional)") parser.add_argument("--upload-folder", default=os.getcwd(), help="Designate the directory to save uploaded files to (default: current working directory)") parser.add_argument("--organize-uploads", action="store_true", help="Organize file uploads into subfolders by remote client IP") # Parse the command-line arguments args = parser.parse_args() # Check if no arguments were provided if len(sys.argv) == 1: parser.print_help() sys.exit(1) # Initializing configuration variables host = args.host port = args.port allowed_ip = args.allowed_ip upload_folder = args.upload_folder organize_uploads = args.organize_uploads server = None try: # Check if the specified upload folder exists, if not try to create it if not os.path.exists(upload_folder): os.makedirs(upload_folder) # Create an HTTP server instance with our custom request handling server = socketserver.TCPServer((host, port), lambda *args, **kwargs: FileUploadHandler(*args, **kwargs, upload_folder=upload_folder, allowed_ip=allowed_ip, organize_uploads=organize_uploads)) # Print our handler details to the terminal print(f"[*] Serving HTTP on {host} port {port} (http://{host}:{port}/)") # Print additional details to the terminal if allowed_ip: print(f"[*] Listener access is restricted to {allowed_ip}") else: print(f"[*] Listener access is unrestricted") if organize_uploads: print(f"[*] Uploads will be organized by client IP in {upload_folder}") else: print(f"[*] Uploads will be saved in {upload_folder}") # Start the HTTP server and keep it running until we stop it server.serve_forever() except KeyboardInterrupt: print("\nKeyboard interrupt received, exiting.") except OSError as ose: if ose.errno == errno.EADDRNOTAVAIL: print(f"[!] The IP address '{host}' does not appear to be available on this system") else: print(f"[!] {str(ose)}") except Exception as ex: print(f"[!] {str(ex)}") finally: if server: server.server_close() if __name__ == '__main__': main() raven-1.0.1/setup.py000066400000000000000000000026641450533644500143570ustar00rootroot00000000000000#!/usr/bin/env python3 from setuptools import setup, find_packages setup( name='raven', version='1.0.0', url='https://github.com/gh0x0st/raven', author='Tristram', author_email="discord:blueteamer", description='A lightweight file upload service used for penetration testing and incident response.', long_description=open('README.md').read(), long_description_content_type='text/markdown', license="MIT", keywords=['file upload', 'penetration testing', 'HTTP server', 'SimpleHTTPServer'], packages=find_packages(), install_requires=[], entry_points={ 'console_scripts': ['raven = raven.__main__:main'], }, classifiers=[ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Operating System :: OS Independent", "Intended Audience :: System Administrators", "Intended Audience :: Security", "Topic :: Utilities", "Topic :: Security", "Topic :: Security :: Penetration Testing", "Topic :: System :: Networking", "Topic :: System :: Systems Administration", ], python_requires='>=3.6', )