souleyez 2.43.26__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 CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = '2.43.26'
1
+ __version__ = '2.43.29'
2
2
 
souleyez/docs/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SoulEyez Documentation
2
2
 
3
- **Version:** 2.43.26
3
+ **Version:** 2.43.29
4
4
  **Last Updated:** January 13, 2026
5
5
  **Organization:** CyberSoul Security
6
6
 
souleyez/main.py CHANGED
@@ -173,7 +173,7 @@ def _check_privileged_tools():
173
173
 
174
174
 
175
175
  @click.group()
176
- @click.version_option(version='2.43.26')
176
+ @click.version_option(version='2.43.29')
177
177
  def cli():
178
178
  """SoulEyez - AI-Powered Pentesting Platform by CyberSoul Security"""
179
179
  from souleyez.log_config import init_logging
@@ -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
@@ -6140,6 +6139,48 @@ def view_job_detail(job_id: int):
6140
6139
  import traceback
6141
6140
  traceback.print_exc()
6142
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
+
6143
6184
  # Parse and display WPScan results if available (only when not showing raw logs)
6144
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):
6145
6186
  try:
@@ -6333,6 +6374,46 @@ def view_job_detail(job_id: int):
6333
6374
  # Silently fail - not critical
6334
6375
  pass
6335
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
+
6336
6417
  # Parse and display DNSRecon results if available (only when not showing raw logs)
6337
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):
6338
6419
  try:
@@ -6776,6 +6857,46 @@ def view_job_detail(job_id: int):
6776
6857
  except Exception as e:
6777
6858
  pass
6778
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
+
6779
6900
  # Parse and display theHarvester results if available (only when not showing raw logs)
6780
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):
6781
6902
  try:
@@ -6864,6 +6985,44 @@ def view_job_detail(job_id: int):
6864
6985
  # Fall back to raw log if parsing fails
6865
6986
  pass
6866
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
+
6867
7026
  # Parse and display Nikto results if available (only when not showing raw logs)
6868
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):
6869
7028
  try:
@@ -6953,6 +7112,40 @@ def view_job_detail(job_id: int):
6953
7112
  # Fall back to raw log if parsing fails
6954
7113
  pass
6955
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
+
6956
7149
  # Parse and display WHOIS results if available (only when not showing raw logs)
6957
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):
6958
7151
  try:
@@ -7032,6 +7225,46 @@ def view_job_detail(job_id: int):
7032
7225
  except Exception as e:
7033
7226
  pass
7034
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
+
7035
7268
  # Parse and display CrackMapExec results if available (only when not showing raw logs)
7036
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):
7037
7270
  try:
@@ -7081,6 +7314,46 @@ def view_job_detail(job_id: int):
7081
7314
  except Exception as e:
7082
7315
  pass
7083
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
+
7084
7357
  # Parse and display SMBMap results if available (only when not showing raw logs)
7085
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):
7086
7359
  try:
@@ -7160,6 +7433,44 @@ def view_job_detail(job_id: int):
7160
7433
  except Exception as e:
7161
7434
  pass
7162
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
+
7163
7474
  # Parse and display enum4linux results if available (only when not showing raw logs)
7164
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):
7165
7476
  try:
@@ -7250,6 +7561,48 @@ def view_job_detail(job_id: int):
7250
7561
  has_credentials = False
7251
7562
  has_many_credentials = False
7252
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
+
7253
7606
  # Parse and display msf_auxiliary results if available (only when not showing raw logs)
7254
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):
7255
7608
  try:
@@ -7313,38 +7666,36 @@ def view_job_detail(job_id: int):
7313
7666
  except Exception as e:
7314
7667
  pass
7315
7668
 
7316
- # Hydra ERROR handler
7317
- if not show_raw_logs and job.get('tool') == 'hydra' and job.get('status') == 'error' and log_path and os.path.exists(log_path):
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):
7318
7671
  try:
7319
7672
  with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
7320
7673
  log_text = f.read()
7321
7674
 
7322
7675
  click.echo(click.style("=" * 70, fg='red'))
