souleyez 2.43.34__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of souleyez might be problematic. Click here for more details.

@@ -337,50 +337,78 @@ class HttpFingerprintPlugin(PluginBase):
337
337
  target = f"http://{target}"
338
338
 
339
339
  try:
340
- # Smart dual-probe: try both HTTP and HTTPS, use the better one
341
- result, effective_url = self._smart_probe(target, timeout)
340
+ # Use thread-based hard timeout to prevent indefinite hangs
341
+ # urllib timeouts don't always work if server accepts connection but stalls
342
+ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeout
343
+
344
+ hard_timeout = timeout * 3 # 30 seconds max for entire probe operation
345
+
346
+ with ThreadPoolExecutor(max_workers=1) as executor:
347
+ future = executor.submit(self._smart_probe, target, timeout)
348
+ try:
349
+ result, effective_url = future.result(timeout=hard_timeout)
350
+ except FuturesTimeout:
351
+ # Hard timeout hit - server is unresponsive
352
+ result = {
353
+ "error": f"Timeout: server did not respond within {hard_timeout}s",
354
+ "status_code": None,
355
+ "server": None,
356
+ "waf": [],
357
+ "cdn": [],
358
+ "managed_hosting": None,
359
+ "technologies": [],
360
+ "headers": {},
361
+ "cookies": [],
362
+ "tls": None,
363
+ "redirect_url": None,
364
+ }
365
+ effective_url = target
366
+
342
367
  output = self._format_output(effective_url, result, label)
343
368
 
344
369
  if log_path:
345
370
  with open(log_path, "a", encoding="utf-8", errors="replace") as fh:
346
371
  fh.write(output)
347
- # Fetch robots.txt and sitemap.xml for path discovery
348
- robots_paths, sitemap_paths = self._fetch_robots_sitemap(
349
- effective_url, timeout
350
- )
351
- result["robots_paths"] = robots_paths
352
- result["sitemap_paths"] = sitemap_paths
353
-
354
- # Quick path probing for CMS, admin panels, API endpoints
355
- quick_probe = self._quick_path_probe(effective_url, timeout)
356
- result["cms_detected"] = quick_probe.get("cms")
357
- result["admin_panels"] = quick_probe.get("admin_panels", [])
358
- result["api_endpoints"] = quick_probe.get("api_endpoints", [])
359
-
360
- # Write additional detections to log
361
- if quick_probe.get("cms"):
362
- cms = quick_probe["cms"]
363
- fh.write(f"\n{'=' * 40}\n")
364
- fh.write(
365
- f"CMS DETECTED: {cms['name']} ({cms['confidence']} confidence)\n"
366
- )
367
- for p in cms["paths"]:
368
- fh.write(f" - {p['path']} (HTTP {p['status']})\n")
369
- fh.write(f"{'=' * 40}\n")
370
372
 
371
- if quick_probe.get("admin_panels"):
372
- fh.write(f"\nADMIN PANELS FOUND:\n")
373
- for panel in quick_probe["admin_panels"]:
373
+ # Skip additional probing if initial fingerprint failed
374
+ if not result.get("error"):
375
+ # Fetch robots.txt and sitemap.xml for path discovery
376
+ robots_paths, sitemap_paths = self._fetch_robots_sitemap(
377
+ effective_url, timeout
378
+ )
379
+ result["robots_paths"] = robots_paths
380
+ result["sitemap_paths"] = sitemap_paths
381
+
382
+ # Quick path probing for CMS, admin panels, API endpoints
383
+ quick_probe = self._quick_path_probe(effective_url, timeout)
384
+ result["cms_detected"] = quick_probe.get("cms")
385
+ result["admin_panels"] = quick_probe.get("admin_panels", [])
386
+ result["api_endpoints"] = quick_probe.get("api_endpoints", [])
387
+
388
+ # Write additional detections to log
389
+ if quick_probe.get("cms"):
390
+ cms = quick_probe["cms"]
391
+ fh.write(f"\n{'=' * 40}\n")
374
392
  fh.write(
375
- f" - {panel['name']}: {panel['url']} (HTTP {panel['status']})\n"
393
+ f"CMS DETECTED: {cms['name']} ({cms['confidence']} confidence)\n"
376
394
  )
