Initial commit
This commit is contained in:
174
scripts/winrm/benchmark_ssh_vs_winrm.py
Normal file
174
scripts/winrm/benchmark_ssh_vs_winrm.py
Normal file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Benchmark SSH vs WinRM performance
|
||||
Compares execution times and capabilities
|
||||
"""
|
||||
|
||||
import time
|
||||
import subprocess
|
||||
import os
|
||||
from winrm import Session
|
||||
|
||||
|
||||
def benchmark_ssh_method():
|
||||
"""Benchmark SSH-based script execution"""
|
||||
print("=== SSH Method Benchmark ===")
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
# Run a simple recon via SSH
|
||||
result = subprocess.run(
|
||||
['timeout', '30', './recon.sh'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=35
|
||||
)
|
||||
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f"✅ SSH method successful")
|
||||
print(f"⏱️ Execution time: {duration:.2f} seconds")
|
||||
return duration, True
|
||||
else:
|
||||
print(f"❌ SSH method failed: {result.stderr}")
|
||||
return duration, False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"❌ SSH method timed out after 35 seconds")
|
||||
return 35, False
|
||||
except Exception as e:
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
print(f"❌ SSH method error: {e}")
|
||||
return duration, False
|
||||
|
||||
|
||||
def benchmark_winrm_method():
|
||||
"""Benchmark WinRM-based script execution"""
|
||||
print("\n=== WinRM Method Benchmark ===")
|
||||
|
||||
host = os.getenv('HOST', '192.168.88.108')
|
||||
username = os.getenv('USER_NAME', 'Admin')
|
||||
password = os.getenv('PASS', 'P@ssw0rd!')
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
# Connect to WinRM
|
||||
session = Session(
|
||||
f'http://{host}:5985/wsman',
|
||||
auth=(username, password),
|
||||
transport='ntlm'
|
||||
)
|
||||
|
||||
# Run equivalent recon commands via WinRM
|
||||
recon_script = """
|
||||
Write-Host ""
|
||||
Write-Host "======== Computer ========"
|
||||
Get-ComputerInfo -Property CsName,WindowsProductName,OsVersion,OsInstallDate | Format-List
|
||||
|
||||
Write-Host "======== Boot Time ========"
|
||||
(Get-CimInstance Win32_OperatingSystem).LastBootUpTime
|
||||
|
||||
Write-Host "======== Local Users ========"
|
||||
Get-LocalUser | Select-Object Name,Enabled,LastLogon | Format-Table -AutoSize
|
||||
|
||||
Write-Host "======== Disks ========"
|
||||
Get-Volume | Where-Object DriveLetter | Select-Object DriveLetter,FileSystemLabel,
|
||||
@{n='Size(GB)';e={[math]::Round($_.Size / 1GB, 1)}},
|
||||
@{n='Free(GB)';e={[math]::Round($_.SizeRemaining / 1GB, 1)}} | Format-Table -AutoSize
|
||||
|
||||
Write-Host "======== IPv4 Addresses ========"
|
||||
Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.InterfaceAlias -notmatch 'Loopback' } |
|
||||
Select-Object InterfaceAlias,IPAddress | Format-Table -AutoSize
|
||||
|
||||
Write-Host "======== WinRM Recon Completed ========"
|
||||
"""
|
||||
|
||||
result = session.run_ps(recon_script)
|
||||
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
|
||||
if result.status_code == 0:
|
||||
print(f"✅ WinRM method successful")
|
||||
print(f"⏱️ Execution time: {duration:.2f} seconds")
|
||||
|
||||
# Show some output
|
||||
output = result.std_out.decode('utf-8', errors='replace')
|
||||
lines = [line.strip() for line in output.split('\n') if line.strip()]
|
||||
print(f"📊 Output lines: {len(lines)}")
|
||||
|
||||
return duration, True
|
||||
else:
|
||||
print(f"❌ WinRM method failed: {result.std_err.decode()}")
|
||||
return duration, False
|
||||
|
||||
except Exception as e:
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
print(f"❌ WinRM method error: {e}")
|
||||
return duration, False
|
||||
|
||||
|
||||
def main():
|
||||
print("🚀 SSH vs WinRM Performance Benchmark")
|
||||
print("=====================================")
|
||||
|
||||
# Benchmark SSH
|
||||
ssh_time, ssh_success = benchmark_ssh_method()
|
||||
|
||||
# Benchmark WinRM
|
||||
winrm_time, winrm_success = benchmark_winrm_method()
|
||||
|
||||
# Results comparison
|
||||
print("\n📊 RESULTS COMPARISON")
|
||||
print("=====================")
|
||||
|
||||
print(f"SSH Method: {ssh_time:.2f}s {'✅' if ssh_success else '❌'}")
|
||||
print(f"WinRM Method: {winrm_time:.2f}s {'✅' if winrm_success else '❌'}")
|
||||
|
||||
if ssh_success and winrm_success:
|
||||
if ssh_time < winrm_time:
|
||||
speedup = winrm_time / ssh_time
|
||||
print(f"\n🏃 SSH is {speedup:.1f}x faster than WinRM")
|
||||
else:
|
||||
speedup = ssh_time / winrm_time
|
||||
print(f"\n🏃 WinRM is {speedup:.1f}x faster than SSH")
|
||||
|
||||
print(f"\nTime difference: {abs(ssh_time - winrm_time):.2f} seconds")
|
||||
|
||||
print("\n🎯 RECOMMENDATION")
|
||||
print("=================")
|
||||
|
||||
if ssh_success and winrm_success:
|
||||
if ssh_time < winrm_time * 1.5: # If SSH is not significantly slower
|
||||
print("✅ Continue using SSH method:")
|
||||
print(" - Proven stability")
|
||||
print(" - Good performance")
|
||||
print(" - Handles large scripts well")
|
||||
print(" - Simpler setup")
|
||||
else:
|
||||
print("✅ Consider WinRM method for:")
|
||||
print(" - Better Windows integration")
|
||||
print(" - Advanced PowerShell features")
|
||||
print(" - Structured error handling")
|
||||
elif ssh_success:
|
||||
print("✅ SSH method is more reliable for this environment")
|
||||
elif winrm_success:
|
||||
print("✅ WinRM method works, SSH has issues")
|
||||
else:
|
||||
print("❌ Both methods have issues, investigate connectivity")
|
||||
|
||||
print("\n💡 HYBRID APPROACH")
|
||||
print("==================")
|
||||
print("Consider using both methods strategically:")
|
||||
print("- SSH: Large scripts, file operations, stability")
|
||||
print("- WinRM: Quick commands, Windows-specific tasks, real-time interaction")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
154
scripts/winrm/generate_winrm_wrappers.py
Executable file
154
scripts/winrm/generate_winrm_wrappers.py
Executable file
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate WinRM wrapper scripts for existing PowerShell scripts
|
||||
Creates Python WinRM alternatives to SSH-based scripts
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
|
||||
def create_winrm_wrapper(ps_script_name, description=""):
|
||||
"""Create a Python WinRM wrapper for a PowerShell script"""
|
||||
|
||||
script_name_base = ps_script_name.replace('.ps1', '')
|
||||
wrapper_name = f"{script_name_base}-winrm.py"
|
||||
|
||||
template = f'''#!/usr/bin/env python3
|
||||
"""
|
||||
WinRM version of {ps_script_name}
|
||||
{description}
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from winrm_executor import WinRMExecutor
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
# Get connection details
|
||||
host = os.getenv('HOST', '192.168.88.108')
|
||||
username = os.getenv('USER_NAME', 'Admin')
|
||||
password = os.getenv('PASS', 'P@ssw0rd!')
|
||||
|
||||
print(f"=== WinRM Execution: {{username}}@{{host}} - {ps_script_name} ===")
|
||||
|
||||
# Create WinRM executor
|
||||
executor = WinRMExecutor(host, username, password)
|
||||
|
||||
try:
|
||||
# Test connection
|
||||
print("[*] Establishing WinRM connection...")
|
||||
if not executor.connect():
|
||||
print("❌ Failed to establish WinRM connection")
|
||||
print("\\nTroubleshooting:")
|
||||
print(" 1. Ensure target machine is powered on and reachable")
|
||||
print(" 2. Verify WinRM service is running")
|
||||
print(" 3. Check firewall and network connectivity")
|
||||
sys.exit(1)
|
||||
|
||||
print("✅ WinRM connection established")
|
||||
|
||||
# Execute PowerShell script
|
||||
script_path = Path(__file__).parent / "{ps_script_name}"
|
||||
if not script_path.exists():
|
||||
print(f"❌ PowerShell script not found: {{script_path}}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[*] Executing {ps_script_name} via WinRM...")
|
||||
print("=" * 60)
|
||||
|
||||
success = executor.upload_and_execute_script(script_path)
|
||||
|
||||
print("=" * 60)
|
||||
if success:
|
||||
print(f"🎉 {{script_path.name}} completed successfully via WinRM!")
|
||||
else:
|
||||
print(f"❌ {{script_path.name}} failed")
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
executor.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
'''
|
||||
|
||||
return wrapper_name, template
|
||||
|
||||
|
||||
def main():
|
||||
current_dir = Path(__file__).parent
|
||||
|
||||
# List of PowerShell scripts and their descriptions
|
||||
scripts = {
|
||||
'recon.ps1': 'System reconnaissance and inventory',
|
||||
'activate-windows.ps1': 'Activate Windows using KMS method',
|
||||
'remove-windows-ai.ps1': 'Remove Windows AI components',
|
||||
'win11debloat.ps1': 'Remove Windows bloatware and disable telemetry',
|
||||
'verify-ai-removed.ps1': 'Verify Windows AI removal',
|
||||
'verify-debloat.ps1': 'Verify Windows debloating changes',
|
||||
'install-thunderbird.ps1': 'Install Thunderbird email client',
|
||||
'setup-thunderbird.ps1': 'Configure Thunderbird IMAP/SMTP',
|
||||
'install-telegram.ps1': 'Install Telegram Desktop',
|
||||
'install-nirsoft.ps1': 'Install NirSoft diagnostic tools',
|
||||
'nirsoft-dump.ps1': 'Collect system diagnostic data',
|
||||
'firewall-allow-ssh.ps1': 'Configure firewall for SSH access',
|
||||
'apply-ui-to-user.ps1': 'Apply UI settings to user',
|
||||
'configure-ime.ps1': 'Configure input method settings',
|
||||
'rename-hide-accounts.ps1': 'Rename and hide user accounts',
|
||||
'install-cports.ps1': 'Install CurrPorts network tool',
|
||||
'remove-apps.ps1': 'Remove specified applications'
|
||||
}
|
||||
|
||||
print("=== Generating WinRM Wrapper Scripts ===")
|
||||
print()
|
||||
|
||||
created_count = 0
|
||||
for ps_script, description in scripts.items():
|
||||
ps_path = current_dir / ps_script
|
||||
|
||||
if ps_path.exists():
|
||||
wrapper_name, wrapper_content = create_winrm_wrapper(ps_script, description)
|
||||
wrapper_path = current_dir / wrapper_name
|
||||
|
||||
# Only create if it doesn't exist or user confirms overwrite
|
||||
if wrapper_path.exists():
|
||||
response = input(f"⚠️ {wrapper_name} exists. Overwrite? [y/N]: ")
|
||||
if response.lower() != 'y':
|
||||
print(f"⏭️ Skipping {wrapper_name}")
|
||||
continue
|
||||
|
||||
with open(wrapper_path, 'w', encoding='utf-8') as f:
|
||||
f.write(wrapper_content)
|
||||
|
||||
# Make executable
|
||||
wrapper_path.chmod(0o755)
|
||||
|
||||
print(f"✅ Created: {wrapper_name}")
|
||||
created_count += 1
|
||||
else:
|
||||
print(f"⚠️ PowerShell script not found: {ps_script}")
|
||||
|
||||
print()
|
||||
print(f"🎉 Generated {created_count} WinRM wrapper scripts")
|
||||
print()
|
||||
print("Usage examples:")
|
||||
print(" python3 recon-winrm.py")
|
||||
print(" HOST=pc-74269 python3 install-thunderbird-winrm.py")
|
||||
print(" USER_NAME=SomeUser python3 apply-ui-to-user-winrm.py")
|
||||
print()
|
||||
print("Benefits of WinRM approach:")
|
||||
print(" ✅ Native Windows remote management")
|
||||
print(" ✅ Better PowerShell integration")
|
||||
print(" ✅ Structured error handling")
|
||||
print(" ✅ Automatic UTF-8 BOM handling for Chinese Windows")
|
||||
print(" ✅ Detailed logging and progress reporting")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
61
scripts/winrm/recon-winrm.py
Executable file
61
scripts/winrm/recon-winrm.py
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
WinRM version of recon script
|
||||
Demonstrates how to replace SSH-based scripts with Python WinRM
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from winrm_executor import WinRMExecutor
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
# Get connection details
|
||||
host = os.getenv('HOST', '192.168.88.108')
|
||||
username = os.getenv('USER_NAME', 'Admin')
|
||||
password = os.getenv('PASS', 'P@ssw0rd!')
|
||||
|
||||
print(f"=== WinRM Reconnaissance: {username}@{host} ===")
|
||||
|
||||
# Create WinRM executor
|
||||
executor = WinRMExecutor(host, username, password)
|
||||
|
||||
try:
|
||||
# Test connection
|
||||
print("[*] Establishing WinRM connection...")
|
||||
if not executor.connect():
|
||||
print("❌ Failed to establish WinRM connection")
|
||||
print("\nTroubleshooting:")
|
||||
print(" 1. Ensure target machine is powered on and reachable")
|
||||
print(" 2. Verify WinRM service is running: Get-Service WinRM")
|
||||
print(" 3. Check AllowUnencrypted setting: winrm get winrm/config/service")
|
||||
print(" 4. Verify firewall rules: Get-NetFirewallRule -DisplayGroup '*Remote Management*'")
|
||||
sys.exit(1)
|
||||
|
||||
print("✅ WinRM connection established")
|
||||
|
||||
# Execute recon script
|
||||
script_path = Path(__file__).parent / "../powershell/recon.ps1"
|
||||
if not script_path.exists():
|
||||
print(f"❌ PowerShell script not found: {script_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[*] Executing reconnaissance script via WinRM...")
|
||||
print("=" * 60)
|
||||
|
||||
success = executor.upload_and_execute_script(script_path)
|
||||
|
||||
print("=" * 60)
|
||||
if success:
|
||||
print("🎉 Reconnaissance completed successfully via WinRM!")
|
||||
else:
|
||||
print("❌ Reconnaissance failed")
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
executor.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
172
scripts/winrm/smart_executor.py
Executable file
172
scripts/winrm/smart_executor.py
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Smart Executor - Automatically choose between SSH and WinRM
|
||||
Based on script size and complexity
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from winrm import Session
|
||||
|
||||
|
||||
class SmartExecutor:
|
||||
def __init__(self, host=None, username=None, password=None):
|
||||
self.host = host or os.getenv('HOST', '192.168.88.108')
|
||||
self.username = username or os.getenv('USER_NAME', 'Admin')
|
||||
self.password = password or os.getenv('PASS', 'P@ssw0rd!')
|
||||
|
||||
def analyze_script(self, script_path):
|
||||
"""Analyze script to determine best execution method"""
|
||||
script_path = Path(script_path)
|
||||
|
||||
if not script_path.exists():
|
||||
return None
|
||||
|
||||
# Get script size
|
||||
size = script_path.stat().st_size
|
||||
|
||||
# Read content for analysis
|
||||
with open(script_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
lines = len(content.split('\n'))
|
||||
chars = len(content)
|
||||
|
||||
analysis = {
|
||||
'size_bytes': size,
|
||||
'lines': lines,
|
||||
'characters': chars,
|
||||
'has_file_operations': any(keyword in content.lower() for keyword in
|
||||
['out-file', 'copy-item', 'move-item', 'new-item', 'remove-item']),
|
||||
'has_long_commands': any(len(line) > 500 for line in content.split('\n')),
|
||||
'complexity_score': lines + (chars / 100) + (size / 1000)
|
||||
}
|
||||
|
||||
return analysis
|
||||
|
||||
def choose_method(self, script_path):
|
||||
"""Choose optimal execution method"""
|
||||
analysis = self.analyze_script(script_path)
|
||||
|
||||
if not analysis:
|
||||
return "error"
|
||||
|
||||
# Decision logic
|
||||
if analysis['size_bytes'] > 5000: # Large scripts
|
||||
return "ssh"
|
||||
elif analysis['lines'] > 100: # Many lines
|
||||
return "ssh"
|
||||
elif analysis['has_file_operations']: # File operations
|
||||
return "ssh"
|
||||
elif analysis['has_long_commands']: # Very long command lines
|
||||
return "ssh"
|
||||
elif analysis['complexity_score'] > 150: # Complex scripts
|
||||
return "ssh"
|
||||
else:
|
||||
return "winrm" # Simple, fast execution
|
||||
|
||||
def execute_via_ssh(self, script_path):
|
||||
"""Execute script using SSH method"""
|
||||
script_name = Path(script_path).stem
|
||||
ssh_script = f"{script_name}.sh"
|
||||
|
||||
if not Path(ssh_script).exists():
|
||||
print(f"❌ SSH wrapper {ssh_script} not found")
|
||||
return False
|
||||
|
||||
try:
|
||||
result = subprocess.run([f'./{ssh_script}'], capture_output=False, timeout=300)
|
||||
return result.returncode == 0
|
||||
except Exception as e:
|
||||
print(f"❌ SSH execution failed: {e}")
|
||||
return False
|
||||
|
||||
def execute_via_winrm(self, script_path):
|
||||
"""Execute script using WinRM method"""
|
||||
try:
|
||||
# Read and execute script content directly
|
||||
with open(script_path, 'r', encoding='utf-8') as f:
|
||||
script_content = f.read()
|
||||
|
||||
# Connect via WinRM
|
||||
session = Session(
|
||||
f'http://{self.host}:5985/wsman',
|
||||
auth=(self.username, self.password),
|
||||
transport='ntlm'
|
||||
)
|
||||
|
||||
# Execute PowerShell
|
||||
result = session.run_ps(script_content)
|
||||
|
||||
# Output results
|
||||
if result.std_out:
|
||||
output = result.std_out.decode('utf-8', errors='replace')
|
||||
for line in output.split('\n'):
|
||||
if line.strip():
|
||||
print(line)
|
||||
|
||||
if result.std_err:
|
||||
error = result.std_err.decode('utf-8', errors='replace')
|
||||
if error.strip():
|
||||
for line in error.split('\n'):
|
||||
if line.strip():
|
||||
print(f"ERROR: {line}")
|
||||
|
||||
return result.status_code == 0
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ WinRM execution failed: {e}")
|
||||
return False
|
||||
|
||||
def execute(self, script_path):
|
||||
"""Smart execute with automatic method selection"""
|
||||
script_path = Path(script_path)
|
||||
|
||||
print(f"🧠 Analyzing script: {script_path.name}")
|
||||
|
||||
analysis = self.analyze_script(script_path)
|
||||
method = self.choose_method(script_path)
|
||||
|
||||
print(f"📊 Script analysis:")
|
||||
print(f" Size: {analysis['size_bytes']} bytes")
|
||||
print(f" Lines: {analysis['lines']}")
|
||||
print(f" Complexity: {analysis['complexity_score']:.1f}")
|
||||
print(f" File operations: {analysis['has_file_operations']}")
|
||||
|
||||
print(f"⚡ Selected method: {method.upper()}")
|
||||
|
||||
if method == "ssh":
|
||||
print("🔗 Executing via SSH (for large/complex scripts)")
|
||||
return self.execute_via_ssh(script_path)
|
||||
elif method == "winrm":
|
||||
print("⚡ Executing via WinRM (for speed)")
|
||||
return self.execute_via_winrm(script_path)
|
||||
else:
|
||||
print("❌ Unable to determine execution method")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python3 smart_executor.py <script.ps1>")
|
||||
sys.exit(1)
|
||||
|
||||
script_path = sys.argv[1]
|
||||
executor = SmartExecutor()
|
||||
|
||||
print("🎯 Smart PowerShell Executor")
|
||||
print("============================")
|
||||
|
||||
success = executor.execute(script_path)
|
||||
|
||||
if success:
|
||||
print(f"\n✅ Script execution completed successfully")
|
||||
else:
|
||||
print(f"\n❌ Script execution failed")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
97
scripts/winrm/test_simple_winrm.py
Normal file
97
scripts/winrm/test_simple_winrm.py
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test simple WinRM script execution
|
||||
Uses very small commands to avoid length limits
|
||||
"""
|
||||
|
||||
import os
|
||||
from winrm import Session
|
||||
|
||||
|
||||
def test_simple_script_execution():
|
||||
host = os.getenv('HOST', '192.168.88.108')
|
||||
username = os.getenv('USER_NAME', 'Admin')
|
||||
password = os.getenv('PASS', 'P@ssw0rd!')
|
||||
|
||||
print(f"Testing simple WinRM script execution on {host}")
|
||||
|
||||
try:
|
||||
# Connect
|
||||
session = Session(
|
||||
f'http://{host}:5985/wsman',
|
||||
auth=(username, password),
|
||||
transport='ntlm'
|
||||
)
|
||||
|
||||
# Test 1: Simple inline PowerShell
|
||||
print("\n[Test 1] Running simple inline PowerShell:")
|
||||
simple_ps = """
|
||||
Write-Host "=== Simple WinRM Test ==="
|
||||
Write-Host "Computer: $env:COMPUTERNAME"
|
||||
Write-Host "User: $env:USERNAME"
|
||||
Write-Host "PowerShell: $($PSVersionTable.PSVersion)"
|
||||
Write-Host "Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
||||
Write-Host "=== End Test ==="
|
||||
"""
|
||||
|
||||
result = session.run_ps(simple_ps)
|
||||
|
||||
if result.status_code == 0:
|
||||
print("✅ Simple PowerShell execution successful:")
|
||||
output = result.std_out.decode('utf-8', errors='replace')
|
||||
for line in output.split('\n'):
|
||||
if line.strip():
|
||||
print(f" {line.strip()}")
|
||||
else:
|
||||
print(f"❌ Simple PowerShell failed: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
# Test 2: Create small script file
|
||||
print("\n[Test 2] Creating and executing small script file:")
|
||||
|
||||
# Create a very small script
|
||||
create_script = """
|
||||
$scriptContent = @'
|
||||
Write-Host "Small script test"
|
||||
Get-ComputerInfo -Property CsName, WindowsProductName | Format-List
|
||||
Write-Host "Small script completed"
|
||||
'@
|
||||
|
||||
$scriptContent | Out-File -FilePath 'C:\\Users\\Admin\\small_test.ps1' -Encoding UTF8
|
||||
Write-Host "Small script file created"
|
||||
"""
|
||||
|
||||
result = session.run_ps(create_script)
|
||||
|
||||
if result.status_code == 0:
|
||||
print("✅ Script file created successfully")
|
||||
|
||||
# Execute the small script
|
||||
exec_result = session.run_cmd('powershell -NoProfile -ExecutionPolicy Bypass -File C:\\Users\\Admin\\small_test.ps1')
|
||||
|
||||
if exec_result.status_code == 0:
|
||||
print("✅ Script execution successful:")
|
||||
output = exec_result.std_out.decode('utf-8', errors='replace')
|
||||
for line in output.split('\n'):
|
||||
if line.strip():
|
||||
print(f" {line.strip()}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Script execution failed: {exec_result.std_err.decode()}")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ Script creation failed: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Test failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if test_simple_script_execution():
|
||||
print("\n🎉 Simple WinRM script execution works!")
|
||||
print("This proves WinRM can execute PowerShell scripts.")
|
||||
print("The issue is with large script handling, not basic functionality.")
|
||||
else:
|
||||
print("\n❌ Basic WinRM functionality failed")
|
||||
96
scripts/winrm/test_winrm_negotiate.py
Normal file
96
scripts/winrm/test_winrm_negotiate.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test WinRM with Negotiate authentication (instead of Basic)
|
||||
"""
|
||||
|
||||
import os
|
||||
from winrm import Session
|
||||
|
||||
|
||||
def test_negotiate_auth():
|
||||
host = os.getenv('HOST', '192.168.88.108')
|
||||
username = os.getenv('USER_NAME', 'Admin')
|
||||
password = os.getenv('PASS', 'P@ssw0rd!')
|
||||
|
||||
print(f"Testing WinRM with Negotiate authentication to {host}")
|
||||
|
||||
try:
|
||||
# Try with negotiate/ntlm transport
|
||||
session = Session(
|
||||
f'http://{host}:5985/wsman',
|
||||
auth=(username, password),
|
||||
transport='ntlm' # Use NTLM instead of plaintext
|
||||
)
|
||||
|
||||
print("✅ Session created with NTLM transport")
|
||||
|
||||
# Test simple command
|
||||
result = session.run_cmd('echo "NTLM test successful"')
|
||||
|
||||
if result.status_code == 0:
|
||||
print(f"✅ Command successful: {result.std_out.decode().strip()}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Command failed: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ NTLM authentication failed: {e}")
|
||||
|
||||
# Try with ssl transport (even though we're using HTTP)
|
||||
try:
|
||||
print("Trying with SSL transport (ignore cert validation)...")
|
||||
session = Session(
|
||||
f'http://{host}:5985/wsman',
|
||||
auth=(username, password),
|
||||
transport='ssl',
|
||||
server_cert_validation='ignore'
|
||||
)
|
||||
|
||||
print("✅ Session created with SSL transport")
|
||||
|
||||
result = session.run_cmd('echo "SSL transport test"')
|
||||
|
||||
if result.status_code == 0:
|
||||
print(f"✅ Command successful: {result.std_out.decode().strip()}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Command failed: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
except Exception as e2:
|
||||
print(f"❌ SSL transport also failed: {e2}")
|
||||
|
||||
# Try with kerberos if available
|
||||
try:
|
||||
print("Trying with Kerberos transport...")
|
||||
session = Session(
|
||||
f'http://{host}:5985/wsman',
|
||||
auth=(username, password),
|
||||
transport='kerberos'
|
||||
)
|
||||
|
||||
print("✅ Session created with Kerberos transport")
|
||||
|
||||
result = session.run_cmd('echo "Kerberos test"')
|
||||
|
||||
if result.status_code == 0:
|
||||
print(f"✅ Command successful: {result.std_out.decode().strip()}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Command failed: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
except Exception as e3:
|
||||
print(f"❌ Kerberos transport failed: {e3}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_negotiate_auth()
|
||||
if success:
|
||||
print("\n🎉 Alternative authentication method works!")
|
||||
else:
|
||||
print("\n❌ All authentication methods failed")
|
||||
print("\nThis suggests the issue is with AllowUnencrypted=false")
|
||||
print("We may need to force this setting or use HTTPS instead")
|
||||
192
scripts/winrm/test_winrm_python.py
Executable file
192
scripts/winrm/test_winrm_python.py
Executable file
@@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Python WinRM Connection Test
|
||||
Tests WinRM connectivity using pywinrm library
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from winrm.protocol import Protocol
|
||||
from winrm import Session
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def test_basic_connection(host, username, password):
|
||||
"""Test basic WinRM connectivity"""
|
||||
print(f"[1/4] Testing basic WinRM protocol connection to {host}...")
|
||||
|
||||
try:
|
||||
# Create a basic protocol connection
|
||||
endpoint = f"http://{host}:5985/wsman"
|
||||
p = Protocol(
|
||||
endpoint=endpoint,
|
||||
transport='plaintext',
|
||||
username=username,
|
||||
password=password
|
||||
)
|
||||
|
||||
# Try a simple identify operation
|
||||
result = p.send_message(
|
||||
'<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsmid="http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd">'
|
||||
'<s:Header></s:Header>'
|
||||
'<s:Body>'
|
||||
'<wsmid:Identify/>'
|
||||
'</s:Body>'
|
||||
'</s:Envelope>'
|
||||
)
|
||||
|
||||
print("✅ Basic WinRM protocol connection successful")
|
||||
|
||||
# Parse response to get some info
|
||||
try:
|
||||
root = ET.fromstring(result)
|
||||
for elem in root.iter():
|
||||
if 'ProductVersion' in elem.tag:
|
||||
print(f" Protocol Version: {elem.text}")
|
||||
elif 'ProductVendor' in elem.tag:
|
||||
print(f" Product Vendor: {elem.text}")
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Basic connection failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def test_session_connection(host, username, password):
|
||||
"""Test WinRM session connectivity"""
|
||||
print(f"\n[2/4] Testing WinRM session connection...")
|
||||
|
||||
try:
|
||||
session = Session(
|
||||
f'http://{host}:5985/wsman',
|
||||
auth=(username, password),
|
||||
transport='plaintext'
|
||||
)
|
||||
|
||||
print("✅ WinRM session created successfully")
|
||||
return session
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Session creation failed: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def test_command_execution(session):
|
||||
"""Test basic command execution"""
|
||||
print(f"\n[3/4] Testing command execution...")
|
||||
|
||||
try:
|
||||
# Test simple command
|
||||
result = session.run_cmd('echo "Hello from WinRM"')
|
||||
|
||||
if result.status_code == 0:
|
||||
print("✅ Command execution successful")
|
||||
print(f" Output: {result.std_out.decode().strip()}")
|
||||
else:
|
||||
print(f"⚠️ Command execution completed with exit code: {result.status_code}")
|
||||
print(f" Error: {result.std_err.decode().strip()}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Command execution failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def test_powershell_execution(session):
|
||||
"""Test PowerShell script execution"""
|
||||
print(f"\n[4/4] Testing PowerShell execution...")
|
||||
|
||||
try:
|
||||
# Test PowerShell command
|
||||
ps_script = """
|
||||
$info = @{
|
||||
ComputerName = $env:COMPUTERNAME
|
||||
UserName = $env:USERNAME
|
||||
PowerShellVersion = $PSVersionTable.PSVersion.ToString()
|
||||
WindowsVersion = (Get-ComputerInfo -Property WindowsProductName).WindowsProductName
|
||||
CurrentTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
|
||||
}
|
||||
|
||||
Write-Host "=== Remote System Information ==="
|
||||
Write-Host "Computer: $($info.ComputerName)"
|
||||
Write-Host "User: $($info.UserName)"
|
||||
Write-Host "PowerShell: $($info.PowerShellVersion)"
|
||||
Write-Host "Windows: $($info.WindowsVersion)"
|
||||
Write-Host "Time: $($info.CurrentTime)"
|
||||
Write-Host "=== End Information ==="
|
||||
"""
|
||||
|
||||
result = session.run_ps(ps_script)
|
||||
|
||||
if result.status_code == 0:
|
||||
print("✅ PowerShell execution successful")
|
||||
print("Remote system info:")
|
||||
output = result.std_out.decode().strip()
|
||||
for line in output.split('\n'):
|
||||
if line.strip():
|
||||
print(f" {line.strip()}")
|
||||
else:
|
||||
print(f"⚠️ PowerShell execution completed with exit code: {result.status_code}")
|
||||
if result.std_err:
|
||||
print(f" Error: {result.std_err.decode().strip()}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ PowerShell execution failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
# Get connection details from environment or use defaults
|
||||
host = os.getenv('HOST', '192.168.88.108')
|
||||
username = os.getenv('USER_NAME', 'Admin')
|
||||
password = os.getenv('PASS', 'P@ssw0rd!')
|
||||
|
||||
print("=== Python WinRM Connection Test ===")
|
||||
print(f"Target: {username}@{host}")
|
||||
print("")
|
||||
|
||||
# Test sequence
|
||||
success = True
|
||||
|
||||
# Test 1: Basic connection
|
||||
if not test_basic_connection(host, username, password):
|
||||
success = False
|
||||
print("\nℹ️ If basic connection fails, ensure:")
|
||||
print(" - WinRM service is running on target")
|
||||
print(" - Port 5985 is accessible")
|
||||
print(" - AllowUnencrypted=true for HTTP connections")
|
||||
sys.exit(1)
|
||||
|
||||
# Test 2: Session creation
|
||||
session = test_session_connection(host, username, password)
|
||||
if not session:
|
||||
success = False
|
||||
sys.exit(1)
|
||||
|
||||
# Test 3: Command execution
|
||||
if not test_command_execution(session):
|
||||
success = False
|
||||
|
||||
# Test 4: PowerShell execution
|
||||
if not test_powershell_execution(session):
|
||||
success = False
|
||||
|
||||
if success:
|
||||
print(f"\n🎉 All tests passed! Python WinRM is working correctly.")
|
||||
print(f"\nNext steps:")
|
||||
print(f" 1. Create Python WinRM wrapper for existing scripts")
|
||||
print(f" 2. Compare performance with SSH method")
|
||||
print(f" 3. Implement error handling and logging")
|
||||
else:
|
||||
print(f"\n⚠️ Some tests failed. Check configuration and try again.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
250
scripts/winrm/winrm_executor.py
Executable file
250
scripts/winrm/winrm_executor.py
Executable file
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
WinRM Script Executor
|
||||
A Python-based alternative to SSH for executing PowerShell scripts on Windows machines.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from winrm import Session
|
||||
import logging
|
||||
|
||||
|
||||
class WinRMExecutor:
|
||||
def __init__(self, host, username, password, port=5985, use_https=False):
|
||||
self.host = host
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.port = port
|
||||
self.use_https = use_https
|
||||
self.session = None
|
||||
|
||||
# Setup logging
|
||||
self.setup_logging()
|
||||
|
||||
def setup_logging(self):
|
||||
"""Setup logging configuration"""
|
||||
log_dir = Path(__file__).parent / "../../logs"
|
||||
log_dir.mkdir(exist_ok=True)
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
log_file = log_dir / f"winrm-{self.host}-{timestamp}.log"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(log_file),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.info(f"Starting WinRM session to {self.host}")
|
||||
|
||||
def connect(self):
|
||||
"""Establish WinRM connection"""
|
||||
try:
|
||||
protocol = 'https' if self.use_https else 'http'
|
||||
endpoint = f'{protocol}://{self.host}:{self.port}/wsman'
|
||||
|
||||
self.logger.info(f"Connecting to {endpoint}")
|
||||
|
||||
self.session = Session(
|
||||
endpoint,
|
||||
auth=(self.username, self.password),
|
||||
transport='ntlm' if not self.use_https else 'ssl',
|
||||
server_cert_validation='ignore' if self.use_https else 'validate'
|
||||
)
|
||||
|
||||
# Test connection with a simple command
|
||||
result = self.session.run_cmd('echo Connection test')
|
||||
if result.status_code == 0:
|
||||
self.logger.info("✅ WinRM connection established successfully")
|
||||
return True
|
||||
else:
|
||||
self.logger.error(f"❌ Connection test failed: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"❌ Failed to connect: {e}")
|
||||
return False
|
||||
|
||||
def upload_and_execute_script(self, local_script_path, remote_script_name=None):
|
||||
"""Upload and execute a PowerShell script"""
|
||||
if not self.session:
|
||||
self.logger.error("No active WinRM session")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Read the local script
|
||||
with open(local_script_path, 'r', encoding='utf-8') as f:
|
||||
script_content = f.read()
|
||||
|
||||
# Generate remote script name if not provided
|
||||
if not remote_script_name:
|
||||
script_name = Path(local_script_path).name
|
||||
remote_script_name = f"C:\\Users\\{self.username}\\{script_name}"
|
||||
|
||||
self.logger.info(f"Uploading script: {local_script_path} -> {remote_script_name}")
|
||||
|
||||
# Create the script on remote machine
|
||||
create_script = f"""
|
||||
$scriptContent = @'
|
||||
{script_content}
|
||||
'@
|
||||
|
||||
# Ensure UTF-8 encoding with BOM for Chinese Windows
|
||||
$utf8BOM = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText('{remote_script_name}', $scriptContent, $utf8BOM)
|
||||
|
||||
Write-Host "Script uploaded to {remote_script_name}"
|
||||
"""
|
||||
|
||||
upload_result = self.session.run_ps(create_script)
|
||||
|
||||
if upload_result.status_code != 0:
|
||||
self.logger.error(f"Failed to upload script: {upload_result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
self.logger.info("✅ Script uploaded successfully")
|
||||
|
||||
# Execute the script
|
||||
self.logger.info(f"Executing PowerShell script: {remote_script_name}")
|
||||
|
||||
execute_command = f"PowerShell -NoProfile -ExecutionPolicy Bypass -File '{remote_script_name}'"
|
||||
result = self.session.run_cmd(execute_command)
|
||||
|
||||
# Output results
|
||||
if result.std_out:
|
||||
output = result.std_out.decode('utf-8', errors='replace')
|
||||
self.logger.info("Script Output:")
|
||||
for line in output.split('\n'):
|
||||
if line.strip():
|
||||
print(line)
|
||||
self.logger.info(line)
|
||||
|
||||
if result.std_err:
|
||||
error = result.std_err.decode('utf-8', errors='replace')
|
||||
if error.strip():
|
||||
self.logger.warning("Script Errors:")
|
||||
for line in error.split('\n'):
|
||||
if line.strip():
|
||||
print(f"ERROR: {line}")
|
||||
self.logger.warning(line)
|
||||
|
||||
success = result.status_code == 0
|
||||
status = "✅ SUCCESS" if success else f"❌ FAILED (exit code: {result.status_code})"
|
||||
self.logger.info(f"Script execution completed: {status}")
|
||||
|
||||
return success
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"❌ Script execution failed: {e}")
|
||||
return False
|
||||
|
||||
def execute_command(self, command, use_powershell=False):
|
||||
"""Execute a single command"""
|
||||
if not self.session:
|
||||
self.logger.error("No active WinRM session")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.logger.info(f"Executing command: {command}")
|
||||
|
||||
if use_powershell:
|
||||
result = self.session.run_ps(command)
|
||||
else:
|
||||
result = self.session.run_cmd(command)
|
||||
|
||||
# Output results
|
||||
if result.std_out:
|
||||
output = result.std_out.decode('utf-8', errors='replace')
|
||||
for line in output.split('\n'):
|
||||
if line.strip():
|
||||
print(line)
|
||||
|
||||
if result.std_err:
|
||||
error = result.std_err.decode('utf-8', errors='replace')
|
||||
if error.strip():
|
||||
for line in error.split('\n'):
|
||||
if line.strip():
|
||||
print(f"ERROR: {line}")
|
||||
|
||||
return result.status_code == 0
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Command execution failed: {e}")
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
"""Close the WinRM session"""
|
||||
if self.session:
|
||||
self.logger.info("Closing WinRM session")
|
||||
# Note: pywinrm doesn't have an explicit close method
|
||||
self.session = None
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Execute PowerShell scripts via WinRM')
|
||||
parser.add_argument('--host', default=os.getenv('HOST', '192.168.88.108'),
|
||||
help='Target host (default: from HOST env var or 192.168.88.108)')
|
||||
parser.add_argument('--username', default=os.getenv('USER_NAME', 'Admin'),
|
||||
help='Username (default: from USER_NAME env var or Admin)')
|
||||
parser.add_argument('--password', default=os.getenv('PASS', 'P@ssw0rd!'),
|
||||
help='Password (default: from PASS env var)')
|
||||
parser.add_argument('--port', type=int, default=5985,
|
||||
help='WinRM port (default: 5985)')
|
||||
parser.add_argument('--https', action='store_true',
|
||||
help='Use HTTPS instead of HTTP')
|
||||
parser.add_argument('--script', required=True,
|
||||
help='PowerShell script file to execute')
|
||||
parser.add_argument('--test-connection', action='store_true',
|
||||
help='Test connection only, do not execute script')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create executor
|
||||
executor = WinRMExecutor(
|
||||
host=args.host,
|
||||
username=args.username,
|
||||
password=args.password,
|
||||
port=args.port,
|
||||
use_https=args.https
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect
|
||||
if not executor.connect():
|
||||
print("❌ Failed to establish WinRM connection")
|
||||
sys.exit(1)
|
||||
|
||||
if args.test_connection:
|
||||
print("✅ WinRM connection test successful")
|
||||
return
|
||||
|
||||
# Execute script
|
||||
script_path = Path(args.script)
|
||||
if not script_path.exists():
|
||||
print(f"❌ Script file not found: {script_path}")
|
||||
sys.exit(1)
|
||||
|
||||
success = executor.upload_and_execute_script(script_path)
|
||||
|
||||
if success:
|
||||
print(f"\n🎉 Script execution completed successfully")
|
||||
else:
|
||||
print(f"\n❌ Script execution failed")
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
executor.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
253
scripts/winrm/winrm_executor_v2.py
Executable file
253
scripts/winrm/winrm_executor_v2.py
Executable file
@@ -0,0 +1,253 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Improved WinRM Script Executor
|
||||
Handles large scripts by uploading them in chunks
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
import time
|
||||
import base64
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from winrm import Session
|
||||
import logging
|
||||
|
||||
|
||||
class WinRMExecutorV2:
|
||||
def __init__(self, host, username, password, port=5985, use_https=False):
|
||||
self.host = host
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.port = port
|
||||
self.use_https = use_https
|
||||
self.session = None
|
||||
|
||||
# Setup logging
|
||||
self.setup_logging()
|
||||
|
||||
def setup_logging(self):
|
||||
"""Setup logging configuration"""
|
||||
log_dir = Path(__file__).parent / "../../logs"
|
||||
log_dir.mkdir(exist_ok=True)
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
log_file = log_dir / f"winrm-v2-{self.host}-{timestamp}.log"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(log_file),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.info(f"Starting WinRM session to {self.host}")
|
||||
|
||||
def connect(self):
|
||||
"""Establish WinRM connection"""
|
||||
try:
|
||||
protocol = 'https' if self.use_https else 'http'
|
||||
endpoint = f'{protocol}://{self.host}:{self.port}/wsman'
|
||||
|
||||
self.logger.info(f"Connecting to {endpoint}")
|
||||
|
||||
self.session = Session(
|
||||
endpoint,
|
||||
auth=(self.username, self.password),
|
||||
transport='ntlm' if not self.use_https else 'ssl',
|
||||
server_cert_validation='ignore' if self.use_https else 'validate'
|
||||
)
|
||||
|
||||
# Test connection with a simple command
|
||||
result = self.session.run_cmd('echo Connection test')
|
||||
if result.status_code == 0:
|
||||
self.logger.info("✅ WinRM connection established successfully")
|
||||
return True
|
||||
else:
|
||||
self.logger.error(f"❌ Connection test failed: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"❌ Failed to connect: {e}")
|
||||
return False
|
||||
|
||||
def upload_script_chunked(self, local_script_path, remote_script_name):
|
||||
"""Upload script in chunks to avoid command line length limits"""
|
||||
try:
|
||||
# Read the local script
|
||||
with open(local_script_path, 'r', encoding='utf-8') as f:
|
||||
script_content = f.read()
|
||||
|
||||
self.logger.info(f"Uploading script in chunks: {local_script_path} -> {remote_script_name}")
|
||||
|
||||
# Encode content as base64 to avoid special character issues
|
||||
script_bytes = script_content.encode('utf-8')
|
||||
script_b64 = base64.b64encode(script_bytes).decode('ascii')
|
||||
|
||||
# Split into chunks of 8000 chars (safe for command line)
|
||||
chunk_size = 8000
|
||||
chunks = [script_b64[i:i+chunk_size] for i in range(0, len(script_b64), chunk_size)]
|
||||
|
||||
self.logger.info(f"Uploading {len(chunks)} chunks...")
|
||||
|
||||
# Create the file and append chunks
|
||||
for i, chunk in enumerate(chunks):
|
||||
if i == 0:
|
||||
# First chunk - create file
|
||||
ps_command = f"""
|
||||
$chunk = '{chunk}'
|
||||
$chunk | Out-File -FilePath '{remote_script_name}.b64' -Encoding ASCII
|
||||
"""
|
||||
else:
|
||||
# Subsequent chunks - append
|
||||
ps_command = f"""
|
||||
$chunk = '{chunk}'
|
||||
$chunk | Out-File -FilePath '{remote_script_name}.b64' -Encoding ASCII -Append
|
||||
"""
|
||||
|
||||
result = self.session.run_ps(ps_command)
|
||||
|
||||
if result.status_code != 0:
|
||||
self.logger.error(f"Failed to upload chunk {i+1}: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
if (i + 1) % 10 == 0:
|
||||
self.logger.info(f"Uploaded {i+1}/{len(chunks)} chunks...")
|
||||
|
||||
# Decode the base64 file to create the final script
|
||||
decode_command = f"""
|
||||
$b64Content = Get-Content '{remote_script_name}.b64' -Raw
|
||||
$bytes = [System.Convert]::FromBase64String($b64Content)
|
||||
|
||||
# Write with UTF-8 BOM for Chinese Windows compatibility
|
||||
$utf8BOM = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllBytes('{remote_script_name}', $bytes)
|
||||
|
||||
# Clean up temporary file
|
||||
Remove-Item '{remote_script_name}.b64' -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "Script uploaded successfully to {remote_script_name}"
|
||||
"""
|
||||
|
||||
result = self.session.run_ps(decode_command)
|
||||
|
||||
if result.status_code == 0:
|
||||
self.logger.info("✅ Script uploaded and decoded successfully")
|
||||
return True
|
||||
else:
|
||||
self.logger.error(f"Failed to decode script: {result.std_err.decode()}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"❌ Script upload failed: {e}")
|
||||
return False
|
||||
|
||||
def execute_script(self, remote_script_name):
|
||||
"""Execute the uploaded PowerShell script"""
|
||||
try:
|
||||
self.logger.info(f"Executing PowerShell script: {remote_script_name}")
|
||||
|
||||
# Execute the script
|
||||
execute_command = f"PowerShell -NoProfile -ExecutionPolicy Bypass -File '{remote_script_name}'"
|
||||
result = self.session.run_cmd(execute_command)
|
||||
|
||||
# Output results in real-time
|
||||
if result.std_out:
|
||||
output = result.std_out.decode('utf-8', errors='replace')
|
||||
self.logger.info("Script Output:")
|
||||
for line in output.split('\n'):
|
||||
if line.strip():
|
||||
print(line)
|
||||
|
||||
if result.std_err:
|
||||
error = result.std_err.decode('utf-8', errors='replace')
|
||||
if error.strip():
|
||||
self.logger.warning("Script Errors:")
|
||||
for line in error.split('\n'):
|
||||
if line.strip():
|
||||
print(f"ERROR: {line}")
|
||||
|
||||
success = result.status_code == 0
|
||||
status = "✅ SUCCESS" if success else f"❌ FAILED (exit code: {result.status_code})"
|
||||
self.logger.info(f"Script execution completed: {status}")
|
||||
|
||||
return success
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"❌ Script execution failed: {e}")
|
||||
return False
|
||||
|
||||
def upload_and_execute_script(self, local_script_path, remote_script_name=None):
|
||||
"""Upload and execute a PowerShell script using chunked upload"""
|
||||
if not self.session:
|
||||
self.logger.error("No active WinRM session")
|
||||
return False
|
||||
|
||||
# Generate remote script name if not provided
|
||||
if not remote_script_name:
|
||||
script_name = Path(local_script_path).name
|
||||
remote_script_name = f"C:\\Users\\{self.username}\\{script_name}"
|
||||
|
||||
# Upload script
|
||||
if not self.upload_script_chunked(local_script_path, remote_script_name):
|
||||
return False
|
||||
|
||||
# Execute script
|
||||
return self.execute_script(remote_script_name)
|
||||
|
||||
def close(self):
|
||||
"""Close the WinRM session"""
|
||||
if self.session:
|
||||
self.logger.info("Closing WinRM session")
|
||||
self.session = None
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Execute PowerShell scripts via WinRM (v2)')
|
||||
parser.add_argument('--host', default=os.getenv('HOST', '192.168.88.108'),
|
||||
help='Target host')
|
||||
parser.add_argument('--username', default=os.getenv('USER_NAME', 'Admin'),
|
||||
help='Username')
|
||||
parser.add_argument('--password', default=os.getenv('PASS', 'P@ssw0rd!'),
|
||||
help='Password')
|
||||
parser.add_argument('--script', required=True,
|
||||
help='PowerShell script file to execute')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create executor
|
||||
executor = WinRMExecutorV2(
|
||||
host=args.host,
|
||||
username=args.username,
|
||||
password=args.password
|
||||
)
|
||||
|
||||
try:
|
||||
# Connect
|
||||
if not executor.connect():
|
||||
sys.exit(1)
|
||||
|
||||
# Execute script
|
||||
script_path = Path(args.script)
|
||||
if not script_path.exists():
|
||||
print(f"❌ Script file not found: {script_path}")
|
||||
sys.exit(1)
|
||||
|
||||
success = executor.upload_and_execute_script(script_path)
|
||||
|
||||
if success:
|
||||
print(f"\n🎉 Script execution completed successfully")
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
executor.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user