7323
- click.echo(click.style("[ERROR] HYDRA ATTACK FAILED", bold=True, fg='red'))
7676
+ click.echo(click.style("[ERROR] METASPLOIT EXPLOIT FAILED", bold=True, fg='red'))
7324
7677
  click.echo(click.style("=" * 70, fg='red'))
7325
7678
  click.echo()
7326
7679
 
7327
- # Check for common hydra errors
7680
+ # Check for common msf_exploit errors
7328
7681
  error_msg = None
7329
7682
  if 'Connection refused' in log_text:
7330
- error_msg = "Connection refused - service may be down or port closed"
7331
- elif 'could not connect' in log_text.lower():
7332
- error_msg = "Could not connect to target service"
7333
- elif 'timed out' in log_text.lower() or 'timeout' in log_text.lower():
7334
- error_msg = "Connection timed out - target may be slow or filtering"
7335
- elif 'too many connections' in log_text.lower():
7336
- error_msg = "Too many connections - try reducing threads with -t"
7337
- elif 'target does not support' in log_text.lower():
7338
- error_msg = "Target does not support the specified protocol"
7339
- elif 'ERROR' in log_text:
7340
- match = re.search(r'\[ERROR\]\s*(.+?)(?:\n|$)', log_text)
7341
- if match:
7342
- error_msg = match.group(1).strip()[:100]
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"
7343
7694
 
7344
7695
  if error_msg:
7345
7696
  click.echo(f" {error_msg}")
7346
7697
  else:
7347
- click.echo(" Attack failed - see raw logs for details (press 'r')")
7698
+ click.echo(" Exploit failed - check raw logs for details (press 'r')")
7348
7699
 
7349
7700
  click.echo()
7350
7701
  click.echo(click.style("=" * 70, fg='red'))
@@ -7353,15 +7704,622 @@ def view_job_detail(job_id: int):
7353
7704
  except Exception:
7354
7705
  pass
7355
7706
 
7356
- # Parse and display Hydra results if available (only when not showing raw logs)
7357
- 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):
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):
7358
7709
  try:
7359
- from souleyez.parsers.hydra_parser import parse_hydra_output
7360
- with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
7361
- log_content = f.read()
7362
- parsed = parse_hydra_output(log_content, job.get('target', ''))
7710
+ from souleyez.parsers.msf_parser import parse_msf_log
7711
+ parsed = parse_msf_log(log_path)
7363
7712
 
7364
- credentials = parsed.get('credentials', [])
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
+
8314
+ # Parse and display Hydra results if available (only when not showing raw logs)
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):
8316
+ try:
8317
+ from souleyez.parsers.hydra_parser import parse_hydra_output
8318
+ with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
8319
+ log_content = f.read()
8320
+ parsed = parse_hydra_output(log_content, job.get('target', ''))
8321
+
8322
+ credentials = parsed.get('credentials', [])
7365
8323
  usernames = parsed.get('usernames', [])
7366
8324
  has_credentials = len(credentials) > 0
7367
8325
 
@@ -7467,6 +8425,40 @@ def view_job_detail(job_id: int):
7467
8425
  # Silently fail - not critical
7468
8426
  pass
7469
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
+
7470
8462
  # Parse and display SearchSploit results if available (only when not showing raw logs)
7471
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):
7472
8464
  try:
@@ -7668,7 +8660,13 @@ def view_job_detail(job_id: int):
7668
8660
  # =================================================================
7669
8661
 
7670
8662
  # Tools with custom error handlers (don't show generic for these)
7671
- tools_with_error_handlers = ['gobuster', 'ffuf', 'nmap', 'ard', 'hydra', 'nuclei', 'sqlmap']
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
+ ]
7672
8670
 
7673
8671
  # Tools with custom warning handlers
7674
8672
  tools_with_warning_handlers = ['gobuster', 'ffuf']
@@ -7678,7 +8676,8 @@ def view_job_detail(job_id: int):
7678
8676
  'ffuf', 'gobuster', 'sqlmap', 'wpscan', 'dnsrecon', 'nuclei',
