souleyez 2.43.24__py3-none-any.whl → 2.43.29__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.
- souleyez/__init__.py +1 -1
- souleyez/docs/README.md +2 -2
- souleyez/main.py +1 -1
- souleyez/ui/interactive.py +1304 -1
- {souleyez-2.43.24.dist-info → souleyez-2.43.29.dist-info}/METADATA +1 -1
- {souleyez-2.43.24.dist-info → souleyez-2.43.29.dist-info}/RECORD +10 -10
- {souleyez-2.43.24.dist-info → souleyez-2.43.29.dist-info}/WHEEL +0 -0
- {souleyez-2.43.24.dist-info → souleyez-2.43.29.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.24.dist-info → souleyez-2.43.29.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.24.dist-info → souleyez-2.43.29.dist-info}/top_level.txt +0 -0
souleyez/ui/interactive.py
CHANGED
|
@@ -5883,7 +5883,6 @@ def view_job_detail(job_id: int):
|
|
|
5883
5883
|
args = job.get('args', [])
|
|
5884
5884
|
for i, arg in enumerate(args):
|
|
5885
5885
|
if arg == '-w' and i + 1 < len(args):
|
|
5886
|
-
import os
|
|
5887
5886
|
wordlist = os.path.basename(args[i + 1])
|
|
5888
5887
|
click.echo(f" Wordlist: {wordlist}")
|
|
5889
5888
|
break
|
|
@@ -5907,6 +5906,48 @@ def view_job_detail(job_id: int):
|
|
|
5907
5906
|
# Silently fail - not critical
|
|
5908
5907
|
pass
|
|
5909
5908
|
|
|
5909
|
+
# SQLMap ERROR handler
|
|
5910
|
+
if not show_raw_logs and job.get('tool') == 'sqlmap' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
5911
|
+
try:
|
|
5912
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
5913
|
+
log_text = f.read()
|
|
5914
|
+
|
|
5915
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
5916
|
+
click.echo(click.style("[ERROR] SQLMAP SCAN FAILED", bold=True, fg='red'))
|
|
5917
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
5918
|
+
click.echo()
|
|
5919
|
+
|
|
5920
|
+
# Check for common sqlmap errors
|
|
5921
|
+
error_msg = None
|
|
5922
|
+
if 'connection timed out' in log_text.lower():
|
|
5923
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
5924
|
+
elif 'unable to connect' in log_text.lower():
|
|
5925
|
+
error_msg = "Unable to connect to target URL"
|
|
5926
|
+
elif 'page not found' in log_text.lower() or '404' in log_text:
|
|
5927
|
+
error_msg = "Target page not found (404)"
|
|
5928
|
+
elif 'invalid target url' in log_text.lower():
|
|
5929
|
+
error_msg = "Invalid target URL - check the URL format"
|
|
5930
|
+
elif 'WAF/IPS' in log_text or 'firewall' in log_text.lower():
|
|
5931
|
+
error_msg = "WAF/IPS detected - try --tamper scripts"
|
|
5932
|
+
elif 'all tested parameters do not appear' in log_text.lower():
|
|
5933
|
+
error_msg = "No injectable parameters found"
|
|
5934
|
+
elif '[CRITICAL]' in log_text:
|
|
5935
|
+
match = re.search(r'\[CRITICAL\]\s*(.+?)(?:\n|$)', log_text)
|
|
5936
|
+
if match:
|
|
5937
|
+
error_msg = match.group(1).strip()[:100]
|
|
5938
|
+
|
|
5939
|
+
if error_msg:
|
|
5940
|
+
click.echo(f" {error_msg}")
|
|
5941
|
+
else:
|
|
5942
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
5943
|
+
|
|
5944
|
+
click.echo()
|
|
5945
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
5946
|
+
click.echo()
|
|
5947
|
+
|
|
5948
|
+
except Exception:
|
|
5949
|
+
pass
|
|
5950
|
+
|
|
5910
5951
|
# Parse and display SQLMap results if available (only when not showing raw logs)
|
|
5911
5952
|
if not show_raw_logs and job.get('tool') == 'sqlmap' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
5912
5953
|
try:
|
|
@@ -6098,6 +6139,48 @@ def view_job_detail(job_id: int):
|
|
|
6098
6139
|
import traceback
|
|
6099
6140
|
traceback.print_exc()
|
|
6100
6141
|
|
|
6142
|
+
# WPScan ERROR handler
|
|
6143
|
+
if not show_raw_logs and job.get('tool') == 'wpscan' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
6144
|
+
try:
|
|
6145
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
6146
|
+
log_text = f.read()
|
|
6147
|
+
|
|
6148
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6149
|
+
click.echo(click.style("[ERROR] WPSCAN FAILED", bold=True, fg='red'))
|
|
6150
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6151
|
+
click.echo()
|
|
6152
|
+
|
|
6153
|
+
# Check for common wpscan errors
|
|
6154
|
+
error_msg = None
|
|
6155
|
+
if 'The target is NOT running WordPress' in log_text:
|
|
6156
|
+
error_msg = "Target is not running WordPress"
|
|
6157
|
+
elif 'could not resolve' in log_text.lower():
|
|
6158
|
+
error_msg = "Could not resolve target hostname"
|
|
6159
|
+
elif 'Connection refused' in log_text or 'Unable to connect' in log_text:
|
|
6160
|
+
error_msg = "Connection refused - web server may be down"
|
|
6161
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
6162
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
6163
|
+
elif 'SSL' in log_text and ('error' in log_text.lower() or 'fail' in log_text.lower()):
|
|
6164
|
+
error_msg = "SSL error - try with --disable-tls-checks"
|
|
6165
|
+
elif 'API limit' in log_text.lower() or 'rate limit' in log_text.lower():
|
|
6166
|
+
error_msg = "WPScan API rate limit reached - try again later"
|
|
6167
|
+
elif '[!]' in log_text:
|
|
6168
|
+
match = re.search(r'\[!\]\s*(.+?)(?:\n|$)', log_text)
|
|
6169
|
+
if match:
|
|
6170
|
+
error_msg = match.group(1).strip()[:100]
|
|
6171
|
+
|
|
6172
|
+
if error_msg:
|
|
6173
|
+
click.echo(f" {error_msg}")
|
|
6174
|
+
else:
|
|
6175
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
6176
|
+
|
|
6177
|
+
click.echo()
|
|
6178
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6179
|
+
click.echo()
|
|
6180
|
+
|
|
6181
|
+
except Exception:
|
|
6182
|
+
pass
|
|
6183
|
+
|
|
6101
6184
|
# Parse and display WPScan results if available (only when not showing raw logs)
|
|
6102
6185
|
if not show_raw_logs and job.get('tool') == 'wpscan' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6103
6186
|
try:
|
|
@@ -6291,6 +6374,46 @@ def view_job_detail(job_id: int):
|
|
|
6291
6374
|
# Silently fail - not critical
|
|
6292
6375
|
pass
|
|
6293
6376
|
|
|
6377
|
+
# DNSRecon ERROR handler
|
|
6378
|
+
if not show_raw_logs and job.get('tool') == 'dnsrecon' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
6379
|
+
try:
|
|
6380
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
6381
|
+
log_text = f.read()
|
|
6382
|
+
|
|
6383
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6384
|
+
click.echo(click.style("[ERROR] DNSRECON FAILED", bold=True, fg='red'))
|
|
6385
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6386
|
+
click.echo()
|
|
6387
|
+
|
|
6388
|
+
# Check for common dnsrecon errors
|
|
6389
|
+
error_msg = None
|
|
6390
|
+
if 'Could not resolve' in log_text or 'NXDOMAIN' in log_text:
|
|
6391
|
+
error_msg = "Could not resolve domain - check if domain exists"
|
|
6392
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
6393
|
+
error_msg = "DNS query timed out - DNS server may be slow"
|
|
6394
|
+
elif 'SERVFAIL' in log_text:
|
|
6395
|
+
error_msg = "DNS server failure (SERVFAIL)"
|
|
6396
|
+
elif 'REFUSED' in log_text:
|
|
6397
|
+
error_msg = "DNS query refused - server may be blocking queries"
|
|
6398
|
+
elif 'No DNS records' in log_text:
|
|
6399
|
+
error_msg = "No DNS records found for domain"
|
|
6400
|
+
elif '[-]' in log_text:
|
|
6401
|
+
match = re.search(r'\[-\]\s*(.+?)(?:\n|$)', log_text)
|
|
6402
|
+
if match:
|
|
6403
|
+
error_msg = match.group(1).strip()[:100]
|
|
6404
|
+
|
|
6405
|
+
if error_msg:
|
|
6406
|
+
click.echo(f" {error_msg}")
|
|
6407
|
+
else:
|
|
6408
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
6409
|
+
|
|
6410
|
+
click.echo()
|
|
6411
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6412
|
+
click.echo()
|
|
6413
|
+
|
|
6414
|
+
except Exception:
|
|
6415
|
+
pass
|
|
6416
|
+
|
|
6294
6417
|
# Parse and display DNSRecon results if available (only when not showing raw logs)
|
|
6295
6418
|
if not show_raw_logs and job.get('tool') == 'dnsrecon' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6296
6419
|
try:
|
|
@@ -6379,6 +6502,78 @@ def view_job_detail(job_id: int):
|
|
|
6379
6502
|
# Parse and display Nmap results if available (only when not showing raw logs)
|
|
6380
6503
|
# ARD plugin uses nmap under the hood, so include it here
|
|
6381
6504
|
nmap_based_tools = ['nmap', 'ard']
|
|
6505
|
+
|
|
6506
|
+
# Nmap ERROR handler
|
|
6507
|
+
if not show_raw_logs and job.get('tool') in nmap_based_tools and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
6508
|
+
try:
|
|
6509
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
6510
|
+
log_text = f.read()
|
|
6511
|
+
|
|
6512
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6513
|
+
click.echo(click.style("[ERROR] NMAP SCAN FAILED", bold=True, fg='red'))
|
|
6514
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6515
|
+
click.echo()
|
|
6516
|
+
|
|
6517
|
+
# Check for common nmap errors
|
|
6518
|
+
error_msg = None
|
|
6519
|
+
if 'Failed to resolve' in log_text or 'Failed to open' in log_text:
|
|
6520
|
+
error_msg = "Failed to resolve target hostname"
|
|
6521
|
+
elif 'No targets were specified' in log_text:
|
|
6522
|
+
error_msg = "No valid targets specified"
|
|
6523
|
+
elif 'requires root privileges' in log_text or 'Operation not permitted' in log_text:
|
|
6524
|
+
error_msg = "Scan type requires root privileges (try sudo)"
|
|
6525
|
+
elif 'Host seems down' in log_text:
|
|
6526
|
+
error_msg = "Host appears to be down or blocking probes"
|
|
6527
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
6528
|
+
error_msg = "Scan timed out - target may be slow or filtering"
|
|
6529
|
+
elif 'Connection refused' in log_text:
|
|
6530
|
+
error_msg = "Connection refused - no services on target ports"
|
|
6531
|
+
|
|
6532
|
+
if error_msg:
|
|
6533
|
+
click.echo(f" {error_msg}")
|
|
6534
|
+
else:
|
|
6535
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
6536
|
+
|
|
6537
|
+
click.echo()
|
|
6538
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6539
|
+
click.echo()
|
|
6540
|
+
|
|
6541
|
+
except Exception:
|
|
6542
|
+
pass
|
|
6543
|
+
|
|
6544
|
+
# Nmap NO_RESULTS handler
|
|
6545
|
+
if not show_raw_logs and job.get('tool') in nmap_based_tools and job.get('status') == 'no_results' and log_path and os.path.exists(log_path):
|
|
6546
|
+
try:
|
|
6547
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
6548
|
+
log_text = f.read()
|
|
6549
|
+
|
|
6550
|
+
click.echo(click.style("=" * 70, fg='cyan'))
|
|
6551
|
+
click.echo(click.style("NMAP SCAN RESULTS", bold=True, fg='cyan'))
|
|
6552
|
+
click.echo(click.style("=" * 70, fg='cyan'))
|
|
6553
|
+
click.echo()
|
|
6554
|
+
click.echo(" No open ports or services discovered.")
|
|
6555
|
+
click.echo()
|
|
6556
|
+
|
|
6557
|
+
# Check for additional context
|
|
6558
|
+
if 'Host seems down' in log_text:
|
|
6559
|
+
click.echo(click.style(" Note: Host appears to be down or blocking probes", fg='yellow'))
|
|
6560
|
+
elif 'filtered' in log_text.lower():
|
|
6561
|
+
click.echo(click.style(" Note: Ports may be filtered by firewall", fg='yellow'))
|
|
6562
|
+
|
|
6563
|
+
click.echo()
|
|
6564
|
+
click.echo(click.style(" This could mean:", fg='bright_black'))
|
|
6565
|
+
click.echo(click.style(" - All ports are closed or filtered", fg='bright_black'))
|
|
6566
|
+
click.echo(click.style(" - Host is behind a firewall", fg='bright_black'))
|
|
6567
|
+
click.echo(click.style(" - Try different scan types (-sS, -sT, -sU)", fg='bright_black'))
|
|
6568
|
+
click.echo(click.style(" - Try scanning more ports (-p-)", fg='bright_black'))
|
|
6569
|
+
click.echo()
|
|
6570
|
+
click.echo(click.style("=" * 70, fg='cyan'))
|
|
6571
|
+
click.echo()
|
|
6572
|
+
|
|
6573
|
+
except Exception:
|
|
6574
|
+
pass
|
|
6575
|
+
|
|
6576
|
+
# Nmap DONE/COMPLETED handler
|
|
6382
6577
|
if not show_raw_logs and job.get('tool') in nmap_based_tools and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
6383
6578
|
try:
|
|
6384
6579
|
from souleyez.parsers.nmap_parser import parse_nmap_output
|
|
@@ -6545,6 +6740,48 @@ def view_job_detail(job_id: int):
|
|
|
6545
6740
|
except Exception as e:
|
|
6546
6741
|
pass
|
|
6547
6742
|
|
|
6743
|
+
# Nuclei ERROR handler
|
|
6744
|
+
if not show_raw_logs and job.get('tool') == 'nuclei' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
6745
|
+
try:
|
|
6746
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
6747
|
+
log_text = f.read()
|
|
6748
|
+
|
|
6749
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6750
|
+
click.echo(click.style("[ERROR] NUCLEI SCAN FAILED", bold=True, fg='red'))
|
|
6751
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6752
|
+
click.echo()
|
|
6753
|
+
|
|
6754
|
+
# Check for common nuclei errors
|
|
6755
|
+
error_msg = None
|
|
6756
|
+
if 'Could not run nuclei' in log_text or 'not found' in log_text.lower():
|
|
6757
|
+
error_msg = "Nuclei binary not found - check installation"
|
|
6758
|
+
elif 'no templates' in log_text.lower():
|
|
6759
|
+
error_msg = "No templates found - update nuclei templates"
|
|
6760
|
+
elif 'rate limit' in log_text.lower():
|
|
6761
|
+
error_msg = "Rate limited by target - try with -rl flag"
|
|
6762
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
6763
|
+
error_msg = "Scan timed out - target may be slow or filtering"
|
|
6764
|
+
elif 'Connection refused' in log_text:
|
|
6765
|
+
error_msg = "Connection refused - target may be down"
|
|
6766
|
+
elif 'could not connect' in log_text.lower():
|
|
6767
|
+
error_msg = "Could not connect to target"
|
|
6768
|
+
elif '[ERR]' in log_text or '[FTL]' in log_text:
|
|
6769
|
+
match = re.search(r'\[(ERR|FTL)\]\s*(.+?)(?:\n|$)', log_text)
|
|
6770
|
+
if match:
|
|
6771
|
+
error_msg = match.group(2).strip()[:100]
|
|
6772
|
+
|
|
6773
|
+
if error_msg:
|
|
6774
|
+
click.echo(f" {error_msg}")
|
|
6775
|
+
else:
|
|
6776
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
6777
|
+
|
|
6778
|
+
click.echo()
|
|
6779
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6780
|
+
click.echo()
|
|
6781
|
+
|
|
6782
|
+
except Exception:
|
|
6783
|
+
pass
|
|
6784
|
+
|
|
6548
6785
|
# Parse and display Nuclei results if available (only when not showing raw logs)
|
|
6549
6786
|
if not show_raw_logs and job.get('tool') == 'nuclei' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6550
6787
|
try:
|
|
@@ -6620,6 +6857,46 @@ def view_job_detail(job_id: int):
|
|
|
6620
6857
|
except Exception as e:
|
|
6621
6858
|
pass
|
|
6622
6859
|
|
|
6860
|
+
# theHarvester ERROR handler
|
|
6861
|
+
if not show_raw_logs and job.get('tool') == 'theharvester' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
6862
|
+
try:
|
|
6863
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
6864
|
+
log_text = f.read()
|
|
6865
|
+
|
|
6866
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6867
|
+
click.echo(click.style("[ERROR] THEHARVESTER FAILED", bold=True, fg='red'))
|
|
6868
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6869
|
+
click.echo()
|
|
6870
|
+
|
|
6871
|
+
# Check for common theharvester errors
|
|
6872
|
+
error_msg = None
|
|
6873
|
+
if 'No results found' in log_text:
|
|
6874
|
+
error_msg = "No results found for the specified domain"
|
|
6875
|
+
elif 'Could not resolve' in log_text or 'DNS' in log_text and 'fail' in log_text.lower():
|
|
6876
|
+
error_msg = "Could not resolve domain"
|
|
6877
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
6878
|
+
error_msg = "Connection timed out - source may be slow"
|
|
6879
|
+
elif 'rate limit' in log_text.lower() or 'blocked' in log_text.lower():
|
|
6880
|
+
error_msg = "Rate limited or blocked by source"
|
|
6881
|
+
elif 'API' in log_text and ('key' in log_text.lower() or 'error' in log_text.lower()):
|
|
6882
|
+
error_msg = "API key error - check your API keys configuration"
|
|
6883
|
+
elif '[-]' in log_text:
|
|
6884
|
+
match = re.search(r'\[-\]\s*(.+?)(?:\n|$)', log_text)
|
|
6885
|
+
if match:
|
|
6886
|
+
error_msg = match.group(1).strip()[:100]
|
|
6887
|
+
|
|
6888
|
+
if error_msg:
|
|
6889
|
+
click.echo(f" {error_msg}")
|
|
6890
|
+
else:
|
|
6891
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
6892
|
+
|
|
6893
|
+
click.echo()
|
|
6894
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6895
|
+
click.echo()
|
|
6896
|
+
|
|
6897
|
+
except Exception:
|
|
6898
|
+
pass
|
|
6899
|
+
|
|
6623
6900
|
# Parse and display theHarvester results if available (only when not showing raw logs)
|
|
6624
6901
|
if not show_raw_logs and job.get('tool') == 'theharvester' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6625
6902
|
try:
|
|
@@ -6708,6 +6985,44 @@ def view_job_detail(job_id: int):
|
|
|
6708
6985
|
# Fall back to raw log if parsing fails
|
|
6709
6986
|
pass
|
|
6710
6987
|
|
|
6988
|
+
# Nikto ERROR handler
|
|
6989
|
+
if not show_raw_logs and job.get('tool') == 'nikto' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
6990
|
+
try:
|
|
6991
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
6992
|
+
log_text = f.read()
|
|
6993
|
+
|
|
6994
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6995
|
+
click.echo(click.style("[ERROR] NIKTO SCAN FAILED", bold=True, fg='red'))
|
|
6996
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
6997
|
+
click.echo()
|
|
6998
|
+
|
|
6999
|
+
# Check for common nikto errors
|
|
7000
|
+
error_msg = None
|
|
7001
|
+
if 'Unable to connect' in log_text or 'Connection refused' in log_text:
|
|
7002
|
+
error_msg = "Unable to connect to target - check if web server is running"
|
|
7003
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7004
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
7005
|
+
elif 'No web server found' in log_text:
|
|
7006
|
+
error_msg = "No web server found on target port"
|
|
7007
|
+
elif 'SSL handshake' in log_text.lower():
|
|
7008
|
+
error_msg = "SSL handshake failed - try with/without -ssl flag"
|
|
7009
|
+
elif 'ERROR:' in log_text:
|
|
7010
|
+
match = re.search(r'ERROR:\s*(.+?)(?:\n|$)', log_text)
|
|
7011
|
+
if match:
|
|
7012
|
+
error_msg = match.group(1).strip()[:100]
|
|
7013
|
+
|
|
7014
|
+
if error_msg:
|
|
7015
|
+
click.echo(f" {error_msg}")
|
|
7016
|
+
else:
|
|
7017
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
7018
|
+
|
|
7019
|
+
click.echo()
|
|
7020
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7021
|
+
click.echo()
|
|
7022
|
+
|
|
7023
|
+
except Exception:
|
|
7024
|
+
pass
|
|
7025
|
+
|
|
6711
7026
|
# Parse and display Nikto results if available (only when not showing raw logs)
|
|
6712
7027
|
if not show_raw_logs and job.get('tool') == 'nikto' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6713
7028
|
try:
|
|
@@ -6797,6 +7112,40 @@ def view_job_detail(job_id: int):
|
|
|
6797
7112
|
# Fall back to raw log if parsing fails
|
|
6798
7113
|
pass
|
|
6799
7114
|
|
|
7115
|
+
# WHOIS ERROR handler
|
|
7116
|
+
if not show_raw_logs and job.get('tool') == 'whois' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7117
|
+
try:
|
|
7118
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7119
|
+
log_text = f.read()
|
|
7120
|
+
|
|
7121
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7122
|
+
click.echo(click.style("[ERROR] WHOIS LOOKUP FAILED", bold=True, fg='red'))
|
|
7123
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7124
|
+
click.echo()
|
|
7125
|
+
|
|
7126
|
+
# Check for common whois errors
|
|
7127
|
+
error_msg = None
|
|
7128
|
+
if 'No match for' in log_text or 'NOT FOUND' in log_text.upper():
|
|
7129
|
+
error_msg = "Domain not found in WHOIS database"
|
|
7130
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7131
|
+
error_msg = "WHOIS query timed out - server may be slow"
|
|
7132
|
+
elif 'Connection refused' in log_text:
|
|
7133
|
+
error_msg = "Connection refused - WHOIS server may be down"
|
|
7134
|
+
elif 'rate limit' in log_text.lower() or 'too many' in log_text.lower():
|
|
7135
|
+
error_msg = "Rate limited - too many WHOIS queries"
|
|
7136
|
+
|
|
7137
|
+
if error_msg:
|
|
7138
|
+
click.echo(f" {error_msg}")
|
|
7139
|
+
else:
|
|
7140
|
+
click.echo(" Lookup failed - see raw logs for details (press 'r')")
|
|
7141
|
+
|
|
7142
|
+
click.echo()
|
|
7143
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7144
|
+
click.echo()
|
|
7145
|
+
|
|
7146
|
+
except Exception:
|
|
7147
|
+
pass
|
|
7148
|
+
|
|
6800
7149
|
# Parse and display WHOIS results if available (only when not showing raw logs)
|
|
6801
7150
|
if not show_raw_logs and job.get('tool') == 'whois' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6802
7151
|
try:
|
|
@@ -6876,6 +7225,46 @@ def view_job_detail(job_id: int):
|
|
|
6876
7225
|
except Exception as e:
|
|
6877
7226
|
pass
|
|
6878
7227
|
|
|
7228
|
+
# CrackMapExec ERROR handler
|
|
7229
|
+
if not show_raw_logs and job.get('tool') == 'crackmapexec' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7230
|
+
try:
|
|
7231
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7232
|
+
log_text = f.read()
|
|
7233
|
+
|
|
7234
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7235
|
+
click.echo(click.style("[ERROR] CRACKMAPEXEC FAILED", bold=True, fg='red'))
|
|
7236
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7237
|
+
click.echo()
|
|
7238
|
+
|
|
7239
|
+
# Check for common CME errors
|
|
7240
|
+
error_msg = None
|
|
7241
|
+
if 'Connection refused' in log_text or 'Connection reset' in log_text:
|
|
7242
|
+
error_msg = "Connection refused - SMB service may be down"
|
|
7243
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7244
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
7245
|
+
elif 'STATUS_LOGON_FAILURE' in log_text:
|
|
7246
|
+
error_msg = "Authentication failed - invalid credentials"
|
|
7247
|
+
elif 'STATUS_ACCESS_DENIED' in log_text:
|
|
7248
|
+
error_msg = "Access denied - insufficient privileges"
|
|
7249
|
+
elif 'Errno 113' in log_text or 'No route to host' in log_text:
|
|
7250
|
+
error_msg = "No route to host - network unreachable"
|
|
7251
|
+
elif '[-]' in log_text:
|
|
7252
|
+
match = re.search(r'\[-\]\s*(.+?)(?:\n|$)', log_text)
|
|
7253
|
+
if match:
|
|
7254
|
+
error_msg = match.group(1).strip()[:100]
|
|
7255
|
+
|
|
7256
|
+
if error_msg:
|
|
7257
|
+
click.echo(f" {error_msg}")
|
|
7258
|
+
else:
|
|
7259
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
7260
|
+
|
|
7261
|
+
click.echo()
|
|
7262
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7263
|
+
click.echo()
|
|
7264
|
+
|
|
7265
|
+
except Exception:
|
|
7266
|
+
pass
|
|
7267
|
+
|
|
6879
7268
|
# Parse and display CrackMapExec results if available (only when not showing raw logs)
|
|
6880
7269
|
if not show_raw_logs and job.get('tool') == 'crackmapexec' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6881
7270
|
try:
|
|
@@ -6925,6 +7314,46 @@ def view_job_detail(job_id: int):
|
|
|
6925
7314
|
except Exception as e:
|
|
6926
7315
|
pass
|
|
6927
7316
|
|
|
7317
|
+
# SMBMap ERROR handler
|
|
7318
|
+
if not show_raw_logs and job.get('tool') == 'smbmap' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7319
|
+
try:
|
|
7320
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7321
|
+
log_text = f.read()
|
|
7322
|
+
|
|
7323
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7324
|
+
click.echo(click.style("[ERROR] SMBMAP FAILED", bold=True, fg='red'))
|
|
7325
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7326
|
+
click.echo()
|
|
7327
|
+
|
|
7328
|
+
# Check for common smbmap errors
|
|
7329
|
+
error_msg = None
|
|
7330
|
+
if 'Connection refused' in log_text or 'Connection reset' in log_text:
|
|
7331
|
+
error_msg = "Connection refused - SMB service may be down"
|
|
7332
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7333
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
7334
|
+
elif 'Authentication error' in log_text or 'LOGON_FAILURE' in log_text:
|
|
7335
|
+
error_msg = "Authentication failed - invalid credentials"
|
|
7336
|
+
elif 'Access denied' in log_text.lower():
|
|
7337
|
+
error_msg = "Access denied - insufficient privileges"
|
|
7338
|
+
elif 'Errno 113' in log_text or 'No route to host' in log_text:
|
|
7339
|
+
error_msg = "No route to host - network unreachable"
|
|
7340
|
+
elif '[-]' in log_text:
|
|
7341
|
+
match = re.search(r'\[-\]\s*(.+?)(?:\n|$)', log_text)
|
|
7342
|
+
if match:
|
|
7343
|
+
error_msg = match.group(1).strip()[:100]
|
|
7344
|
+
|
|
7345
|
+
if error_msg:
|
|
7346
|
+
click.echo(f" {error_msg}")
|
|
7347
|
+
else:
|
|
7348
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
7349
|
+
|
|
7350
|
+
click.echo()
|
|
7351
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7352
|
+
click.echo()
|
|
7353
|
+
|
|
7354
|
+
except Exception:
|
|
7355
|
+
pass
|
|
7356
|
+
|
|
6928
7357
|
# Parse and display SMBMap results if available (only when not showing raw logs)
|
|
6929
7358
|
if not show_raw_logs and job.get('tool') == 'smbmap' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
6930
7359
|
try:
|
|
@@ -7004,6 +7433,44 @@ def view_job_detail(job_id: int):
|
|
|
7004
7433
|
except Exception as e:
|
|
7005
7434
|
pass
|
|
7006
7435
|
|
|
7436
|
+
# enum4linux ERROR handler
|
|
7437
|
+
if not show_raw_logs and job.get('tool') == 'enum4linux' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7438
|
+
try:
|
|
7439
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7440
|
+
log_text = f.read()
|
|
7441
|
+
|
|
7442
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7443
|
+
click.echo(click.style("[ERROR] ENUM4LINUX FAILED", bold=True, fg='red'))
|
|
7444
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7445
|
+
click.echo()
|
|
7446
|
+
|
|
7447
|
+
# Check for common enum4linux errors
|
|
7448
|
+
error_msg = None
|
|
7449
|
+
if 'Connection refused' in log_text:
|
|
7450
|
+
error_msg = "Connection refused - SMB/NetBIOS service may be down"
|
|
7451
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7452
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
7453
|
+
elif 'NT_STATUS_ACCESS_DENIED' in log_text:
|
|
7454
|
+
error_msg = "Access denied - null session may be blocked"
|
|
7455
|
+
elif 'NT_STATUS_LOGON_FAILURE' in log_text:
|
|
7456
|
+
error_msg = "Logon failure - authentication failed"
|
|
7457
|
+
elif 'Errno 113' in log_text or 'No route to host' in log_text:
|
|
7458
|
+
error_msg = "No route to host - network unreachable"
|
|
7459
|
+
elif 'Could not initialise' in log_text:
|
|
7460
|
+
error_msg = "Could not initialize - target may not support SMB"
|
|
7461
|
+
|
|
7462
|
+
if error_msg:
|
|
7463
|
+
click.echo(f" {error_msg}")
|
|
7464
|
+
else:
|
|
7465
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
7466
|
+
|
|
7467
|
+
click.echo()
|
|
7468
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7469
|
+
click.echo()
|
|
7470
|
+
|
|
7471
|
+
except Exception:
|
|
7472
|
+
pass
|
|
7473
|
+
|
|
7007
7474
|
# Parse and display enum4linux results if available (only when not showing raw logs)
|
|
7008
7475
|
if not show_raw_logs and job.get('tool') == 'enum4linux' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
7009
7476
|
try:
|
|
@@ -7094,6 +7561,48 @@ def view_job_detail(job_id: int):
|
|
|
7094
7561
|
has_credentials = False
|
|
7095
7562
|
has_many_credentials = False
|
|
7096
7563
|
|
|
7564
|
+
# msf_auxiliary ERROR handler
|
|
7565
|
+
if not show_raw_logs and job.get('tool') == 'msf_auxiliary' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7566
|
+
try:
|
|
7567
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7568
|
+
log_text = f.read()
|
|
7569
|
+
|
|
7570
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7571
|
+
click.echo(click.style("[ERROR] METASPLOIT AUXILIARY FAILED", bold=True, fg='red'))
|
|
7572
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7573
|
+
click.echo()
|
|
7574
|
+
|
|
7575
|
+
# Check for common MSF errors
|
|
7576
|
+
error_msg = None
|
|
7577
|
+
if 'Connection refused' in log_text:
|
|
7578
|
+
error_msg = "Connection refused - target service may be down"
|
|
7579
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7580
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
7581
|
+
elif 'Exploit failed' in log_text:
|
|
7582
|
+
error_msg = "Exploit failed - target may not be vulnerable"
|
|
7583
|
+
elif 'Module not found' in log_text or 'Unknown module' in log_text:
|
|
7584
|
+
error_msg = "Module not found - check module name"
|
|
7585
|
+
elif 'Required option' in log_text:
|
|
7586
|
+
match = re.search(r'Required option\s*[\'"]?(\w+)[\'"]?\s*is missing', log_text)
|
|
7587
|
+
if match:
|
|
7588
|
+
error_msg = f"Required option '{match.group(1)}' is missing"
|
|
7589
|
+
elif '[-]' in log_text:
|
|
7590
|
+
match = re.search(r'\[-\]\s*(.+?)(?:\n|$)', log_text)
|
|
7591
|
+
if match:
|
|
7592
|
+
error_msg = match.group(1).strip()[:100]
|
|
7593
|
+
|
|
7594
|
+
if error_msg:
|
|
7595
|
+
click.echo(f" {error_msg}")
|
|
7596
|
+
else:
|
|
7597
|
+
click.echo(" Module failed - see raw logs for details (press 'r')")
|
|
7598
|
+
|
|
7599
|
+
click.echo()
|
|
7600
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7601
|
+
click.echo()
|
|
7602
|
+
|
|
7603
|
+
except Exception:
|
|
7604
|
+
pass
|
|
7605
|
+
|
|
7097
7606
|
# Parse and display msf_auxiliary results if available (only when not showing raw logs)
|
|
7098
7607
|
if not show_raw_logs and job.get('tool') == 'msf_auxiliary' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
7099
7608
|
try:
|
|
@@ -7157,6 +7666,651 @@ def view_job_detail(job_id: int):
|
|
|
7157
7666
|
except Exception as e:
|
|
7158
7667
|
pass
|
|
7159
7668
|
|
|
7669
|
+
# msf_exploit ERROR handler
|
|
7670
|
+
if not show_raw_logs and job.get('tool') == 'msf_exploit' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7671
|
+
try:
|
|
7672
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7673
|
+
log_text = f.read()
|
|
7674
|
+
|
|
7675
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7676
|
+
click.echo(click.style("[ERROR] METASPLOIT EXPLOIT FAILED", bold=True, fg='red'))
|
|
7677
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7678
|
+
click.echo()
|
|
7679
|
+
|
|
7680
|
+
# Check for common msf_exploit errors
|
|
7681
|
+
error_msg = None
|
|
7682
|
+
if 'Connection refused' in log_text:
|
|
7683
|
+
error_msg = "Connection refused - target service may be down or firewalled"
|
|
7684
|
+
elif 'Connection timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7685
|
+
error_msg = "Connection timed out - target may be unreachable"
|
|
7686
|
+
elif 'Invalid module' in log_text or 'Unknown module' in log_text:
|
|
7687
|
+
error_msg = "Invalid or unknown exploit module"
|
|
7688
|
+
elif 'Missing option' in log_text or 'required option' in log_text.lower():
|
|
7689
|
+
error_msg = "Missing required exploit options"
|
|
7690
|
+
elif 'Target is not exploitable' in log_text:
|
|
7691
|
+
error_msg = "Target is not exploitable with current settings"
|
|
7692
|
+
elif 'Exploit failed' in log_text:
|
|
7693
|
+
error_msg = "Exploit execution failed"
|
|
7694
|
+
|
|
7695
|
+
if error_msg:
|
|
7696
|
+
click.echo(f" {error_msg}")
|
|
7697
|
+
else:
|
|
7698
|
+
click.echo(" Exploit failed - check raw logs for details (press 'r')")
|
|
7699
|
+
|
|
7700
|
+
click.echo()
|
|
7701
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7702
|
+
click.echo()
|
|
7703
|
+
|
|
7704
|
+
except Exception:
|
|
7705
|
+
pass
|
|
7706
|
+
|
|
7707
|
+
# Parse and display msf_exploit results if available (done/completed)
|
|
7708
|
+
if not show_raw_logs and job.get('tool') == 'msf_exploit' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
7709
|
+
try:
|
|
7710
|
+
from souleyez.parsers.msf_parser import parse_msf_log
|
|
7711
|
+
parsed = parse_msf_log(log_path)
|
|
7712
|
+
|
|
7713
|
+
sessions = parsed.get('sessions', [])
|
|
7714
|
+
findings = parsed.get('findings', [])
|
|
7715
|
+
|
|
7716
|
+
if sessions or findings:
|
|
7717
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7718
|
+
click.echo(click.style("METASPLOIT EXPLOIT RESULTS", bold=True, fg='green'))
|
|
7719
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7720
|
+
click.echo()
|
|
7721
|
+
|
|
7722
|
+
# Show sessions (most important)
|
|
7723
|
+
if sessions:
|
|
7724
|
+
click.echo(click.style(f"Sessions Created ({len(sessions)}):", bold=True, fg='green'))
|
|
7725
|
+
for s in sessions:
|
|
7726
|
+
session_type = s.get('type', 'unknown').title()
|
|
7727
|
+
session_id = s.get('id', '?')
|
|
7728
|
+
tunnel = s.get('tunnel', 'unknown')
|
|
7729
|
+
click.echo(click.style(f" [{session_id}] {session_type} session", fg='green') + f" - {tunnel}")
|
|
7730
|
+
click.echo()
|
|
7731
|
+
|
|
7732
|
+
# Show findings
|
|
7733
|
+
if findings:
|
|
7734
|
+
severity_colors = {'critical': 'green', 'high': 'red', 'medium': 'yellow', 'low': 'blue', 'info': 'cyan'}
|
|
7735
|
+
for f in findings:
|
|
7736
|
+
sev = f.get('severity', 'info')
|
|
7737
|
+
color = severity_colors.get(sev, 'white')
|
|
7738
|
+
title = f.get('title', 'Unknown')
|
|
7739
|
+
if 'Successful' in title or 'session' in title.lower():
|
|
7740
|
+
click.echo(click.style(f" [SUCCESS] {title}", fg='green'))
|
|
7741
|
+
elif 'Failed' in title:
|
|
7742
|
+
click.echo(click.style(f" [FAILED] {title}", fg='yellow'))
|
|
7743
|
+
else:
|
|
7744
|
+
click.echo(click.style(f" [{sev.upper()}] ", fg=color) + title)
|
|
7745
|
+
click.echo()
|
|
7746
|
+
|
|
7747
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7748
|
+
click.echo()
|
|
7749
|
+
except Exception:
|
|
7750
|
+
pass
|
|
7751
|
+
|
|
7752
|
+
# msf_exploit NO_RESULTS handler
|
|
7753
|
+
if not show_raw_logs and job.get('tool') == 'msf_exploit' and job.get('status') == 'no_results':
|
|
7754
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7755
|
+
click.echo(click.style("METASPLOIT EXPLOIT RESULTS", bold=True, fg='yellow'))
|
|
7756
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7757
|
+
click.echo()
|
|
7758
|
+
click.echo(" No session was created.")
|
|
7759
|
+
click.echo()
|
|
7760
|
+
click.echo(click.style(" Possible reasons:", fg='bright_black'))
|
|
7761
|
+
click.echo(click.style(" - Target is not vulnerable to this exploit", fg='bright_black'))
|
|
7762
|
+
click.echo(click.style(" - Exploit completed but payload failed to execute", fg='bright_black'))
|
|
7763
|
+
click.echo(click.style(" - Target OS/version mismatch with exploit requirements", fg='bright_black'))
|
|
7764
|
+
click.echo(click.style(" - Security controls blocked the exploit (AV, DEP, ASLR)", fg='bright_black'))
|
|
7765
|
+
click.echo()
|
|
7766
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7767
|
+
click.echo()
|
|
7768
|
+
|
|
7769
|
+
# impacket_secretsdump ERROR handler
|
|
7770
|
+
if not show_raw_logs and job.get('tool') == 'impacket_secretsdump' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7771
|
+
try:
|
|
7772
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7773
|
+
log_text = f.read()
|
|
7774
|
+
|
|
7775
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7776
|
+
click.echo(click.style("[ERROR] SECRETSDUMP FAILED", bold=True, fg='red'))
|
|
7777
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7778
|
+
click.echo()
|
|
7779
|
+
|
|
7780
|
+
# Check for common secretsdump errors
|
|
7781
|
+
error_msg = None
|
|
7782
|
+
if 'Connection refused' in log_text:
|
|
7783
|
+
error_msg = "Connection refused - target SMB service may be down"
|
|
7784
|
+
elif 'Access denied' in log_text.lower() or 'STATUS_ACCESS_DENIED' in log_text:
|
|
7785
|
+
error_msg = "Access denied - insufficient privileges to dump secrets"
|
|
7786
|
+
elif 'STATUS_LOGON_FAILURE' in log_text:
|
|
7787
|
+
error_msg = "Logon failure - invalid credentials"
|
|
7788
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7789
|
+
error_msg = "Connection timed out - target may be unreachable"
|
|
7790
|
+
elif 'DRSUAPI method not supported' in log_text:
|
|
7791
|
+
error_msg = "DRSUAPI not supported - may need different method (-use-vss)"
|
|
7792
|
+
elif 'Cannot reach' in log_text:
|
|
7793
|
+
error_msg = "Cannot reach target - check network connectivity"
|
|
7794
|
+
|
|
7795
|
+
if error_msg:
|
|
7796
|
+
click.echo(f" {error_msg}")
|
|
7797
|
+
else:
|
|
7798
|
+
click.echo(" Credential dump failed - check raw logs for details (press 'r')")
|
|
7799
|
+
|
|
7800
|
+
click.echo()
|
|
7801
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7802
|
+
click.echo()
|
|
7803
|
+
|
|
7804
|
+
except Exception:
|
|
7805
|
+
pass
|
|
7806
|
+
|
|
7807
|
+
# Parse and display impacket_secretsdump results (done/completed)
|
|
7808
|
+
if not show_raw_logs and job.get('tool') == 'impacket_secretsdump' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
7809
|
+
try:
|
|
7810
|
+
from souleyez.parsers.impacket_parser import parse_secretsdump
|
|
7811
|
+
target = job.get('target', 'unknown')
|
|
7812
|
+
parsed = parse_secretsdump(log_path, target)
|
|
7813
|
+
|
|
7814
|
+
hashes = parsed.get('hashes', [])
|
|
7815
|
+
credentials = parsed.get('credentials', [])
|
|
7816
|
+
tickets = parsed.get('tickets', [])
|
|
7817
|
+
hashes_count = parsed.get('hashes_count', 0)
|
|
7818
|
+
creds_count = parsed.get('credentials_count', 0)
|
|
7819
|
+
tickets_count = parsed.get('tickets_count', 0)
|
|
7820
|
+
|
|
7821
|
+
if hashes or credentials or tickets:
|
|
7822
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7823
|
+
click.echo(click.style("SECRETSDUMP RESULTS", bold=True, fg='green'))
|
|
7824
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7825
|
+
click.echo()
|
|
7826
|
+
|
|
7827
|
+
# Show summary
|
|
7828
|
+
click.echo(click.style("Summary:", bold=True))
|
|
7829
|
+
click.echo(f" NTLM Hashes: {hashes_count}")
|
|
7830
|
+
click.echo(f" Plaintext Credentials: {creds_count}")
|
|
7831
|
+
click.echo(f" Kerberos Tickets: {tickets_count}")
|
|
7832
|
+
click.echo()
|
|
7833
|
+
|
|
7834
|
+
# Show hashes (limited)
|
|
7835
|
+
if hashes:
|
|
7836
|
+
click.echo(click.style(f"NTLM Hashes ({len(hashes)}):", bold=True, fg='yellow'))
|
|
7837
|
+
for h in hashes[:5]:
|
|
7838
|
+
username = h.get('username', '?')
|
|
7839
|
+
nt_hash = h.get('nt_hash', '?')[:16] + "..."
|
|
7840
|
+
click.echo(f" {username}: {nt_hash}")
|
|
7841
|
+
if len(hashes) > 5:
|
|
7842
|
+
click.echo(click.style(f" ... and {len(hashes) - 5} more hashes", fg='bright_black'))
|
|
7843
|
+
click.echo()
|
|
7844
|
+
|
|
7845
|
+
# Show plaintext credentials
|
|
7846
|
+
if credentials:
|
|
7847
|
+
click.echo(click.style(f"Plaintext Credentials ({len(credentials)}):", bold=True, fg='green'))
|
|
7848
|
+
for c in credentials[:5]:
|
|
7849
|
+
domain = c.get('domain', '')
|
|
7850
|
+
username = c.get('username', '?')
|
|
7851
|
+
password = c.get('password', '?')
|
|
7852
|
+
if domain:
|
|
7853
|
+
click.echo(click.style(f" {domain}\\{username}:{password}", fg='green'))
|
|
7854
|
+
else:
|
|
7855
|
+
click.echo(click.style(f" {username}:{password}", fg='green'))
|
|
7856
|
+
if len(credentials) > 5:
|
|
7857
|
+
click.echo(click.style(f" ... and {len(credentials) - 5} more", fg='bright_black'))
|
|
7858
|
+
click.echo()
|
|
7859
|
+
|
|
7860
|
+
# Show Kerberos tickets
|
|
7861
|
+
if tickets:
|
|
7862
|
+
click.echo(click.style(f"Kerberos Tickets ({len(tickets)}):", bold=True, fg='cyan'))
|
|
7863
|
+
for t in tickets[:3]:
|
|
7864
|
+
username = t.get('username', '?')
|
|
7865
|
+
ticket_type = t.get('ticket_type', 'Kerberos')
|
|
7866
|
+
click.echo(f" {username}: {ticket_type}")
|
|
7867
|
+
if len(tickets) > 3:
|
|
7868
|
+
click.echo(click.style(f" ... and {len(tickets) - 3} more tickets", fg='bright_black'))
|
|
7869
|
+
click.echo()
|
|
7870
|
+
|
|
7871
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7872
|
+
click.echo()
|
|
7873
|
+
except Exception:
|
|
7874
|
+
pass
|
|
7875
|
+
|
|
7876
|
+
# impacket_secretsdump NO_RESULTS handler
|
|
7877
|
+
if not show_raw_logs and job.get('tool') == 'impacket_secretsdump' and job.get('status') == 'no_results':
|
|
7878
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7879
|
+
click.echo(click.style("SECRETSDUMP RESULTS", bold=True, fg='yellow'))
|
|
7880
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7881
|
+
click.echo()
|
|
7882
|
+
click.echo(" No credentials or hashes were extracted.")
|
|
7883
|
+
click.echo()
|
|
7884
|
+
click.echo(click.style(" Possible reasons:", fg='bright_black'))
|
|
7885
|
+
click.echo(click.style(" - Target has no stored credentials", fg='bright_black'))
|
|
7886
|
+
click.echo(click.style(" - Insufficient privileges (need admin/SYSTEM)", fg='bright_black'))
|
|
7887
|
+
click.echo(click.style(" - SAM/NTDS database is protected or unavailable", fg='bright_black'))
|
|
7888
|
+
click.echo(click.style(" - Try -use-vss flag for VSS shadow copy extraction", fg='bright_black'))
|
|
7889
|
+
click.echo()
|
|
7890
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7891
|
+
click.echo()
|
|
7892
|
+
|
|
7893
|
+
# impacket_psexec ERROR handler
|
|
7894
|
+
if not show_raw_logs and job.get('tool') == 'impacket_psexec' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7895
|
+
try:
|
|
7896
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7897
|
+
log_text = f.read()
|
|
7898
|
+
|
|
7899
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7900
|
+
click.echo(click.style("[ERROR] PSEXEC FAILED", bold=True, fg='red'))
|
|
7901
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7902
|
+
click.echo()
|
|
7903
|
+
|
|
7904
|
+
# Check for common psexec errors
|
|
7905
|
+
error_msg = None
|
|
7906
|
+
if 'Connection refused' in log_text:
|
|
7907
|
+
error_msg = "Connection refused - target SMB service may be down"
|
|
7908
|
+
elif 'Access denied' in log_text.lower() or 'STATUS_ACCESS_DENIED' in log_text:
|
|
7909
|
+
error_msg = "Access denied - need admin privileges on target"
|
|
7910
|
+
elif 'STATUS_LOGON_FAILURE' in log_text:
|
|
7911
|
+
error_msg = "Logon failure - invalid credentials"
|
|
7912
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
7913
|
+
error_msg = "Connection timed out - target may be unreachable"
|
|
7914
|
+
elif 'Service installation' in log_text and 'failed' in log_text.lower():
|
|
7915
|
+
error_msg = "Service installation failed - AV may be blocking"
|
|
7916
|
+
elif 'ERROR_SERVICE' in log_text or 'service' in log_text.lower() and 'error' in log_text.lower():
|
|
7917
|
+
error_msg = "Service error - may be blocked by endpoint protection"
|
|
7918
|
+
|
|
7919
|
+
if error_msg:
|
|
7920
|
+
click.echo(f" {error_msg}")
|
|
7921
|
+
else:
|
|
7922
|
+
click.echo(" Remote execution failed - check raw logs for details (press 'r')")
|
|
7923
|
+
|
|
7924
|
+
click.echo()
|
|
7925
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7926
|
+
click.echo()
|
|
7927
|
+
|
|
7928
|
+
except Exception:
|
|
7929
|
+
pass
|
|
7930
|
+
|
|
7931
|
+
# Parse and display impacket_psexec results (done/completed)
|
|
7932
|
+
if not show_raw_logs and job.get('tool') == 'impacket_psexec' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
7933
|
+
try:
|
|
7934
|
+
from souleyez.parsers.impacket_parser import parse_psexec
|
|
7935
|
+
target = job.get('target', 'unknown')
|
|
7936
|
+
parsed = parse_psexec(log_path, target)
|
|
7937
|
+
|
|
7938
|
+
success = parsed.get('success', False)
|
|
7939
|
+
output_lines = parsed.get('output_lines', 0)
|
|
7940
|
+
|
|
7941
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7942
|
+
click.echo(click.style("PSEXEC RESULTS", bold=True, fg='green'))
|
|
7943
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7944
|
+
click.echo()
|
|
7945
|
+
|
|
7946
|
+
if success:
|
|
7947
|
+
click.echo(click.style(" [SUCCESS] Remote shell established!", fg='green'))
|
|
7948
|
+
click.echo(f" Output lines captured: {output_lines}")
|
|
7949
|
+
click.echo()
|
|
7950
|
+
click.echo(" Press 'r' to view full command output.")
|
|
7951
|
+
else:
|
|
7952
|
+
click.echo(click.style(" Connection made but no shell prompt detected.", fg='yellow'))
|
|
7953
|
+
click.echo(" Press 'r' to view raw output for details.")
|
|
7954
|
+
|
|
7955
|
+
click.echo()
|
|
7956
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
7957
|
+
click.echo()
|
|
7958
|
+
except Exception:
|
|
7959
|
+
pass
|
|
7960
|
+
|
|
7961
|
+
# impacket_psexec NO_RESULTS handler
|
|
7962
|
+
if not show_raw_logs and job.get('tool') == 'impacket_psexec' and job.get('status') == 'no_results':
|
|
7963
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7964
|
+
click.echo(click.style("PSEXEC RESULTS", bold=True, fg='yellow'))
|
|
7965
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7966
|
+
click.echo()
|
|
7967
|
+
click.echo(" No command output captured.")
|
|
7968
|
+
click.echo()
|
|
7969
|
+
click.echo(click.style(" Possible reasons:", fg='bright_black'))
|
|
7970
|
+
click.echo(click.style(" - Connection failed before shell was established", fg='bright_black'))
|
|
7971
|
+
click.echo(click.style(" - Insufficient privileges (need local admin)", fg='bright_black'))
|
|
7972
|
+
click.echo(click.style(" - AV/EDR blocked the service installation", fg='bright_black'))
|
|
7973
|
+
click.echo(click.style(" - Try smbexec or wmiexec as alternatives", fg='bright_black'))
|
|
7974
|
+
click.echo()
|
|
7975
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
7976
|
+
click.echo()
|
|
7977
|
+
|
|
7978
|
+
# john ERROR handler
|
|
7979
|
+
if not show_raw_logs and job.get('tool') == 'john' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
7980
|
+
try:
|
|
7981
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
7982
|
+
log_text = f.read()
|
|
7983
|
+
|
|
7984
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7985
|
+
click.echo(click.style("[ERROR] JOHN THE RIPPER FAILED", bold=True, fg='red'))
|
|
7986
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
7987
|
+
click.echo()
|
|
7988
|
+
|
|
7989
|
+
# Check for common john errors
|
|
7990
|
+
error_msg = None
|
|
7991
|
+
if 'No password hashes loaded' in log_text:
|
|
7992
|
+
error_msg = "No password hashes loaded - check hash file format"
|
|
7993
|
+
elif 'Unknown ciphertext format' in log_text:
|
|
7994
|
+
error_msg = "Unknown hash format - try specifying --format=TYPE"
|
|
7995
|
+
elif 'No such file' in log_text or 'cannot open' in log_text.lower():
|
|
7996
|
+
error_msg = "Hash file not found or cannot be opened"
|
|
7997
|
+
elif 'out of memory' in log_text.lower():
|
|
7998
|
+
error_msg = "Out of memory - try reducing parallel tasks"
|
|
7999
|
+
elif 'Invalid session name' in log_text:
|
|
8000
|
+
error_msg = "Invalid session name or session file corrupted"
|
|
8001
|
+
|
|
8002
|
+
if error_msg:
|
|
8003
|
+
click.echo(f" {error_msg}")
|
|
8004
|
+
else:
|
|
8005
|
+
click.echo(" Password cracking failed - check raw logs for details (press 'r')")
|
|
8006
|
+
|
|
8007
|
+
click.echo()
|
|
8008
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8009
|
+
click.echo()
|
|
8010
|
+
|
|
8011
|
+
except Exception:
|
|
8012
|
+
pass
|
|
8013
|
+
|
|
8014
|
+
# Parse and display john results (done/completed)
|
|
8015
|
+
if not show_raw_logs and job.get('tool') == 'john' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
8016
|
+
try:
|
|
8017
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
8018
|
+
log_text = f.read()
|
|
8019
|
+
|
|
8020
|
+
from souleyez.parsers.john_parser import parse_john_output
|
|
8021
|
+
parsed = parse_john_output(log_text)
|
|
8022
|
+
|
|
8023
|
+
cracked = parsed.get('cracked', [])
|
|
8024
|
+
total_loaded = parsed.get('total_loaded', 0)
|
|
8025
|
+
total_cracked = parsed.get('total_cracked', len(cracked))
|
|
8026
|
+
session_status = parsed.get('session_status', 'unknown')
|
|
8027
|
+
|
|
8028
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8029
|
+
click.echo(click.style("JOHN THE RIPPER RESULTS", bold=True, fg='green'))
|
|
8030
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8031
|
+
click.echo()
|
|
8032
|
+
|
|
8033
|
+
# Summary
|
|
8034
|
+
click.echo(click.style("Summary:", bold=True))
|
|
8035
|
+
click.echo(f" Hashes loaded: {total_loaded}")
|
|
8036
|
+
click.echo(f" Passwords cracked: {total_cracked}")
|
|
8037
|
+
click.echo(f" Session status: {session_status}")
|
|
8038
|
+
click.echo()
|
|
8039
|
+
|
|
8040
|
+
# Show cracked passwords
|
|
8041
|
+
if cracked:
|
|
8042
|
+
click.echo(click.style(f"Cracked Passwords ({len(cracked)}):", bold=True, fg='green'))
|
|
8043
|
+
for c in cracked[:10]:
|
|
8044
|
+
username = c.get('username', '?')
|
|
8045
|
+
password = c.get('password', '?')
|
|
8046
|
+
click.echo(click.style(f" {username}:{password}", fg='green'))
|
|
8047
|
+
if len(cracked) > 10:
|
|
8048
|
+
click.echo(click.style(f" ... and {len(cracked) - 10} more", fg='bright_black'))
|
|
8049
|
+
else:
|
|
8050
|
+
click.echo(click.style(" No passwords cracked.", fg='yellow'))
|
|
8051
|
+
|
|
8052
|
+
click.echo()
|
|
8053
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8054
|
+
click.echo()
|
|
8055
|
+
except Exception:
|
|
8056
|
+
pass
|
|
8057
|
+
|
|
8058
|
+
# john NO_RESULTS handler
|
|
8059
|
+
if not show_raw_logs and job.get('tool') == 'john' and job.get('status') == 'no_results':
|
|
8060
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8061
|
+
click.echo(click.style("JOHN THE RIPPER RESULTS", bold=True, fg='yellow'))
|
|
8062
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8063
|
+
click.echo()
|
|
8064
|
+
click.echo(" No passwords cracked.")
|
|
8065
|
+
click.echo()
|
|
8066
|
+
click.echo(click.style(" Suggestions:", fg='bright_black'))
|
|
8067
|
+
click.echo(click.style(" - Try a larger wordlist", fg='bright_black'))
|
|
8068
|
+
click.echo(click.style(" - Use rules: --rules=best64 or --rules=dive", fg='bright_black'))
|
|
8069
|
+
click.echo(click.style(" - Try incremental mode: --incremental", fg='bright_black'))
|
|
8070
|
+
click.echo(click.style(" - Check hash format is correct: --format=TYPE", fg='bright_black'))
|
|
8071
|
+
click.echo()
|
|
8072
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8073
|
+
click.echo()
|
|
8074
|
+
|
|
8075
|
+
# hashcat ERROR handler
|
|
8076
|
+
if not show_raw_logs and job.get('tool') == 'hashcat' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
8077
|
+
try:
|
|
8078
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
8079
|
+
log_text = f.read()
|
|
8080
|
+
|
|
8081
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8082
|
+
click.echo(click.style("[ERROR] HASHCAT FAILED", bold=True, fg='red'))
|
|
8083
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8084
|
+
click.echo()
|
|
8085
|
+
|
|
8086
|
+
# Check for common hashcat errors
|
|
8087
|
+
error_msg = None
|
|
8088
|
+
if 'No hashes loaded' in log_text:
|
|
8089
|
+
error_msg = "No hashes loaded - check hash file format"
|
|
8090
|
+
elif 'Token length exception' in log_text:
|
|
8091
|
+
error_msg = "Invalid hash format - wrong hash type (-m) specified"
|
|
8092
|
+
elif 'Cannot find input' in log_text or 'No such file' in log_text:
|
|
8093
|
+
error_msg = "Hash file or wordlist not found"
|
|
8094
|
+
elif 'CUDA' in log_text or 'OpenCL' in log_text:
|
|
8095
|
+
if 'error' in log_text.lower():
|
|
8096
|
+
error_msg = "GPU driver or OpenCL/CUDA error - check GPU drivers"
|
|
8097
|
+
elif 'out of memory' in log_text.lower():
|
|
8098
|
+
error_msg = "GPU out of memory - try smaller workload (-w)"
|
|
8099
|
+
elif 'Separator unmatched' in log_text:
|
|
8100
|
+
error_msg = "Invalid hash format or separator"
|
|
8101
|
+
|
|
8102
|
+
if error_msg:
|
|
8103
|
+
click.echo(f" {error_msg}")
|
|
8104
|
+
else:
|
|
8105
|
+
click.echo(" Hash cracking failed - check raw logs for details (press 'r')")
|
|
8106
|
+
|
|
8107
|
+
click.echo()
|
|
8108
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8109
|
+
click.echo()
|
|
8110
|
+
|
|
8111
|
+
except Exception:
|
|
8112
|
+
pass
|
|
8113
|
+
|
|
8114
|
+
# Parse and display hashcat results (done/completed)
|
|
8115
|
+
if not show_raw_logs and job.get('tool') == 'hashcat' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
8116
|
+
try:
|
|
8117
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
8118
|
+
log_text = f.read()
|
|
8119
|
+
|
|
8120
|
+
from souleyez.parsers.hashcat_parser import parse_hashcat_output
|
|
8121
|
+
parsed = parse_hashcat_output(log_text)
|
|
8122
|
+
|
|
8123
|
+
cracked = parsed.get('cracked', [])
|
|
8124
|
+
stats = parsed.get('stats', {})
|
|
8125
|
+
cracked_count = stats.get('cracked_count', len(cracked))
|
|
8126
|
+
total_count = stats.get('total_count', 0)
|
|
8127
|
+
status = stats.get('status', 'unknown')
|
|
8128
|
+
|
|
8129
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8130
|
+
click.echo(click.style("HASHCAT RESULTS", bold=True, fg='green'))
|
|
8131
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8132
|
+
click.echo()
|
|
8133
|
+
|
|
8134
|
+
# Summary
|
|
8135
|
+
click.echo(click.style("Summary:", bold=True))
|
|
8136
|
+
if total_count > 0:
|
|
8137
|
+
click.echo(f" Recovered: {cracked_count}/{total_count}")
|
|
8138
|
+
else:
|
|
8139
|
+
click.echo(f" Cracked: {cracked_count}")
|
|
8140
|
+
click.echo(f" Status: {status}")
|
|
8141
|
+
click.echo()
|
|
8142
|
+
|
|
8143
|
+
# Show cracked passwords
|
|
8144
|
+
if cracked:
|
|
8145
|
+
click.echo(click.style(f"Cracked Hashes ({len(cracked)}):", bold=True, fg='green'))
|
|
8146
|
+
for c in cracked[:10]:
|
|
8147
|
+
hash_preview = c.get('hash', '?')[:24] + "..."
|
|
8148
|
+
password = c.get('password', '?')
|
|
8149
|
+
click.echo(click.style(f" {hash_preview} -> {password}", fg='green'))
|
|
8150
|
+
if len(cracked) > 10:
|
|
8151
|
+
click.echo(click.style(f" ... and {len(cracked) - 10} more", fg='bright_black'))
|
|
8152
|
+
else:
|
|
8153
|
+
click.echo(click.style(" No passwords cracked.", fg='yellow'))
|
|
8154
|
+
|
|
8155
|
+
click.echo()
|
|
8156
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8157
|
+
click.echo()
|
|
8158
|
+
except Exception:
|
|
8159
|
+
pass
|
|
8160
|
+
|
|
8161
|
+
# hashcat NO_RESULTS handler
|
|
8162
|
+
if not show_raw_logs and job.get('tool') == 'hashcat' and job.get('status') == 'no_results':
|
|
8163
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8164
|
+
click.echo(click.style("HASHCAT RESULTS", bold=True, fg='yellow'))
|
|
8165
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8166
|
+
click.echo()
|
|
8167
|
+
click.echo(" No passwords cracked.")
|
|
8168
|
+
click.echo()
|
|
8169
|
+
click.echo(click.style(" Suggestions:", fg='bright_black'))
|
|
8170
|
+
click.echo(click.style(" - Try a larger wordlist", fg='bright_black'))
|
|
8171
|
+
click.echo(click.style(" - Use rules: -r best64.rule or -r dive.rule", fg='bright_black'))
|
|
8172
|
+
click.echo(click.style(" - Try mask attack: -a 3 ?a?a?a?a?a?a", fg='bright_black'))
|
|
8173
|
+
click.echo(click.style(" - Verify hash mode is correct: -m HASHTYPE", fg='bright_black'))
|
|
8174
|
+
click.echo()
|
|
8175
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8176
|
+
click.echo()
|
|
8177
|
+
|
|
8178
|
+
# responder ERROR handler
|
|
8179
|
+
if not show_raw_logs and job.get('tool') == 'responder' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
8180
|
+
try:
|
|
8181
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
8182
|
+
log_text = f.read()
|
|
8183
|
+
|
|
8184
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8185
|
+
click.echo(click.style("[ERROR] RESPONDER FAILED", bold=True, fg='red'))
|
|
8186
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8187
|
+
click.echo()
|
|
8188
|
+
|
|
8189
|
+
# Check for common responder errors
|
|
8190
|
+
error_msg = None
|
|
8191
|
+
if 'Permission denied' in log_text or 'root' in log_text.lower():
|
|
8192
|
+
error_msg = "Permission denied - Responder requires root privileges"
|
|
8193
|
+
elif 'Address already in use' in log_text:
|
|
8194
|
+
error_msg = "Port already in use - another service may be running"
|
|
8195
|
+
elif 'No such device' in log_text or 'Interface' in log_text:
|
|
8196
|
+
error_msg = "Invalid network interface - check interface name"
|
|
8197
|
+
elif 'cannot bind' in log_text.lower():
|
|
8198
|
+
error_msg = "Cannot bind to port - check if ports are available"
|
|
8199
|
+
|
|
8200
|
+
if error_msg:
|
|
8201
|
+
click.echo(f" {error_msg}")
|
|
8202
|
+
else:
|
|
8203
|
+
click.echo(" Responder failed - check raw logs for details (press 'r')")
|
|
8204
|
+
|
|
8205
|
+
click.echo()
|
|
8206
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8207
|
+
click.echo()
|
|
8208
|
+
|
|
8209
|
+
except Exception:
|
|
8210
|
+
pass
|
|
8211
|
+
|
|
8212
|
+
# Parse and display responder results (done/completed)
|
|
8213
|
+
if not show_raw_logs and job.get('tool') == 'responder' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
8214
|
+
try:
|
|
8215
|
+
from souleyez.parsers.responder_parser import parse_responder
|
|
8216
|
+
interface = job.get('args', {}).get('interface', 'unknown')
|
|
8217
|
+
parsed = parse_responder(log_path, interface)
|
|
8218
|
+
|
|
8219
|
+
credentials = parsed.get('credentials', [])
|
|
8220
|
+
creds_count = parsed.get('credentials_captured', len(credentials))
|
|
8221
|
+
summary = parsed.get('summary', '')
|
|
8222
|
+
|
|
8223
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8224
|
+
click.echo(click.style("RESPONDER RESULTS", bold=True, fg='green'))
|
|
8225
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8226
|
+
click.echo()
|
|
8227
|
+
|
|
8228
|
+
# Summary
|
|
8229
|
+
click.echo(click.style("Summary:", bold=True))
|
|
8230
|
+
click.echo(f" {summary}")
|
|
8231
|
+
click.echo()
|
|
8232
|
+
|
|
8233
|
+
# Show captured credentials
|
|
8234
|
+
if credentials:
|
|
8235
|
+
click.echo(click.style(f"Captured NTLMv2 Hashes ({len(credentials)}):", bold=True, fg='green'))
|
|
8236
|
+
for c in credentials[:10]:
|
|
8237
|
+
domain = c.get('domain', '')
|
|
8238
|
+
username = c.get('username', '?')
|
|
8239
|
+
protocol = c.get('protocol', '?')
|
|
8240
|
+
if domain:
|
|
8241
|
+
click.echo(click.style(f" [{protocol}] {domain}\\{username}", fg='green'))
|
|
8242
|
+
else:
|
|
8243
|
+
click.echo(click.style(f" [{protocol}] {username}", fg='green'))
|
|
8244
|
+
if len(credentials) > 10:
|
|
8245
|
+
click.echo(click.style(f" ... and {len(credentials) - 10} more", fg='bright_black'))
|
|
8246
|
+
click.echo()
|
|
8247
|
+
click.echo(click.style(" Tip: Crack these hashes with hashcat -m 5600", fg='cyan'))
|
|
8248
|
+
else:
|
|
8249
|
+
click.echo(click.style(" No credentials captured.", fg='yellow'))
|
|
8250
|
+
|
|
8251
|
+
click.echo()
|
|
8252
|
+
click.echo(click.style("=" * 70, fg='green'))
|
|
8253
|
+
click.echo()
|
|
8254
|
+
except Exception:
|
|
8255
|
+
pass
|
|
8256
|
+
|
|
8257
|
+
# responder NO_RESULTS handler
|
|
8258
|
+
if not show_raw_logs and job.get('tool') == 'responder' and job.get('status') == 'no_results':
|
|
8259
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8260
|
+
click.echo(click.style("RESPONDER RESULTS", bold=True, fg='yellow'))
|
|
8261
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8262
|
+
click.echo()
|
|
8263
|
+
click.echo(" No credentials captured.")
|
|
8264
|
+
click.echo()
|
|
8265
|
+
click.echo(click.style(" Possible reasons:", fg='bright_black'))
|
|
8266
|
+
click.echo(click.style(" - No LLMNR/NBT-NS/mDNS traffic on network", fg='bright_black'))
|
|
8267
|
+
click.echo(click.style(" - Network is using proper DNS infrastructure", fg='bright_black'))
|
|
8268
|
+
click.echo(click.style(" - Firewall blocking broadcast traffic", fg='bright_black'))
|
|
8269
|
+
click.echo(click.style(" - Try running for longer or during peak hours", fg='bright_black'))
|
|
8270
|
+
click.echo()
|
|
8271
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8272
|
+
click.echo()
|
|
8273
|
+
|
|
8274
|
+
# Hydra ERROR handler
|
|
8275
|
+
if not show_raw_logs and job.get('tool') == 'hydra' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
8276
|
+
try:
|
|
8277
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
8278
|
+
log_text = f.read()
|
|
8279
|
+
|
|
8280
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8281
|
+
click.echo(click.style("[ERROR] HYDRA ATTACK FAILED", bold=True, fg='red'))
|
|
8282
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8283
|
+
click.echo()
|
|
8284
|
+
|
|
8285
|
+
# Check for common hydra errors
|
|
8286
|
+
error_msg = None
|
|
8287
|
+
if 'Connection refused' in log_text:
|
|
8288
|
+
error_msg = "Connection refused - service may be down or port closed"
|
|
8289
|
+
elif 'could not connect' in log_text.lower():
|
|
8290
|
+
error_msg = "Could not connect to target service"
|
|
8291
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
8292
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
8293
|
+
elif 'too many connections' in log_text.lower():
|
|
8294
|
+
error_msg = "Too many connections - try reducing threads with -t"
|
|
8295
|
+
elif 'target does not support' in log_text.lower():
|
|
8296
|
+
error_msg = "Target does not support the specified protocol"
|
|
8297
|
+
elif 'ERROR' in log_text:
|
|
8298
|
+
match = re.search(r'\[ERROR\]\s*(.+?)(?:\n|$)', log_text)
|
|
8299
|
+
if match:
|
|
8300
|
+
error_msg = match.group(1).strip()[:100]
|
|
8301
|
+
|
|
8302
|
+
if error_msg:
|
|
8303
|
+
click.echo(f" {error_msg}")
|
|
8304
|
+
else:
|
|
8305
|
+
click.echo(" Attack failed - see raw logs for details (press 'r')")
|
|
8306
|
+
|
|
8307
|
+
click.echo()
|
|
8308
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8309
|
+
click.echo()
|
|
8310
|
+
|
|
8311
|
+
except Exception:
|
|
8312
|
+
pass
|
|
8313
|
+
|
|
7160
8314
|
# Parse and display Hydra results if available (only when not showing raw logs)
|
|
7161
8315
|
if not show_raw_logs and job.get('tool') == 'hydra' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
7162
8316
|
try:
|
|
@@ -7271,6 +8425,40 @@ def view_job_detail(job_id: int):
|
|
|
7271
8425
|
# Silently fail - not critical
|
|
7272
8426
|
pass
|
|
7273
8427
|
|
|
8428
|
+
# SearchSploit ERROR handler
|
|
8429
|
+
if not show_raw_logs and job.get('tool') == 'searchsploit' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
|
|
8430
|
+
try:
|
|
8431
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
8432
|
+
log_text = f.read()
|
|
8433
|
+
|
|
8434
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8435
|
+
click.echo(click.style("[ERROR] SEARCHSPLOIT FAILED", bold=True, fg='red'))
|
|
8436
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8437
|
+
click.echo()
|
|
8438
|
+
|
|
8439
|
+
# Check for common searchsploit errors
|
|
8440
|
+
error_msg = None
|
|
8441
|
+
if 'not found' in log_text.lower() and 'command' in log_text.lower():
|
|
8442
|
+
error_msg = "searchsploit not found - install exploitdb package"
|
|
8443
|
+
elif 'database' in log_text.lower() and ('not found' in log_text.lower() or 'missing' in log_text.lower()):
|
|
8444
|
+
error_msg = "Exploit database not found - run searchsploit -u to update"
|
|
8445
|
+
elif 'Error' in log_text:
|
|
8446
|
+
match = re.search(r'Error[:\s]+(.+?)(?:\n|$)', log_text, re.IGNORECASE)
|
|
8447
|
+
if match:
|
|
8448
|
+
error_msg = match.group(1).strip()[:100]
|
|
8449
|
+
|
|
8450
|
+
if error_msg:
|
|
8451
|
+
click.echo(f" {error_msg}")
|
|
8452
|
+
else:
|
|
8453
|
+
click.echo(" Search failed - see raw logs for details (press 'r')")
|
|
8454
|
+
|
|
8455
|
+
click.echo()
|
|
8456
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8457
|
+
click.echo()
|
|
8458
|
+
|
|
8459
|
+
except Exception:
|
|
8460
|
+
pass
|
|
8461
|
+
|
|
7274
8462
|
# Parse and display SearchSploit results if available (only when not showing raw logs)
|
|
7275
8463
|
if not show_raw_logs and job.get('tool') == 'searchsploit' and job.get('status') in ['done', 'completed', 'no_results'] and log_path and os.path.exists(log_path):
|
|
7276
8464
|
try:
|
|
@@ -7467,6 +8655,121 @@ def view_job_detail(job_id: int):
|
|
|
7467
8655
|
# Silently fail - not critical
|
|
7468
8656
|
pass
|
|
7469
8657
|
|
|
8658
|
+
# =================================================================
|
|
8659
|
+
# GENERIC STATUS HANDLERS (fallbacks for tools without custom handlers)
|
|
8660
|
+
# =================================================================
|
|
8661
|
+
|
|
8662
|
+
# Tools with custom error handlers (don't show generic for these)
|
|
8663
|
+
tools_with_error_handlers = [
|
|
8664
|
+
'gobuster', 'ffuf', 'nmap', 'ard', 'hydra', 'nuclei', 'sqlmap',
|
|
8665
|
+
'nikto', 'wpscan', 'dnsrecon', 'theharvester', 'crackmapexec',
|
|
8666
|
+
'smbmap', 'enum4linux', 'whois', 'searchsploit', 'msf_auxiliary',
|
|
8667
|
+
'msf_exploit', 'impacket_secretsdump', 'impacket_psexec', 'john',
|
|
8668
|
+
'hashcat', 'responder'
|
|
8669
|
+
]
|
|
8670
|
+
|
|
8671
|
+
# Tools with custom warning handlers
|
|
8672
|
+
tools_with_warning_handlers = ['gobuster', 'ffuf']
|
|
8673
|
+
|
|
8674
|
+
# Tools with custom no_results handlers
|
|
8675
|
+
tools_with_no_results_handlers = [
|
|
8676
|
+
'ffuf', 'gobuster', 'sqlmap', 'wpscan', 'dnsrecon', 'nuclei',
|
|
8677
|
+
'theharvester', 'nikto', 'whois', 'crackmapexec', 'smbmap',
|
|
8678
|
+
'enum4linux', 'hydra', 'searchsploit', 'http_fingerprint',
|
|
8679
|
+
'nmap', 'ard', 'msf_exploit', 'impacket_secretsdump', 'impacket_psexec',
|
|
8680
|
+
'john', 'hashcat', 'responder'
|
|
8681
|
+
]
|
|
8682
|
+
|
|
8683
|
+
current_tool = job.get('tool', '')
|
|
8684
|
+
current_status = job.get('status', '')
|
|
8685
|
+
|
|
8686
|
+
# Generic ERROR handler
|
|
8687
|
+
if current_status == 'error' and current_tool not in tools_with_error_handlers:
|
|
8688
|
+
tool_name = current_tool.upper().replace('_', ' ') if current_tool else 'UNKNOWN'
|
|
8689
|
+
click.echo()
|
|
8690
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8691
|
+
click.echo(click.style(f"[ERROR] {tool_name} SCAN FAILED", bold=True, fg='red'))
|
|
8692
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8693
|
+
click.echo()
|
|
8694
|
+
|
|
8695
|
+
# Try to extract error message from log
|
|
8696
|
+
error_msg = None
|
|
8697
|
+
if log_path and os.path.exists(log_path):
|
|
8698
|
+
try:
|
|
8699
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
8700
|
+
log_text = f.read()
|
|
8701
|
+
|
|
8702
|
+
if 'Connection refused' in log_text:
|
|
8703
|
+
error_msg = "Connection refused - target may be down or port closed"
|
|
8704
|
+
elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
|
|
8705
|
+
error_msg = "Command timed out - target may be slow or unresponsive"
|
|
8706
|
+
elif 'Permission denied' in log_text:
|
|
8707
|
+
error_msg = "Permission denied - may need elevated privileges"
|
|
8708
|
+
elif 'not found' in log_text.lower() and 'command' in log_text.lower():
|
|
8709
|
+
error_msg = "Tool not found - check installation"
|
|
8710
|
+
elif 'ERROR:' in log_text:
|
|
8711
|
+
match = re.search(r'ERROR:\s*(.+?)(?:\n|$)', log_text)
|
|
8712
|
+
if match:
|
|
8713
|
+
error_msg = match.group(1).strip()[:100]
|
|
8714
|
+
except:
|
|
8715
|
+
pass
|
|
8716
|
+
|
|
8717
|
+
if error_msg:
|
|
8718
|
+
click.echo(f" {error_msg}")
|
|
8719
|
+
else:
|
|
8720
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
8721
|
+
|
|
8722
|
+
click.echo()
|
|
8723
|
+
click.echo(click.style("=" * 70, fg='red'))
|
|
8724
|
+
click.echo()
|
|
8725
|
+
|
|
8726
|
+
# Generic WARNING handler
|
|
8727
|
+
elif current_status == 'warning' and current_tool not in tools_with_warning_handlers:
|
|
8728
|
+
tool_name = current_tool.upper().replace('_', ' ') if current_tool else 'UNKNOWN'
|
|
8729
|
+
click.echo()
|
|
8730
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8731
|
+
click.echo(click.style(f"[WARNING] {tool_name} SCAN COMPLETED WITH ISSUES", bold=True, fg='yellow'))
|
|
8732
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8733
|
+
click.echo()
|
|
8734
|
+
click.echo(" Scan completed but encountered problems.")
|
|
8735
|
+
click.echo(" Press 'r' to view raw logs for details.")
|
|
8736
|
+
click.echo()
|
|
8737
|
+
click.echo(click.style("=" * 70, fg='yellow'))
|
|
8738
|
+
click.echo()
|
|
8739
|
+
|
|
8740
|
+
# Generic KILLED handler (no tools have custom killed handlers)
|
|
8741
|
+
elif current_status == 'killed':
|
|
8742
|
+
tool_name = current_tool.upper().replace('_', ' ') if current_tool else 'UNKNOWN'
|
|
8743
|
+
click.echo()
|
|
8744
|
+
click.echo(click.style("=" * 70, fg='magenta'))
|
|
8745
|
+
click.echo(click.style(f"[KILLED] {tool_name} SCAN TERMINATED", bold=True, fg='magenta'))
|
|
8746
|
+
click.echo(click.style("=" * 70, fg='magenta'))
|
|
8747
|
+
click.echo()
|
|
8748
|
+
click.echo(" Scan was manually stopped by user.")
|
|
8749
|
+
if log_path and os.path.exists(log_path):
|
|
8750
|
+
click.echo(" Partial results may be available in raw logs (press 'r').")
|
|
8751
|
+
click.echo()
|
|
8752
|
+
click.echo(click.style("=" * 70, fg='magenta'))
|
|
8753
|
+
click.echo()
|
|
8754
|
+
|
|
8755
|
+
# Generic NO_RESULTS handler (for tools without custom handlers)
|
|
8756
|
+
elif current_status == 'no_results' and current_tool not in tools_with_no_results_handlers:
|
|
8757
|
+
tool_name = current_tool.upper().replace('_', ' ') if current_tool else 'UNKNOWN'
|
|
8758
|
+
click.echo()
|
|
8759
|
+
click.echo(click.style("=" * 70, fg='cyan'))
|
|
8760
|
+
click.echo(click.style(f"{tool_name} RESULTS", bold=True, fg='cyan'))
|
|
8761
|
+
click.echo(click.style("=" * 70, fg='cyan'))
|
|
8762
|
+
click.echo()
|
|
8763
|
+
click.echo(" No results found.")
|
|
8764
|
+
click.echo()
|
|
8765
|
+
click.echo(click.style(" This could mean:", fg='bright_black'))
|
|
8766
|
+
click.echo(click.style(" - Target is not vulnerable to this check", fg='bright_black'))
|
|
8767
|
+
click.echo(click.style(" - Service/port is not available", fg='bright_black'))
|
|
8768
|
+
click.echo(click.style(" - Different scan options may yield results", fg='bright_black'))
|
|
8769
|
+
click.echo()
|
|
8770
|
+
click.echo(click.style("=" * 70, fg='cyan'))
|
|
8771
|
+
click.echo()
|
|
8772
|
+
|
|
7470
8773
|
click.echo()
|
|
7471
8774
|
|
|
7472
8775
|
# Actions menu
|