Initial commit: Werkzeuge-Sammlung

Enthält:
- rdp_client.py: RDP Client mit GUI und Monitor-Auswahl
- rdp.sh: Bash-basierter RDP Client
- teamleader_test/: Network Scanner Fullstack-App
- teamleader_test2/: Network Mapper CLI

Subdirectories mit eigenem Repo wurden ausgeschlossen.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
root
2026-01-28 09:39:24 +01:00
commit cb073786b3
112 changed files with 23543 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
"""Network scanner module."""
from app.scanner.network_scanner import NetworkScanner
from app.scanner.port_scanner import PortScanner
from app.scanner.service_detector import ServiceDetector
__all__ = ['NetworkScanner', 'PortScanner', 'ServiceDetector']

View File

@@ -0,0 +1,242 @@
"""Network scanner implementation for host discovery."""
import socket
import ipaddress
import asyncio
from typing import List, Set, Optional, Callable
from concurrent.futures import ThreadPoolExecutor
import logging
from app.config import settings
logger = logging.getLogger(__name__)
class NetworkScanner:
"""Scanner for discovering active hosts on a network."""
# Common ports for host discovery
DISCOVERY_PORTS = [21, 22, 23, 25, 80, 443, 445, 3389, 8080, 8443]
def __init__(
self,
timeout: int = None,
max_workers: int = None,
progress_callback: Optional[Callable[[str, float], None]] = None
):
"""
Initialize network scanner.
Args:
timeout: Socket connection timeout in seconds
max_workers: Maximum number of concurrent workers
progress_callback: Optional callback for progress updates
"""
self.timeout = timeout or settings.default_scan_timeout
self.max_workers = max_workers or settings.max_concurrent_scans
self.progress_callback = progress_callback
async def scan_network(self, network_range: str) -> List[str]:
"""
Scan a network range for active hosts.
Args:
network_range: Network in CIDR notation (e.g., '192.168.1.0/24')
Returns:
List of active IP addresses
"""
logger.info(f"Starting network scan of {network_range}")
try:
network = ipaddress.ip_network(network_range, strict=False)
# Validate private network if restriction enabled
if settings.scan_private_networks_only and not network.is_private:
raise ValueError(f"Network {network_range} is not a private network")
# Generate list of hosts to scan
hosts = [str(ip) for ip in network.hosts()]
total_hosts = len(hosts)
if total_hosts == 0:
# Single host network
hosts = [str(network.network_address)]
total_hosts = 1
logger.info(f"Scanning {total_hosts} hosts in {network_range}")
# Scan hosts concurrently
active_hosts = await self._scan_hosts_async(hosts)
logger.info(f"Scan completed. Found {len(active_hosts)} active hosts")
return active_hosts
except ValueError as e:
logger.error(f"Invalid network range: {e}")
raise
except Exception as e:
logger.error(f"Error during network scan: {e}")
raise
async def _scan_hosts_async(self, hosts: List[str]) -> List[str]:
"""
Scan multiple hosts asynchronously.
Args:
hosts: List of IP addresses to scan
Returns:
List of active hosts
"""
active_hosts: Set[str] = set()
total = len(hosts)
completed = 0
# Use ThreadPoolExecutor for socket operations
loop = asyncio.get_event_loop()
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
futures = []
for host in hosts:
future = loop.run_in_executor(executor, self._check_host, host)
futures.append((host, future))
# Process results as they complete
for host, future in futures:
try:
is_active = await future
if is_active:
active_hosts.add(host)
logger.debug(f"Host {host} is active")
except Exception as e:
logger.debug(f"Error checking host {host}: {e}")
finally:
completed += 1
if self.progress_callback:
progress = completed / total
self.progress_callback(host, progress)
return sorted(list(active_hosts), key=lambda ip: ipaddress.ip_address(ip))
def _check_host(self, ip: str) -> bool:
"""
Check if a host is active by attempting TCP connections.
Args:
ip: IP address to check
Returns:
True if host responds on any discovery port
"""
for port in self.DISCOVERY_PORTS:
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(self.timeout)
result = sock.connect_ex((ip, port))
sock.close()
if result == 0:
return True
except socket.error:
continue
except Exception as e:
logger.debug(f"Error checking {ip}:{port}: {e}")
continue
return False
def get_local_network_range(self) -> Optional[str]:
"""
Detect local network range.
Returns:
Network range in CIDR notation or None
"""
try:
import netifaces
# Get default gateway interface
gateways = netifaces.gateways()
if 'default' not in gateways or netifaces.AF_INET not in gateways['default']:
return None
default_interface = gateways['default'][netifaces.AF_INET][1]
# Get interface addresses
addrs = netifaces.ifaddresses(default_interface)
if netifaces.AF_INET not in addrs:
return None
# Get IP and netmask
inet_info = addrs[netifaces.AF_INET][0]
ip = inet_info.get('addr')
netmask = inet_info.get('netmask')
if not ip or not netmask:
return None
# Calculate network address
network = ipaddress.ip_network(f"{ip}/{netmask}", strict=False)
return str(network)
except ImportError:
logger.warning("netifaces not available, cannot detect local network")
return None
except Exception as e:
logger.error(f"Error detecting local network: {e}")
return None
def resolve_hostname(self, ip: str) -> Optional[str]:
"""
Resolve IP address to hostname.
Args:
ip: IP address
Returns:
Hostname or None
"""
try:
hostname = socket.gethostbyaddr(ip)[0]
return hostname
except socket.herror:
return None
except Exception as e:
logger.debug(f"Error resolving {ip}: {e}")
return None
def get_mac_address(self, ip: str) -> Optional[str]:
"""
Get MAC address for an IP (requires ARP access).
Args:
ip: IP address
Returns:
MAC address or None
"""
try:
# Try to get MAC from ARP cache
import subprocess
import re
# Platform-specific ARP command
import platform
if platform.system() == 'Windows':
arp_output = subprocess.check_output(['arp', '-a', ip]).decode()
mac_pattern = r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})'
else:
arp_output = subprocess.check_output(['arp', '-n', ip]).decode()
mac_pattern = r'([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}'
match = re.search(mac_pattern, arp_output)
if match:
return match.group(0).upper()
return None
except Exception as e:
logger.debug(f"Error getting MAC for {ip}: {e}")
return None