7679
8677
  'theharvester', 'nikto', 'whois', 'crackmapexec', 'smbmap',
7680
8678
  'enum4linux', 'hydra', 'searchsploit', 'http_fingerprint',
7681
- 'nmap', 'ard'
8679
+ 'nmap', 'ard', 'msf_exploit', 'impacket_secretsdump', 'impacket_psexec',
8680
+ 'john', 'hashcat', 'responder'
7682
8681
  ]
7683
8682
 
7684
8683
  current_tool = job.get('tool', '')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: souleyez
3
- Version: 2.43.26
3
+ Version: 2.43.29
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>
@@ -1,10 +1,10 @@
1
- souleyez/__init__.py,sha256=W7_ihmKebGDg5wOXze4pirFaaBA2KbGP_fIT3exGUTg,25
1
+ souleyez/__init__.py,sha256=q8SRbvbOJ7HvTBgD-w_SNKEhB76Cvr51rFUlnDPrPLg,25
2
2
  souleyez/config.py,sha256=av357I3GYRWAklv8Dto-9-5Db699Wq5znez7zo7241Q,11595
3
3
  souleyez/devtools.py,sha256=rptmUY4a5eVvYjdEc6273MSagL-D9xibPOFgohVqUno,3508
4
4
  souleyez/feature_flags.py,sha256=mo6YAq07lc6sR3lEFKmIwTKxXZ2JPxwa5X97uR_mu50,4642
5
5
  souleyez/history.py,sha256=gzs5I_j-3OigIP6yfmBChdqxaFmyUIxvTpzWUPe_Q6c,2853
6
6
  souleyez/log_config.py,sha256=MMhPAJOqgXDfuE-xm5g0RxAfWndcmbhFHvIEMm1a_Wo,5830
7
- souleyez/main.py,sha256=ofrfuhN8kGrX6zj9Za9oHKTKaI1fT_DeCY0MIaoNkX4,129101
7
+ souleyez/main.py,sha256=Ks5HG8UCvFerPaZrg9hau6erIyknCC3vhSfKQYkrLBk,129101
8
8
  souleyez/scanner.py,sha256=U3IWHRrJ5aQ32dSHiVAHB60w1R_z0E0QxfM99msYNlw,3124
9
9
  souleyez/security.py,sha256=S84m1QmnKz_6NgH2I6IBIAorMHxRPNYVFSnks5xjihQ,2479
10
10
  souleyez/ui.py,sha256=15pfsqoDPnojAqr5S0TZHJE2ZkSHzkHpNVfVvsRj66A,34301
@@ -104,7 +104,7 @@ souleyez/detection/__init__.py,sha256=QIhvXjFdjrquQ6A0VQ7GZQkK_EXB59t8Dv9PKXhEUe
104
104
  souleyez/detection/attack_signatures.py,sha256=akgWwiIkh6WYnghCuLhRV0y6FS0SQ0caGF8tZUc49oA,6965
105
105
  souleyez/detection/mitre_mappings.py,sha256=xejE80YK-g8kKaeQoo-vBl8P3t8RTTItbfN0NaVZw6s,20558
106
106
  souleyez/detection/validator.py,sha256=-AJ7QSJ3-6jFKLnPG_Rc34IXyF4JPyI82BFUgTA9zw0,15641
107
- souleyez/docs/README.md,sha256=n8Xwj9zC-sjYh3ehIgjZBDCl_k92dhLyjJBMkezu6Aw,7188
107
+ souleyez/docs/README.md,sha256=ub9YtlSAKcwgwtJtS4mzkO6G5PDjQqzCDOvf-xpDwe4,7188
108
108
  souleyez/docs/api-reference/cli-commands.md,sha256=lTLFnILN3YRVdqCaag7WgsYXfDGglb1TuPexkxDsVdE,12917
109
109
  souleyez/docs/api-reference/engagement-api.md,sha256=nd-EvQMtiJrobg2bzFEADp853HP1Uhb9dmgok0_-neE,11672