395
+ for p in cms["paths"]:
396
+ fh.write(f" - {p['path']} (HTTP {p['status']})\n")
397
+ fh.write(f"{'=' * 40}\n")
398
+
399
+ if quick_probe.get("admin_panels"):
400
+ fh.write(f"\nADMIN PANELS FOUND:\n")
401
+ for panel in quick_probe["admin_panels"]:
402
+ fh.write(
403
+ f" - {panel['name']}: {panel['url']} (HTTP {panel['status']})\n"
404
+ )
377
405
 
378
- if quick_probe.get("api_endpoints"):
379
- fh.write(f"\nAPI ENDPOINTS FOUND:\n")
380
- for api in quick_probe["api_endpoints"]:
381
- fh.write(
382
- f" - {api['type']}: {api['url']} (HTTP {api['status']})\n"
383
- )
406
+ if quick_probe.get("api_endpoints"):
407
+ fh.write(f"\nAPI ENDPOINTS FOUND:\n")
408
+ for api in quick_probe["api_endpoints"]:
409
+ fh.write(
410
+ f" - {api['type']}: {api['url']} (HTTP {api['status']})\n"
411
+ )
384
412
 
385
413
  # Write JSON result for parsing
386
414
  fh.write("\n\n=== JSON_RESULT ===\n")
@@ -413,8 +441,30 @@ class HttpFingerprintPlugin(PluginBase):
413
441
  tuple: (result_dict, effective_url)