View File

@@ -0,0 +1,260 @@
"""Nmap integration for advanced scanning capabilities."""
import logging
from typing import Optional, Dict, Any, List
import asyncio
logger = logging.getLogger(__name__)
class NmapScanner:
"""Wrapper for python-nmap with safe execution."""
def __init__(self):
"""Initialize nmap scanner."""
self.nmap_available = self._check_nmap_available()
if not self.nmap_available:
logger.warning("nmap is not available on this system")
def _check_nmap_available(self) -> bool:
"""
Check if nmap is available on the system.
Returns:
True if nmap is available
"""
try:
import nmap
nm = nmap.PortScanner()
nm.nmap_version()
return True
except Exception as e:
logger.debug(f"nmap not available: {e}")
return False
async def scan_host(
self,
host: str,
arguments: str = '-sT -T4'
) -> Optional[Dict[str, Any]]:
"""
Scan a host using nmap.
Args:
host: IP address or hostname
arguments: Nmap arguments (default: TCP connect scan, aggressive timing)
Returns:
Scan results dictionary or None
"""
if not self.nmap_available:
logger.warning("Attempted to use nmap but it's not available")
return None
try:
import nmap
# Run nmap scan in thread pool
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(
None,
self._run_nmap_scan,
host,
arguments
)
return result
except Exception as e:
logger.error(f"Error running nmap scan on {host}: {e}")
return None
def _run_nmap_scan(self, host: str, arguments: str) -> Optional[Dict[str, Any]]:
"""
Run nmap scan synchronously.
Args:
host: Host to scan
arguments: Nmap arguments
Returns:
Scan results
"""
try:
import nmap
nm = nmap.PortScanner()
# Sanitize host input
if not self._validate_host(host):
logger.error(f"Invalid host: {host}")
return None
# Execute scan
logger.info(f"Running nmap scan: nmap {arguments} {host}")
nm.scan(hosts=host, arguments=arguments)
# Parse results
if host not in nm.all_hosts():
logger.debug(f"No results for {host}")
return None
host_info = nm[host]
# Extract relevant information
result = {
'hostname': host_info.hostname(),
'state': host_info.state(),
'protocols': list(host_info.all_protocols()),
'ports': []
}
# Extract port information
for proto in host_info.all_protocols():
ports = host_info[proto].keys()
for port in ports:
port_info = host_info[proto][port]
result['ports'].append({
'port': port,
'protocol': proto,
'state': port_info['state'],
'service_name': port_info.get('name'),
'service_version': port_info.get('version'),
'service_product': port_info.get('product'),
'extrainfo': port_info.get('extrainfo')
})
# OS detection if available
if 'osmatch' in host_info:
result['os_matches'] = [
{
'name': os['name'],
'accuracy': os['accuracy']
}
for os in host_info['osmatch']
]
return result
except Exception as e:
logger.error(f"Error in _run_nmap_scan for {host}: {e}")
return None
def _validate_host(self, host: str) -> bool:
"""
Validate host input to prevent command injection.
Args:
host: Host string to validate
Returns:
True if valid
"""
import ipaddress
import re
# Try as IP address
try:
ipaddress.ip_address(host)
return True
except ValueError:
pass
# Try as network range
try:
ipaddress.ip_network(host, strict=False)
return True
except ValueError:
pass
# Try as hostname (alphanumeric, dots, hyphens only)
if re.match(r'^[a-zA-Z0-9.-]+$', host):
return True
return False
def get_scan_arguments(
self,
scan_type: str,
service_detection: bool = True,
os_detection: bool = False,
port_range: Optional[str] = None
) -> str:
"""
Generate nmap arguments based on scan configuration.
Args:
scan_type: Type of scan ('quick', 'standard', 'deep')
service_detection: Enable service/version detection
os_detection: Enable OS detection (requires root)
port_range: Custom port range (e.g., '1-1000' or '80,443,8080')
Returns:
Nmap argument string
"""
args = []
# Use TCP connect scan (no root required)
args.append('-sT')
# Port specification
if port_range:
args.append(f'-p {port_range}')
elif scan_type == 'quick':
args.append('--top-ports 100')
elif scan_type == 'standard':
args.append('--top-ports 1000')
elif scan_type == 'deep':
args.append('-p-') # All ports
# Only show open ports
args.append('--open')
# Timing
if scan_type == 'quick':
args.append('-T5') # Insane
elif scan_type == 'deep':
args.append('-T3') # Normal
else:
args.append('-T4') # Aggressive
# Service detection
if service_detection:
args.append('-sV')
# OS detection (requires root)
if os_detection:
args.append('-O')
logger.warning("OS detection requires root privileges")
return ' '.join(args)
async def scan_network_with_nmap(
self,
network: str,
scan_type: str = 'quick'
) -> List[Dict[str, Any]]:
"""
Scan entire network using nmap.
Args:
network: Network in CIDR notation
scan_type: Type of scan
Returns:
List of host results
"""
if not self.nmap_available:
return []
try:
arguments = self.get_scan_arguments(scan_type)
result = await self.scan_host(network, arguments)
if result:
return [result]
return []
except Exception as e:
logger.error(f"Error scanning network {network}: {e}")
return []