110
110
  souleyez/docs/api-reference/integration-guide.md,sha256=c96uX79ukHyYotLa54wZ20Kx-EUZnrKegTeGkfLD-pw,16285
@@ -347,7 +347,7 @@ souleyez/ui/export_view.py,sha256=0nQvVsKk7FU4uRzSfJ_qBZh_Lfn8hgGA2rbJ5bNg5-Y,65
347
347
  souleyez/ui/gap_analysis_view.py,sha256=AytAOEBq010wwo9hne1TE-uJpY_xicjLrFANbvN3r3w,30727
348
348
  souleyez/ui/help_system.py,sha256=nKGxLaMi-TKYs6xudTyw_tZqBb1cGFEuYYh6N-MAsJE,16648
349
349
  souleyez/ui/intelligence_view.py,sha256=VeAQ-3mANRnLIVpRqocL3JV0HUmJtADdxDeC5lzQhE0,32168
350
- souleyez/ui/interactive.py,sha256=WkxOlN1OyMvI49pZXje_tulORScHfoV5RNvE6PZPi50,1442929
350
+ souleyez/ui/interactive.py,sha256=iF1hD9ax_piBn_H6gg5JkG0Vib_-6urJ5LYuBrsXtI8,1494232
351
351
  souleyez/ui/interactive_selector.py,sha256=6A51fgmFRnemBY0aCPHIhK2Rpba16NjSGKLzC0Q5vI8,16407
352
352
  souleyez/ui/log_formatter.py,sha256=akhIkYoO_cCaKxS1V5N3iPmIrHzgsU7pmsedx70s9TI,3845
353
353
  souleyez/ui/menu_components.py,sha256=N8zq2QXGmfaLJ08l53MMYt1y-5LRWgpZH6r8nXHonj8,3519
@@ -371,9 +371,9 @@ souleyez/ui/wazuh_vulns_view.py,sha256=3vJJEmrjgS2wD6EDB7ZV7WxgytBHTm-1WqNDjp7lV
371
371
  souleyez/ui/wordlist_browser.py,sha256=iQ2YYxrVo8FGCfM-Bc0teVBijSAbd2rjbSQ2hOE7eiY,16110
372
372
  souleyez/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
373
373
  souleyez/utils/tool_checker.py,sha256=YzNajZpFyKJA5fp0Kq_gQ0YnKb7J1BaKJSZ8vP-IWj8,30868
374
- souleyez-2.43.26.dist-info/licenses/LICENSE,sha256=J7vDD5QMF4w2oSDm35eBgosATE70ah1M40u9W4EpTZs,1090
375
- souleyez-2.43.26.dist-info/METADATA,sha256=lPnWrbGHwqpBwcB6xinsSJ8WYUdpMnBaDCjbCpIit_E,10427
376
- souleyez-2.43.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
377
- souleyez-2.43.26.dist-info/entry_points.txt,sha256=bN5W1dhjDZJl3TKclMjRpfQvGPmyrJLwwDuCj_X39HE,48
378
- souleyez-2.43.26.dist-info/top_level.txt,sha256=afAMzS9p4lcdBNxhGo6jl3ipQE9HUvvNIPOdjtPjr_Q,9
379
- souleyez-2.43.26.dist-info/RECORD,,
374
+ souleyez-2.43.29.dist-info/licenses/LICENSE,sha256=J7vDD5QMF4w2oSDm35eBgosATE70ah1M40u9W4EpTZs,1090
375
+ souleyez-2.43.29.dist-info/METADATA,sha256=JHHy81-ALcPTD7N1v625MGQFGa5vjvdBb2nNH4Z92UI,10427
376
+ souleyez-2.43.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
377
+ souleyez-2.43.29.dist-info/entry_points.txt,sha256=bN5W1dhjDZJl3TKclMjRpfQvGPmyrJLwwDuCj_X39HE,48
378
+ souleyez-2.43.29.dist-info/top_level.txt,sha256=afAMzS9p4lcdBNxhGo6jl3ipQE9HUvvNIPOdjtPjr_Q,9
379
+ souleyez-2.43.29.dist-info/RECORD,,