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.
- souleyez/__init__.py +1 -1
- souleyez/core/tool_chaining.py +219 -183
- souleyez/docs/README.md +2 -2
- souleyez/handlers/hydra_handler.py +1 -0
- souleyez/handlers/msf_auxiliary_handler.py +1 -0
- souleyez/handlers/nxc_handler.py +47 -1
- souleyez/main.py +1 -1
- souleyez/plugins/http_fingerprint.py +94 -35
- souleyez/plugins/wpscan.py +46 -0
- souleyez/security/validation.py +14 -0
- souleyez/ui/interactive.py +244 -38
- {souleyez-2.43.34.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
- {souleyez-2.43.34.dist-info → souleyez-3.0.0.dist-info}/RECORD +17 -17
- {souleyez-2.43.34.dist-info → souleyez-3.0.0.dist-info}/WHEEL +0 -0
- {souleyez-2.43.34.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.34.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.34.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -337,50 +337,78 @@ class HttpFingerprintPlugin(PluginBase):
|
|
|
337
337
|
target = f"http://{target}"
|
|
338
338
|
|
|
339
339
|
try:
|
|
340
|
-
#
|
|
341
|
-
|
|
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
|
|
372
|
-
|
|
373
|
-
for
|
|
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"
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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]:
|
souleyez/plugins/wpscan.py
CHANGED
|
@@ -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
|
souleyez/security/validation.py
CHANGED
|
@@ -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(
|
souleyez/ui/interactive.py
CHANGED
|
@@ -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!
|
|
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
|
-
|
|
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
|
-
|
|
15029
|
-
#
|
|
15030
|
-
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
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
|
-
|
|
15044
|
-
|
|
15045
|
-
|
|
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
|
-
|
|
15048
|
-
|
|
15141
|
+
shell_choice = click.prompt(
|
|
15142
|
+
"Select shell type", type=str, default="1"
|
|
15143
|
+
).strip()
|
|
15049
15144
|
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
shell_cmd =
|
|
15054
|
-
|
|
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
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15060
|
-
|
|
15061
|
-
|
|
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
|
-
|
|
15064
|
-
|
|
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
|
-
|
|
15068
|
-
|
|
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:
|
|
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 ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Environment :: Console :: Curses
|
|
16
16
|
Classifier: Intended Audience :: Developers
|