414
442
  """
415
443
  parsed = urlparse(target)
444
+
445
+ # Quick connectivity check - fail fast if port isn't responding
416
446
  host = parsed.hostname
417
447
  port = parsed.port or (443 if parsed.scheme == "https" else 80)
448
+ try:
449
+ with socket.create_connection((host, port), timeout=min(timeout, 5)) as sock:
450
+ pass # Just checking if we can connect
451
+ except (socket.timeout, socket.error, OSError) as e:
452
+ # Port not responding - return error result immediately
453
+ return {
454
+ "error": f"Connection failed: {e}",
455
+ "status_code": None,
456
+ "server": None,
457
+ "waf": [],
458
+ "cdn": [],
459
+ "managed_hosting": None,
460
+ "technologies": [],
461
+ "headers": {},
462
+ "cookies": [],
463
+ "tls": None,
464
+ "redirect_url": None,
465
+ "protocol_detection": "failed",
466
+ "effective_url": target,
467
+ }, target
418
468
 
419
469
  # Build both URL variants
420
470
  http_url = (
@@ -564,6 +614,11 @@ class HttpFingerprintPlugin(PluginBase):
564
614
  import urllib.error
565
615
  import urllib.request
566
616
 
617
+ # Set global socket timeout to prevent hanging on slow/unresponsive servers
618
+ # This is a safety net - individual requests also have timeouts
619
+ old_timeout = socket.getdefaulttimeout()
620
+ socket.setdefaulttimeout(timeout + 5) # Slightly longer than request timeout
621
+
567
622
  result = {
568
623
  "server": None,
569
624
  "server_version": None,
@@ -700,6 +755,10 @@ class HttpFingerprintPlugin(PluginBase):
700
755
  except Exception as e:
701
756
  result["error"] = f"{type(e).__name__}: {e}"
702
757
 
758
+ finally:
759
+ # Restore original socket timeout
760
+ socket.setdefaulttimeout(old_timeout)
761
+
703
762
  return result
704
763
 
705
764
  def _detect_waf(self, headers: Dict[str, str], cookies: List[str]) -> List[str]:
@@ -188,6 +188,44 @@ class WpscanPlugin(PluginBase):
188
188
  base = urlunparse((parsed.scheme, parsed.netloc, path, "", "", ""))
189
189
  return base
190
190
 
191
+ def _fix_enumerate_args(self, args: List[str]) -> List[str]:
192
+ """
193
+ Fix incompatible WPScan enumerate options.
194
+
195
+ WPScan has mutually exclusive options:
196
+ - vp (vulnerable plugins) and ap (all plugins) cannot be used together
197
+ - vt (vulnerable themes) and at (all themes) cannot be used together
198
+
199
+ If both are present, prefer the vulnerable-only option (vp/vt) as it's
200
+ faster and more focused.
201
+ """
202
+ new_args = []
203
+ i = 0
204
+ while i < len(args):
205
+ arg = args[i]
206
+ if arg == "--enumerate" and i + 1 < len(args):
207
+ enum_value = args[i + 1]
208
+ # Parse the enumerate options
209
+ options = [opt.strip() for opt in enum_value.split(",")]
210
+
211
+ # Fix incompatible options
212
+ # If both vp and ap, remove ap (prefer vulnerable-only)
213
+ if "vp" in options and "ap" in options:
214
+ options.remove("ap")
215
+ # If both vt and at, remove at (prefer vulnerable-only)
216
+ if "vt" in options and "at" in options:
217
+ options.remove("at")
218
+
219
+ # Rebuild enumerate value
220
+ new_args.append("--enumerate")
221
+ new_args.append(",".join(options))
222
+ i += 2
223
+ else:
224
+ new_args.append(arg)
225
+ i += 1
226
+
227
+ return new_args
228
+
191
229
  def build_command(
192
230
  self, target: str, args: List[str] = None, label: str = "", log_path: str = None
193
231
  ):
@@ -217,6 +255,11 @@ class WpscanPlugin(PluginBase):
217
255
 
218
256
  args = args or []
219
257
 
258
+ # Fix incompatible enumerate options (vp/ap, vt/at are mutually exclusive)
259
+ # vp = vulnerable plugins, ap = all plugins (can't use both)
260
+ # vt = vulnerable themes, at = all themes (can't use both)
261
+ args = self._fix_enumerate_args(args)
262
+
220
263
  # Add --disable-tls-checks for HTTPS targets (handles self-signed certs)
221
264
  if target.startswith("https://") and "--disable-tls-checks" not in args:
222
265
  args = ["--disable-tls-checks"] + args
@@ -268,6 +311,9 @@ class WpscanPlugin(PluginBase):
268
311
  if args is None:
269
312
  args = []
270
313
 
314
+ # Fix incompatible enumerate options (vp/ap, vt/at are mutually exclusive)
315
+ args = self._fix_enumerate_args(args)
316
+
271
317
  # Add --disable-tls-checks for HTTPS targets (handles self-signed certs)
272
318
  if target.startswith("https://") and "--disable-tls-checks" not in args:
273
319
  args = ["--disable-tls-checks"] + args
@@ -608,6 +608,20 @@ def validate_target_or_url(target: str) -> str:
608
608
  except ValidationError:
609
609
  pass
610
610
 
611
+ # Check if input looks like an IP address attempt
612
+ # Patterns: dot-separated numbers, or starts with numbers and dots
613
+ # If so, don't allow fallthrough to hostname - it's an invalid IP
614
+ ip_like_pattern = re.compile(r"^-?\d+(\.\d+)+$") # All numeric octets
615
+ mixed_ip_pattern = re.compile(
616
+ r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.[a-zA-Z]"
617
+ ) # 3 numeric octets + letters
618
+ if ip_like_pattern.match(target) or mixed_ip_pattern.match(target):
619
+ # Looks like an IP but failed IP validation - reject it
620
+ raise ValidationError(
621
+ f"Invalid IP address: '{target}'. "
622
+ "Each octet must be 0-255 (e.g., 192.168.1.1)"
623
+ )
624
+
611
625
  # Try hostname (more lenient pattern for domains)
612
626
  # Allow single-part hostnames and domains
613
627
  hostname_pattern = re.compile(
@@ -14747,20 +14747,26 @@ def view_job_detail(job_id: int):
14747
14747
  actions.append("n")
14748
14748
 
14749
14749
  # [s] Spawn shell (for jobs with admin credentials)
14750
- # Supported: evil_winrm, crackmapexec (Pwn3d!), nxc (Pwn3d!), secretsdump, psexec
14750
+ # Supported: evil_winrm, crackmapexec (Pwn3d!), nxc (Pwn3d! or SSH Shell access!), secretsdump, psexec
14751
14751
  can_spawn_shell = False
14752
+ is_ssh_shell = False # Track if this is an SSH shell (for sshpass)
14752
14753
  tool_name = job.get("tool", "")
14753
14754
 
14754
14755
  if tool_name == "evil_winrm" and job.get("status") == "done":
14755
14756
  if parse_result and parse_result.get("success"):
14756
14757
  can_spawn_shell = True
14757
14758
  elif tool_name in ["crackmapexec", "nxc"] and job.get("status") == "done":
14758
- # Check if Pwn3d! in log (works for both crackmapexec and nxc)
14759
+ # Check if Pwn3d! or SSH Shell access! in log
14759
14760
  if log_path and os.path.exists(log_path):
14760
14761
  try:
14761
14762
  with open(log_path, "r", encoding="utf-8", errors="replace") as f:
14762
- if "Pwn3d!" in f.read():
14763
+ log_content = f.read()
14764
+ if "Pwn3d!" in log_content:
14765
+ can_spawn_shell = True
14766
+ elif "Shell access!" in log_content:
14767
+ # SSH shell access (Linux) - use sshpass
14763
14768
  can_spawn_shell = True
14769
+ is_ssh_shell = True
14764
14770
  except Exception:
14765
14771
  pass
14766
14772
  elif tool_name == "impacket-secretsdump" and job.get("status") == "done":
@@ -14769,6 +14775,39 @@ def view_job_detail(job_id: int):
14769
14775
  elif tool_name == "impacket-psexec" and job.get("status") == "done":
14770
14776
  # psexec with done status means we have working shell access
14771
14777
  can_spawn_shell = True
14778
+ elif tool_name == "msf_auxiliary" and job.get("status") == "done":
14779
+ # Check if msf_auxiliary found SSH or telnet credentials
14780
+ if parse_result:
14781
+ creds = parse_result.get("credentials", [])
14782
+ # Check for SSH or telnet credentials
14783
+ for cred in creds if isinstance(creds, list) else []:
14784
+ service = cred.get("service", "").lower() if isinstance(cred, dict) else ""
14785
+ if service in ["ssh", "telnet"]:
14786
+ can_spawn_shell = True
14787
+ if service == "ssh":
14788
+ is_ssh_shell = True
14789
+ break
14790
+ elif tool_name == "hydra" and job.get("status") == "done":
14791
+ # Hydra found valid credentials - check if SSH or telnet
14792
+ if parse_result:
14793
+ service = parse_result.get("service", "").lower()
14794
+ creds = parse_result.get("credentials", [])
14795
+ if service in ["ssh", "telnet", "ftp"] and creds:
14796
+ can_spawn_shell = True
14797
+ if service == "ssh":
14798
+ is_ssh_shell = True
14799
+ # Also check log for session opened
14800
+ if not can_spawn_shell and log_path and os.path.exists(log_path):
14801
+ try:
14802
+ with open(log_path, "r", encoding="utf-8", errors="replace") as f:
14803
+ log_content = f.read()
14804
+ if "session" in log_content.lower() and "opened" in log_content.lower():
14805
+ can_spawn_shell = True
14806
+ # Check if SSH or telnet
14807
+ if "ssh" in log_content.lower():
14808
+ is_ssh_shell = True
14809
+ except Exception:
14810
+ pass
14772
14811
 
14773
14812
  if can_spawn_shell:
14774
14813
  click.echo(
@@ -14986,6 +15025,37 @@ def view_job_detail(job_id: int):
14986
15025
  if not target:
14987
15026
  target = match.group(4)
14988
15027
 
15028
+ elif tool_name == "msf_auxiliary":
15029
+ # Get credentials from parse_result
15030
+ if parse_result:
15031
+ creds = parse_result.get("credentials", [])
15032
+ if isinstance(creds, list) and creds:
15033
+ # Find first SSH or telnet credential
15034
+ for cred in creds:
15035
+ if isinstance(cred, dict):
15036
+ service = cred.get("service", "").lower()
15037
+ if service in ["ssh", "telnet"]:
15038
+ username = cred.get("username")
15039
+ password = cred.get("password")
15040
+ break
15041
+ # If no SSH/telnet, use first credential
15042
+ if not username and creds:
15043
+ first_cred = creds[0]
15044
+ if isinstance(first_cred, dict):
15045
+ username = first_cred.get("username")
15046
+ password = first_cred.get("password")
15047
+
15048
+ elif tool_name == "hydra":
15049
+ # Get credentials from parse_result
15050
+ if parse_result:
15051
+ creds = parse_result.get("credentials", [])
15052
+ if isinstance(creds, list) and creds:
15053
+ # Use first credential
15054
+ first_cred = creds[0]
15055
+ if isinstance(first_cred, dict):
15056
+ username = first_cred.get("username") or first_cred.get("login")
15057
+ password = first_cred.get("password")
15058
+
14989
15059
  if not username or (not password and not nt_hash):
14990
15060
  click.echo(
14991
15061
  click.style(
@@ -15025,47 +15095,183 @@ def view_job_detail(job_id: int):
15025
15095
  f"impacket-psexec '{cred_prefix}:{password}@{target}'"
15026
15096
  )
15027
15097
 
15028
- else:
15029
- # For credential jobs (crackmapexec, nxc, secretsdump) - show menu
15030
- click.echo()
15031
- click.echo(click.style("=" * 70, fg="green"))
15032
- click.echo(click.style("SPAWN SHELL", bold=True, fg="green"))
15033
- click.echo(click.style("=" * 70, fg="green"))
15034
- click.echo()
15035
- click.echo(f" Target: {target}")
15036
- click.echo(f" User: {cred_prefix}")
15037
- click.echo()
15038
- click.echo(" [1] evil-winrm (WinRM - port 5985)")
15039
- click.echo(" [2] psexec (SMB - port 445)")
15040
- click.echo(" [q] Cancel")
15041
- click.echo()
15098
+ elif tool_name == "msf_auxiliary":
15099
+ # msf_auxiliary found credentials - determine service type
15100
+ service_type = None
15101
+ if parse_result:
15102
+ creds = parse_result.get("credentials", [])
15103
+ for cred in creds if isinstance(creds, list) else []:
15104
+ if isinstance(cred, dict):
15105
+ service_type = cred.get("service", "").lower()
15106
+ if service_type in ["ssh", "telnet"]:
15107
+ break
15042
15108
 
15043
- shell_choice = click.prompt(
15044
- "Select shell type", type=str, default="2"
15045
- ).strip()
15109
+ if service_type == "ssh" or is_ssh_shell:
15110
+ # SSH - use sshpass
15111
+ shell_cmd = f"sshpass -p '{password}' ssh -o StrictHostKeyChecking=no -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa {username}@{target}"
15112
+ elif service_type == "telnet":
15113
+ # Telnet - show command to run manually (telnet doesn't support password on cmdline easily)
15114
+ click.echo()
15115
+ click.echo(click.style("=" * 70, fg="green"))
15116
+ click.echo(click.style("TELNET SHELL", bold=True, fg="green"))
15117
+ click.echo(click.style("=" * 70, fg="green"))
15118
+ click.echo()
15119
+ click.echo(f" Target: {target}")
15120
+ click.echo(f" User: {username}")
15121
+ click.echo(f" Pass: {password}")
15122
+ click.echo()
15123
+ click.echo(" Launching telnet... Enter password when prompted.")
15124
+ click.echo()
15125
+ shell_cmd = f"telnet {target}"
15126
+ else:
15127
+ # Unknown service - show menu
15128
+ click.echo()
15129
+ click.echo(click.style("=" * 70, fg="green"))
15130
+ click.echo(click.style("SPAWN SHELL", bold=True, fg="green"))
15131
+ click.echo(click.style("=" * 70, fg="green"))
15132
+ click.echo()
15133
+ click.echo(f" Target: {target}")
15134
+ click.echo(f" User: {username}")
15135
+ click.echo()
15136
+ click.echo(" [1] ssh (SSH - port 22)")
15137
+ click.echo(" [2] telnet (Telnet - port 23)")
15138
+ click.echo(" [q] Cancel")
15139
+ click.echo()
15046
15140
 
15047
- if shell_choice == "q":
15048
- continue
15141
+ shell_choice = click.prompt(
15142
+ "Select shell type", type=str, default="1"
15143
+ ).strip()
15049
15144
 
15050
- if shell_choice == "1":
15051
- # evil-winrm
15052
- if nt_hash:
15053
- shell_cmd = (
15054
- f"evil-winrm -i {target} -u '{username}' -H '{nt_hash}'"
15055
- )
15145
+ if shell_choice == "q":
15146
+ continue
15147
+ elif shell_choice == "1":
15148
+ shell_cmd = f"sshpass -p '{password}' ssh -o StrictHostKeyChecking=no -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa {username}@{target}"
15149
+ elif shell_choice == "2":
15150
+ click.echo(f"\n Password: {password}")
15151
+ shell_cmd = f"telnet {target}"
15056
15152
  else:
15057
- shell_cmd = f"evil-winrm -i {target} -u '{username}' -p '{password}'"
15058
- elif shell_choice == "2":
15059
- # psexec
15060
- if nt_hash:
15061
- shell_cmd = f"impacket-psexec '{cred_prefix}@{target}' -hashes ':{nt_hash}'"
15153
+ click.echo(click.style(" Invalid choice", fg="red"))
15154
+ continue
15155
+
15156
+ elif tool_name == "hydra":
15157
+ # Hydra found valid credentials - determine service type
15158
+ service_type = parse_result.get("service", "").lower() if parse_result else ""
15159
+
15160
+ if service_type == "ssh" or is_ssh_shell:
15161
+ # SSH - use sshpass
15162
+ shell_cmd = f"sshpass -p '{password}' ssh -o StrictHostKeyChecking=no -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa {username}@{target}"
15163
+ elif service_type == "telnet":
15164
+ # Telnet
15165
+ click.echo()
15166
+ click.echo(click.style("=" * 70, fg="green"))
15167
+ click.echo(click.style("TELNET SHELL", bold=True, fg="green"))
15168
+ click.echo(click.style("=" * 70, fg="green"))
15169
+ click.echo()
15170
+ click.echo(f" Target: {target}")
15171
+ click.echo(f" User: {username}")
15172
+ click.echo(f" Pass: {password}")
15173
+ click.echo()
15174
+ click.echo(" Launching telnet... Enter password when prompted.")
15175
+ click.echo()
15176
+ shell_cmd = f"telnet {target}"
15177
+ elif service_type == "ftp":
15178
+ # FTP
15179
+ click.echo()
15180
+ click.echo(click.style("=" * 70, fg="green"))
15181
+ click.echo(click.style("FTP SHELL", bold=True, fg="green"))
15182
+ click.echo(click.style("=" * 70, fg="green"))
15183
+ click.echo()
15184
+ click.echo(f" Target: {target}")
15185
+ click.echo(f" User: {username}")
15186
+ click.echo(f" Pass: {password}")
15187
+ click.echo()
15188
+ shell_cmd = f"ftp {target}"
15189
+ else:
15190
+ # Unknown service - show menu
15191
+ click.echo()
15192
+ click.echo(click.style("=" * 70, fg="green"))
15193
+ click.echo(click.style("SPAWN SHELL", bold=True, fg="green"))
15194
+ click.echo(click.style("=" * 70, fg="green"))
15195
+ click.echo()
15196
+ click.echo(f" Target: {target}")
15197
+ click.echo(f" User: {username}")
15198
+ click.echo(f" Service: {service_type or 'unknown'}")
15199
+ click.echo()
15200
+ click.echo(" [1] ssh (SSH - port 22)")
15201
+ click.echo(" [2] telnet (Telnet - port 23)")
15202
+ click.echo(" [3] ftp (FTP - port 21)")
15203
+ click.echo(" [q] Cancel")
15204
+ click.echo()
15205
+
15206
+ shell_choice = click.prompt(
15207
+ "Select shell type", type=str, default="1"
15208
+ ).strip()
15209
+
15210
+ if shell_choice == "q":
15211
+ continue
15212
+ elif shell_choice == "1":
15213
+ shell_cmd = f"sshpass -p '{password}' ssh -o StrictHostKeyChecking=no -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa {username}@{target}"
15214
+ elif shell_choice == "2":
15215
+ click.echo(f"\n Password: {password}")
15216
+ shell_cmd = f"telnet {target}"
15217
+ elif shell_choice == "3":
15218
+ click.echo(f"\n Password: {password}")
15219
+ shell_cmd = f"ftp {target}"
15062
15220
  else:
15063
- shell_cmd = (
15064
- f"impacket-psexec '{cred_prefix}:{password}@{target}'"
15065
- )
15221
+ click.echo(click.style(" Invalid choice", fg="red"))
15222
+ continue
15223
+
15224
+ else:
15225
+ # For credential jobs (crackmapexec, nxc, secretsdump) - show menu
15226
+ # Check if this is an SSH shell (set by detection logic above)
15227
+ if is_ssh_shell:
15228
+ # SSH shell - use sshpass directly
15229
+ shell_cmd = f"sshpass -p '{password}' ssh -o StrictHostKeyChecking=no -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa {username}@{target}"
15066
15230
  else:
15067
- click.echo(click.style(" Invalid choice", fg="red"))
15068
- continue
15231
+ # Windows shell options menu
15232
+ click.echo()
15233
+ click.echo(click.style("=" * 70, fg="green"))
15234
+ click.echo(click.style("SPAWN SHELL", bold=True, fg="green"))
15235
+ click.echo(click.style("=" * 70, fg="green"))
15236
+ click.echo()
15237
+ click.echo(f" Target: {target}")
15238
+ click.echo(f" User: {cred_prefix}")
15239
+ click.echo()
15240
+ click.echo(" [1] evil-winrm (WinRM - port 5985)")
15241
+ click.echo(" [2] psexec (SMB - port 445)")
15242
+ click.echo(" [3] ssh (SSH - port 22, requires sshpass)")
15243
+ click.echo(" [q] Cancel")
15244
+ click.echo()
15245
+
15246
+ shell_choice = click.prompt(
15247
+ "Select shell type", type=str, default="2"
15248
+ ).strip()
15249
+
15250
+ if shell_choice == "q":
15251
+ continue
15252
+
15253
+ if shell_choice == "1":
15254
+ # evil-winrm
15255
+ if nt_hash:
15256
+ shell_cmd = (
15257
+ f"evil-winrm -i {target} -u '{username}' -H '{nt_hash}'"
15258
+ )
15259
+ else:
15260
+ shell_cmd = f"evil-winrm -i {target} -u '{username}' -p '{password}'"
15261
+ elif shell_choice == "2":
15262
+ # psexec
15263
+ if nt_hash:
15264
+ shell_cmd = f"impacket-psexec '{cred_prefix}@{target}' -hashes ':{nt_hash}'"
15265
+ else:
15266
+ shell_cmd = (
15267
+ f"impacket-psexec '{cred_prefix}:{password}@{target}'"
15268
+ )
15269
+ elif shell_choice == "3":
15270
+ # SSH with sshpass
15271
+ shell_cmd = f"sshpass -p '{password}' ssh -o StrictHostKeyChecking=no -o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostKeyAlgorithms=+ssh-rsa {username}@{target}"
15272
+ else:
15273
+ click.echo(click.style(" Invalid choice", fg="red"))
15274
+ continue
15069
15275
 
15070
15276
  if not shell_cmd:
15071
15277
  click.echo(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: souleyez
3
- Version: 2.43.34
3
+ Version: 3.0.0
4
4
  Summary: AI-Powered Penetration Testing Platform with 40+ integrated tools
5
5
  Author-email: CyberSoul Security <contact@cybersoulsecurity.com>
6
6
  Maintainer-email: CyberSoul Security <contact@cybersoulsecurity.com>
@@ -10,7 +10,7 @@ Project-URL: Documentation, https://github.com/cyber-soul-security/SoulEyez#read
10
10
  Project-URL: Repository, https://github.com/cyber-soul-security/SoulEyez.git
11
11
  Project-URL: Issues, https://github.com/cyber-soul-security/SoulEyez/issues
12
12
  Keywords: pentesting,security,hacking,penetration-testing,cybersecurity,nmap,metasploit
13
- Classifier: Development Status :: 4 - Beta
13
+ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Environment :: Console :: Curses
16
16
  Classifier: Intended Audience :: Developers