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 +1 -1
- souleyez/docs/README.md +1 -1
- souleyez/main.py +1 -1
- souleyez/ui/interactive.py +1027 -28
- {souleyez-2.43.26.dist-info → souleyez-2.43.29.dist-info}/METADATA +1 -1
- {souleyez-2.43.26.dist-info → souleyez-2.43.29.dist-info}/RECORD +10 -10
- {souleyez-2.43.26.dist-info → souleyez-2.43.29.dist-info}/WHEEL +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.29.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.29.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.29.dist-info}/top_level.txt +0 -0
souleyez/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = '2.43.
|
|
1
|
+
__version__ = '2.43.29'
|
|
2
2
|
|
souleyez/docs/README.md
CHANGED
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.
|
|
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
|
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
|
|
@@ -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
|
-
#
|
|
7317
|
-
if not show_raw_logs and job.get('tool') == '
|
|
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]
|
|
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
|
|
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
|
|
7331
|
-
elif '
|
|
7332
|
-
error_msg = "
|
|
7333
|
-
elif '
|
|
7334
|
-
error_msg = "
|
|
7335
|
-
elif '
|
|
7336
|
-
error_msg = "
|
|
7337
|
-
elif '
|
|
7338
|
-
error_msg = "Target
|
|
7339
|
-
elif '
|
|
7340
|
-
|
|
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("
|
|
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
|
|
7357
|
-
if not show_raw_logs and job.get('tool') == '
|
|
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.
|
|
7360
|
-
|
|
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
|
-
|
|
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 = [
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
375
|
-
souleyez-2.43.
|
|
376
|
-
souleyez-2.43.
|
|
377
|
-
souleyez-2.43.
|
|
378
|
-
souleyez-2.43.
|
|
379
|
-
souleyez-2.43.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|