View File

@@ -0,0 +1,213 @@
"""Port scanner implementation."""
import socket
import asyncio
from typing import List, Dict, Set, Optional, Callable
from concurrent.futures import ThreadPoolExecutor
import logging
from app.config import settings
logger = logging.getLogger(__name__)
class PortScanner:
"""Scanner for detecting open ports on hosts."""
# Predefined port ranges for different scan types
PORT_RANGES = {
'quick': [21, 22, 23, 25, 53, 80, 110, 143, 443, 445, 3306, 3389, 5432, 8080, 8443],
'standard': list(range(1, 1001)),
'deep': list(range(1, 65536)),
}
def __init__(
self,
timeout: int = None,
max_workers: int = None,
progress_callback: Optional[Callable[[str, int, float], None]] = None
):
"""
Initialize port scanner.
Args:
timeout: Socket connection timeout in seconds
max_workers: Maximum number of concurrent workers
progress_callback: Optional callback for progress updates (host, port, progress)
"""
self.timeout = timeout or settings.default_scan_timeout
self.max_workers = max_workers or settings.max_concurrent_scans
self.progress_callback = progress_callback
async def scan_host_ports(
self,
host: str,
scan_type: str = 'quick',
custom_ports: Optional[List[int]] = None
) -> List[Dict[str, any]]:
"""
Scan ports on a single host.
Args:
host: IP address or hostname
scan_type: Type of scan ('quick', 'standard', 'deep', or 'custom')
custom_ports: Custom port list (required if scan_type is 'custom')
Returns:
List of dictionaries with port information
"""
logger.info(f"Starting port scan on {host} (type: {scan_type})")
# Determine ports to scan
if scan_type == 'custom' and custom_ports:
ports = custom_ports
elif scan_type in self.PORT_RANGES:
ports = self.PORT_RANGES[scan_type]
else:
ports = self.PORT_RANGES['quick']
# Scan ports
open_ports = await self._scan_ports_async(host, ports)
logger.info(f"Scan completed on {host}. Found {len(open_ports)} open ports")
return open_ports
async def _scan_ports_async(self, host: str, ports: List[int]) -> List[Dict[str, any]]:
"""
Scan multiple ports asynchronously.
Args:
host: Host to scan
ports: List of ports to scan
Returns:
List of open port information
"""
open_ports = []
total = len(ports)
completed = 0
loop = asyncio.get_event_loop()
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
futures = []
for port in ports:
future = loop.run_in_executor(executor, self._check_port, host, port)
futures.append((port, future))
# Process results
for port, future in futures:
try:
result = await future
if result:
open_ports.append(result)
logger.debug(f"Found open port {port} on {host}")
except Exception as e:
logger.debug(f"Error checking port {port} on {host}: {e}")
finally:
completed += 1
if self.progress_callback:
progress = completed / total
self.progress_callback(host, port, progress)
return sorted(open_ports, key=lambda x: x['port'])
def _check_port(self, host: str, port: int) -> Optional[Dict[str, any]]:
"""
Check if a port is open on a host.
Args:
host: Host to check
port: Port number
Returns:
Dictionary with port info if open, None otherwise
"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(self.timeout)
result = sock.connect_ex((host, port))
sock.close()
if result == 0:
return {
'port': port,
'protocol': 'tcp',
'state': 'open',
'service_name': self._guess_service_name(port)
}
return None
except socket.error as e:
logger.debug(f"Socket error checking {host}:{port}: {e}")
return None
except Exception as e:
logger.debug(f"Error checking {host}:{port}: {e}")
return None
def _guess_service_name(self, port: int) -> Optional[str]:
"""
Guess service name based on well-known ports.
Args:
port: Port number
Returns:
Service name or None
"""
common_services = {
20: 'ftp-data',
21: 'ftp',
22: 'ssh',
23: 'telnet',
25: 'smtp',
53: 'dns',
80: 'http',
110: 'pop3',
143: 'imap',
443: 'https',
445: 'smb',
3306: 'mysql',
3389: 'rdp',
5432: 'postgresql',
5900: 'vnc',
8080: 'http-alt',
8443: 'https-alt',
}
return common_services.get(port)
def parse_port_range(self, port_range: str) -> List[int]:
"""
Parse port range string to list of ports.
Args:
port_range: String like "80,443,8000-8100"
Returns:
List of port numbers
"""
ports = set()
try:
for part in port_range.split(','):
part = part.strip()
if '-' in part:
# Range like "8000-8100"
start, end = map(int, part.split('-'))
if 1 <= start <= end <= 65535:
ports.update(range(start, end + 1))
else:
# Single port
port = int(part)
if 1 <= port <= 65535:
ports.add(port)
return sorted(list(ports))
except ValueError as e:
logger.error(f"Error parsing port range '{port_range}': {e}")
return []

View File

@@ -0,0 +1,250 @@
"""Service detection and banner grabbing implementation."""
import socket
import logging
from typing import Optional, Dict, Any
logger = logging.getLogger(__name__)
class ServiceDetector:
"""Detector for identifying services running on open ports."""
def __init__(self, timeout: int = 3):
"""
Initialize service detector.
Args:
timeout: Socket timeout in seconds
"""
self.timeout = timeout
def detect_service(self, host: str, port: int) -> Dict[str, Any]:
"""
Detect service on a specific port.
Args:
host: Host IP or hostname
port: Port number
Returns:
Dictionary with service information
"""
service_info = {
'port': port,
'protocol': 'tcp',
'service_name': None,
'service_version': None,
'banner': None
}
# Try banner grabbing
banner = self.grab_banner(host, port)
if banner:
service_info['banner'] = banner
# Try to identify service from banner
service_name, version = self._identify_from_banner(banner, port)
if service_name:
service_info['service_name'] = service_name
if version:
service_info['service_version'] = version
# If no banner, use port-based guess
if not service_info['service_name']:
service_info['service_name'] = self._guess_service_from_port(port)
return service_info
def grab_banner(self, host: str, port: int) -> Optional[str]:
"""
Attempt to grab service banner.
Args:
host: Host IP or hostname
port: Port number
Returns:
Banner string or None
"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(self.timeout)
sock.connect((host, port))
# Try to receive banner
try:
banner = sock.recv(1024)
banner_str = banner.decode('utf-8', errors='ignore').strip()
sock.close()
if banner_str:
logger.debug(f"Got banner from {host}:{port}: {banner_str[:100]}")
return banner_str
except socket.timeout:
# Try sending a probe for services that need it
banner_str = self._probe_service(sock, port)
sock.close()
return banner_str
except Exception as e:
logger.debug(f"Error grabbing banner from {host}:{port}: {e}")
return None
def _probe_service(self, sock: socket.socket, port: int) -> Optional[str]:
"""
Send service-specific probe to elicit response.
Args:
sock: Connected socket
port: Port number
Returns:
Response string or None
"""
probes = {
80: b"GET / HTTP/1.0\r\n\r\n",
443: b"GET / HTTP/1.0\r\n\r\n",
8080: b"GET / HTTP/1.0\r\n\r\n",
8443: b"GET / HTTP/1.0\r\n\r\n",
25: b"EHLO test\r\n",
110: b"USER test\r\n",
143: b"A001 CAPABILITY\r\n",
}
probe = probes.get(port, b"\r\n")
try:
sock.send(probe)
response = sock.recv(1024)
return response.decode('utf-8', errors='ignore').strip()
except:
return None
def _identify_from_banner(self, banner: str, port: int) -> tuple[Optional[str], Optional[str]]:
"""
Identify service and version from banner.
Args:
banner: Banner string
port: Port number
Returns:
Tuple of (service_name, version)
"""
banner_lower = banner.lower()
# HTTP servers
if 'http' in banner_lower or port in [80, 443, 8080, 8443]:
if 'apache' in banner_lower:
return self._extract_apache_version(banner)
elif 'nginx' in banner_lower:
return self._extract_nginx_version(banner)
elif 'iis' in banner_lower or 'microsoft' in banner_lower:
return 'IIS', None
else:
return 'HTTP', None
# SSH
if 'ssh' in banner_lower or port == 22:
if 'openssh' in banner_lower:
return self._extract_openssh_version(banner)
return 'SSH', None
# FTP
if 'ftp' in banner_lower or port in [20, 21]:
if 'filezilla' in banner_lower:
return 'FileZilla FTP', None
elif 'proftpd' in banner_lower:
return 'ProFTPD', None
return 'FTP', None
# SMTP
if 'smtp' in banner_lower or 'mail' in banner_lower or port == 25:
if 'postfix' in banner_lower:
return 'Postfix', None
elif 'exim' in banner_lower:
return 'Exim', None
return 'SMTP', None
# MySQL
if 'mysql' in banner_lower or port == 3306:
return 'MySQL', None
# PostgreSQL
if 'postgresql' in banner_lower or port == 5432:
return 'PostgreSQL', None
# Generic identification
if port == 22:
return 'SSH', None
elif port in [80, 8080]:
return 'HTTP', None
elif port in [443, 8443]:
return 'HTTPS', None
return None, None
def _extract_apache_version(self, banner: str) -> tuple[str, Optional[str]]:
"""Extract Apache version from banner."""
import re
match = re.search(r'Apache/?([\d.]+)?', banner, re.IGNORECASE)
if match:
version = match.group(1)
return 'Apache', version
return 'Apache', None
def _extract_nginx_version(self, banner: str) -> tuple[str, Optional[str]]:
"""Extract nginx version from banner."""
import re
match = re.search(r'nginx/?([\d.]+)?', banner, re.IGNORECASE)
if match:
version = match.group(1)
return 'nginx', version
return 'nginx', None
def _extract_openssh_version(self, banner: str) -> tuple[str, Optional[str]]:
"""Extract OpenSSH version from banner."""
import re
match = re.search(r'OpenSSH[_/]?([\d.]+\w*)?', banner, re.IGNORECASE)
if match:
version = match.group(1)
return 'OpenSSH', version
return 'OpenSSH', None
def _guess_service_from_port(self, port: int) -> Optional[str]:
"""
Guess service name from well-known port number.
Args:
port: Port number
Returns:
Service name or None
"""
common_services = {
20: 'ftp-data',
21: 'ftp',
22: 'ssh',
23: 'telnet',
25: 'smtp',
53: 'dns',
80: 'http',
110: 'pop3',
143: 'imap',
443: 'https',
445: 'smb',
993: 'imaps',
995: 'pop3s',
3306: 'mysql',
3389: 'rdp',
5432: 'postgresql',
5900: 'vnc',
6379: 'redis',
8080: 'http-alt',
8443: 'https-alt',
27017: 'mongodb',
}
return common_services.get(port)