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

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

Potentially problematic release.


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

souleyez/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.43.34"
1
+ __version__ = "3.0.0"
@@ -113,6 +113,96 @@ SERVICE_GROUPS = {
113
113
  }
114
114
 
115
115
 
116
+ def should_test_url_for_sqli(endpoint_url: str) -> bool:
117
+ """
118
+ Determine if a URL should be tested for SQL injection.
119
+
120
+ This filters out URLs that are unlikely to have injectable parameters,
121
+ while allowing URLs that might have forms or query parameters.
122
+
123
+ Args:
124
+ endpoint_url: The URL to evaluate
125
+
126
+ Returns:
127
+ True if the URL should be tested, False if it should be skipped
128
+
129
+ Examples:
130
+ >>> should_test_url_for_sqli("http://example.com/payroll_app.php")
131
+ True # Dynamic page, might have forms
132
+ >>> should_test_url_for_sqli("http://example.com/index.php")
133
+ False # Common default page, rarely injectable
134
+ >>> should_test_url_for_sqli("http://example.com/search.php?q=test")
135
+ True # Has query parameters
136
+ >>> should_test_url_for_sqli("http://example.com/phpinfo.php")
137
+ False # phpinfo output, not injectable
138
+ >>> should_test_url_for_sqli("http://example.com/cgi-bin/")
139
+ False # Directory, not a script
140
+ >>> should_test_url_for_sqli("http://example.com/")
141
+ False # No path, no params
142
+ """
143
+ from urllib.parse import urlparse
144
+
145
+ path_lower = endpoint_url.lower()
146
+ has_params = "?" in endpoint_url
147
+
148
+ # URLs with query params should generally be tested - they have injection points
149
+ # But still skip known non-injectable apps even with params
150
+ if has_params:
151
+ # Even with params, skip known non-injectable applications
152
+ hard_skip = [
153
+ "/phpmyadmin/", # phpMyAdmin - DB admin tool, not a target
154
+ "/phpmyadmin?", # phpMyAdmin with params
155
+ ]
156
+ if any(pattern in path_lower for pattern in hard_skip):
157
+ return False
158
+ # Skip static files with version/cache-busting params
159
+ # These are not injectable: /jquery.js?v=1.2.3, /style.css?ver=5.0
160
+ if ".js?" in path_lower or ".css?" in path_lower:
161
+ return False
162
+ # Has params and not in hard skip - test it
163
+ return True
164
+
165
+ # No query params - apply stricter filtering
166
+ # Skip known non-injectable paths (only when no params)
167
+ skip_patterns = [
168
+ "/twiki/", # TWiki wiki - not SQLi vulnerable
169
+ "/phpmyadmin/", # phpMyAdmin - DB admin, not SQLi target
170
+ "/phpmyadmin.", # phpMyAdmin CSS/JS files
171
+ "/phpinfo", # phpinfo() output - no injection
172
+ "/cgi-bin/", # Base CGI dir without script - no injection
173
+ "/misc/", # Drupal/CMS static assets directory
174
+ "/modules/", # Drupal modules directory (static files)
175
+ ]
176
+ if any(pattern in path_lower for pattern in skip_patterns):
177
+ return False
178
+
179
+ # No params - require dynamic extension that might have forms
180
+ dynamic_extensions = (".php", ".asp", ".aspx", ".jsp", ".do", ".action", ".cgi")
181
+ is_dynamic = any(path_lower.endswith(ext) for ext in dynamic_extensions)
182
+
183
+ if not is_dynamic:
184
+ # No params and not a dynamic page - skip
185
+ return False
186
+
187
+ # Skip common default/utility pages that rarely have injectable forms
188
+ useless_dynamic_pages = [
189
+ "/index.php", "/index.asp", "/index.aspx", "/index.jsp",
190
+ "/default.php", "/default.asp", "/default.aspx",
191
+ "/home.php", "/home.asp",
192
+ "/info.php", "/test.php",
193
+ ]
194
+ if not has_params:
195
+ # Only check this list for pages without params
196
+ try:
197
+ parsed = urlparse(endpoint_url)
198
+ if parsed.path.lower() in useless_dynamic_pages:
199
+ return False
200
+ except Exception:
201
+ pass
202
+
203
+ return True
204
+
205
+
116
206
  def classify_os_device(os_string: str, services: list) -> dict:
117
207
  """
118
208
  Classify OS and device type from nmap output.
@@ -1502,23 +1592,15 @@ class ToolChaining:
1502
1592
  ],
1503
1593
  description="WordPress detected, scanning for vulnerabilities with WPScan",
1504
1594
  ),
1505
- # phpMyAdmin → SQLMap (common SQLi target)
1506
- ChainRule(
1507
- trigger_tool="gobuster",
1508
- trigger_condition="finding:phpmyadmin",
1509
- target_tool="sqlmap",
1510
- priority=8,
1511
- args_template=[
1512
- "-u",
1513
- "{target}",
1514
- "--batch",
1515
- "--forms",
1516
- "--level=2",
1517
- "--risk=2",
1518
- "--threads=5",
1519
- ],
1520
- description="phpMyAdmin found, testing for SQL injection",
1521
- ),
1595
+ # phpMyAdmin → SQLMap - DISABLED
1596
+ # phpMyAdmin is a database management tool with proper authentication.
1597
+ # It doesn't have SQLi in its login form. Use nuclei/searchsploit instead.
1598
+ # ChainRule(
1599
+ # trigger_tool="gobuster",
1600
+ # trigger_condition="finding:phpmyadmin",
1601
+ # target_tool="sqlmap",
1602
+ # ...
1603
+ # ),
1522
1604
  # === END WordPress-specific chains ===
1523
1605
  # Dalfox - Deep XSS scan if nuclei hints at XSS
1524
1606
  ChainRule(
@@ -1677,9 +1759,11 @@ class ToolChaining:
1677
1759
  description="Domain member detected, scanning subnet for Domain Controller",
1678
1760
  ),
1679
1761
  # Kerbrute chains - enumerate users via Kerberos when domain discovered
1762
+ # IMPORTANT: Require port 88 (Kerberos) to be open - not just "has:domains"
1763
+ # Samba workgroups report as "domains" but are NOT Active Directory
1680
1764
  ChainRule(
1681
1765
  trigger_tool="enum4linux",
1682
- trigger_condition="has:domains",
1766
+ trigger_condition="has:domains & port:88",
1683
1767
  target_tool="kerbrute",
1684
1768
  priority=6,
1685
1769
  args_template=[
@@ -1690,11 +1774,11 @@ class ToolChaining:
1690
1774
  "{dc_ip}",
1691
1775
  "data/wordlists/ad_users.txt",
1692
1776
  ],
1693
- description="Domain discovered via SMB - enumerating users via Kerberos",
1777
+ description="Active Directory detected (Kerberos port 88 open) - enumerating users",
1694
1778
  ),
1695
1779
  ChainRule(
1696
1780
  trigger_tool="crackmapexec",
1697
- trigger_condition="has:domains",
1781
+ trigger_condition="has:domains & port:88",
1698
1782
  target_tool="kerbrute",
1699
1783
  priority=6,
1700
1784
  args_template=[
@@ -1705,7 +1789,7 @@ class ToolChaining:
1705
1789
  "{dc_ip}",
1706
1790
  "data/wordlists/ad_users.txt",
1707
1791
  ],
1708
- description="Domain discovered via SMB - enumerating users via Kerberos",
1792
+ description="Active Directory detected (Kerberos port 88 open) - enumerating users",
1709
1793
  ),
1710
1794
  ]
1711
1795
  )
@@ -1981,9 +2065,10 @@ class ToolChaining:
1981
2065
  description="Domain discovered - enumerating all objects to find hidden users",
1982
2066
  ),
1983
2067
  # Kerbrute user enumeration (works even when anonymous LDAP is blocked)
2068
+ # IMPORTANT: Require port 88 (Kerberos) - LDAP alone doesn't mean AD
1984
2069
  ChainRule(
1985
2070
  trigger_tool="ldapsearch",
1986
- trigger_condition="has:domains",
2071
+ trigger_condition="has:domains & port:88",
1987
2072
  target_tool="kerbrute",
1988
2073
  priority=6,
1989
2074
  args_template=[
@@ -1994,7 +2079,7 @@ class ToolChaining:
1994
2079
  "{dc_ip}",
1995
2080
  "data/wordlists/ad_users.txt",
1996
2081
  ],
1997
- description="Domain discovered via LDAP - enumerating users via Kerberos",
2082
+ description="Active Directory detected (Kerberos port 88 open) - enumerating users",
1998
2083
  ),
1999
2084
  ]
2000
2085
  )
@@ -2175,49 +2260,28 @@ class ToolChaining:
2175
2260
  )
2176
2261
  )
2177
2262
 
2178
- # Gobuster discovered PHP files → crawl base URL once to find parametrized URLs
2179
- # NOTE: Special handling in auto_chain() limits to ONE crawl per base URL
2180
- self.rules.append(
2181
- ChainRule(
2182
- trigger_tool="gobuster",
2183
- trigger_condition="has:php_files",
2184
- target_tool="sqlmap",
2185
- priority=7,
2186
- args_template=[
2187
- "-u",
2188
- "{target}",
2189
- "--batch",
2190
- "--crawl=2",
2191
- "--risk=2",
2192
- "--level=3",
2193
- "--forms",
2194
- "--threads=5",
2195
- ],
2196
- description="PHP files discovered, crawling site to find parametrized pages and forms",
2197
- )
2198
- )
2263
+ # Gobuster discovered PHP files → crawl base URL - DISABLED
2264
+ # Reason: katana→sqlmap handles this better by targeting specific parametrized URLs.
2265
+ # Crawling the base URL with SQLMap is slow and often wasteful.
2266
+ # self.rules.append(
2267
+ # ChainRule(
2268
+ # trigger_tool="gobuster",
2269
+ # trigger_condition="has:php_files",
2270
+ # target_tool="sqlmap",
2271
+ # ...
2272
+ # )
2273
+ # )
2199
2274
 
2200
- # Gobuster discovered ASP/ASPX files → crawl base URL once to find parametrized URLs
2201
- # Same logic as PHP files - ASP is common in Windows/IIS environments
2202
- self.rules.append(
2203
- ChainRule(
2204
- trigger_tool="gobuster",
2205
- trigger_condition="has:asp_files",
2206
- target_tool="sqlmap",
2207
- priority=7,
2208
- args_template=[
2209
- "-u",
2210
- "{target}",
2211
- "--batch",
2212
- "--crawl=2",
2213
- "--risk=2",
2214
- "--level=3",
2215
- "--forms",
2216
- "--threads=5",
2217
- ],
2218
- description="ASP/ASPX files discovered, crawling site to find parametrized pages and forms",
2219
- )
2220
- )
2275
+ # Gobuster discovered ASP/ASPX files → crawl base URL - DISABLED
2276
+ # Reason: katana→sqlmap handles this better by targeting specific parametrized URLs.
2277
+ # self.rules.append(
2278
+ # ChainRule(
2279
+ # trigger_tool="gobuster",
2280
+ # trigger_condition="has:asp_files",
2281
+ # target_tool="sqlmap",
2282
+ # ...
2283
+ # )
2284
+ # )
2221
2285
 
2222
2286
  # SMART API DISCOVERY CHAIN
2223
2287
  # Replaced broken direct SQLMap rules with intelligent two-step approach
@@ -2885,37 +2949,44 @@ class ToolChaining:
2885
2949
  )
2886
2950
 
2887
2951
  # ffuf found parameters → test with SQLMap (skip for LFI scans)
2888
- self.rules.append(
2889
- ChainRule(
2890
- trigger_tool="ffuf",
2891
- trigger_condition="has:parameters_found & !is:lfi_scan",
2892
- target_tool="sqlmap",
2893
- priority=9,
2894
- args_template=["-u", "{target}", "--batch", "--level=2", "--risk=2"],
2895
- description="Parameters discovered, testing for SQL injection",
2896
- )
2897
- )
2952
+ # DISABLED: This rule passes {target} (original ffuf target) to sqlmap,
2953
+ # which is useless - we need actual discovered endpoints with parameters.
2954
+ # The smart chain (rule #-1) in auto_chain() handles ffuf → sqlmap properly
2955
+ # by parsing ffuf output and testing discovered endpoints.
2956
+ # self.rules.append(
2957
+ # ChainRule(
2958
+ # trigger_tool="ffuf",
2959
+ # trigger_condition="has:parameters_found & !is:lfi_scan",
2960
+ # target_tool="sqlmap",
2961
+ # priority=9,
2962
+ # args_template=["-u", "{target}", "--batch", "--level=2", "--risk=2"],
2963
+ # description="Parameters discovered, testing for SQL injection",
2964
+ # )
2965
+ # )
2898
2966
 
2899
2967
  # ffuf found parameters → test with Nuclei XSS (skip for LFI scans)
2900
- self.rules.append(
2901
- ChainRule(
2902
- trigger_tool="ffuf",
2903
- trigger_condition="has:parameters_found & !is:lfi_scan",
2904
- target_tool="nuclei",
2905
- priority=8,
2906
- args_template=[
2907
- "-tags",
2908
- "xss,rxss",
2909
- "-severity",
2910
- "critical,high,medium",
2911
- "-rate-limit",
2912
- "50",
2913
- "-c",
2914
- "10",
2915
- ],
2916
- description="Parameters discovered, testing for XSS",
2917
- )
2918
- )
2968
+ # DISABLED: Same issue as above - uses {target} instead of discovered parameters.
2969
+ # Running XSS scans on bare directories like /cgi-bin/ is useless.
2970
+ # Smart chains in auto_chain() handle ffuf → nuclei properly.
2971
+ # self.rules.append(
2972
+ # ChainRule(
2973
+ # trigger_tool="ffuf",
2974
+ # trigger_condition="has:parameters_found & !is:lfi_scan",
2975
+ # target_tool="nuclei",
2976
+ # priority=8,
2977
+ # args_template=[
2978
+ # "-tags",
2979
+ # "xss,rxss",
2980
+ # "-severity",
2981
+ # "critical,high,medium",
2982
+ # "-rate-limit",
2983
+ # "50",
2984
+ # "-c",
2985
+ # "10",
2986
+ # ],
2987
+ # description="Parameters discovered, testing for XSS",
2988
+ # )
2989
+ # )
2919
2990
 
2920
2991
  # Gobuster found API endpoints → parameter fuzzing with ffuf
2921
2992
  self.rules.extend(
@@ -3135,27 +3206,17 @@ class ToolChaining:
3135
3206
  )
3136
3207
  )
3137
3208
 
3138
- # Custom PHP → SQLMap standard crawl
3139
- self.rules.append(
3140
- ChainRule(
3141
- trigger_tool="gobuster",
3142
- trigger_condition="category:custom_php",
3143
- target_tool="sqlmap",
3144
- priority=7,
3145
- args_template=[
3146
- "-u",
3147
- "{target}",
3148
- "--batch",
3149
- "--crawl=2",
3150
- "--forms",
3151
- "--level=2",
3152
- "--risk=2",
3153
- "--smart",
3154
- "--threads=5",
3155
- ],
3156
- description="Custom PHP app detected, scan forms for SQL injection",
3157
- )
3158
- )
3209
+ # Custom PHP → SQLMap standard crawl - DISABLED
3210
+ # Reason: katana→sqlmap handles this better by targeting specific parametrized URLs.
3211
+ # The "custom_php" category is too broad (default for unrecognized paths).
3212
+ # self.rules.append(
3213
+ # ChainRule(
3214
+ # trigger_tool="gobuster",
3215
+ # trigger_condition="category:custom_php",
3216
+ # target_tool="sqlmap",
3217
+ # ...
3218
+ # )
3219
+ # )
3159
3220
 
3160
3221
  # === END Directory Category Chain Rules ===
3161
3222
 
@@ -8544,6 +8605,11 @@ class ToolChaining:
8544
8605
 
8545
8606
  # === SQLMap for testable endpoints ===
8546
8607
  # Skip SQLMap if this was an LFI fuzz scan - results are LFI payloads, not SQLi targets
8608
+
8609
+ # Use helper function to filter non-injectable URLs
8610
+ if not should_test_url_for_sqli(endpoint_url):
8611
+ continue
8612
+
8547
8613
  if (
8548
8614
  not is_lfi_scan
8549
8615
  and status_code in testable_statuses
@@ -9190,6 +9256,43 @@ class ToolChaining:
9190
9256
  ):
9191
9257
  continue
9192
9258
 
9259
+ # Skip external URLs - only test URLs on the original target host
9260
+ try:
9261
+ from urllib.parse import urlparse
9262
+ parsed_url = urlparse(url)
9263
+ parsed_target = urlparse(target)
9264
+ if parsed_url.netloc and parsed_target.netloc:
9265
+ if parsed_url.netloc.lower() != parsed_target.netloc.lower():
9266
+ logger.debug(f" Skipping external URL: {url}")
9267
+ continue
9268
+ except Exception:
9269
+ pass
9270
+
9271
+ # Skip non-injectable paths (TWiki, phpMyAdmin, Apache dir params)
9272
+ skip_patterns = [
9273
+ "/twiki/", # TWiki wiki - not SQLi vulnerable
9274
+ "/phpmyadmin/", # phpMyAdmin - DB admin, not SQLi
9275
+ "/phpmyadmin.", # phpMyAdmin CSS/JS files
9276
+ "?c=d", "?c=s", "?c=m", "?c=n", # Apache dir listing sort params
9277
+ "?o=a", "?o=d", # Apache dir listing order params
9278
+ ";o=a", ";o=d", # Apache dir listing (semicolon variant)
9279
+ "/misc/", # Drupal/CMS static assets directory
9280
+ "/modules/", # Drupal modules directory (static files)
9281
+ ]
9282
+ # Also skip static files with version/cache-busting params
9283
+ # These are not injectable: /jquery.js?v=1.2.3, /style.css?ver=5.0
9284
+ if ".js?" in path_lower or ".css?" in path_lower:
9285
+ logger.debug(f" Skipping static file with cache param: {url}")
9286
+ continue
9287
+ if any(pattern in path_lower for pattern in skip_patterns):
9288
+ logger.debug(f" Skipping non-injectable path: {url}")
9289
+ continue
9290
+
9291
+ # Skip URLs without real parameters (just base URL or path)
9292
+ if "?" not in url and url not in forms_found:
9293
+ logger.debug(f" Skipping URL without parameters: {url}")
9294
+ continue
9295
+
9193
9296
  # Determine if this is a form (POST) or URL param (GET)
9194
9297
  is_form = url in forms_found
9195
9298
 
@@ -10571,74 +10674,7 @@ class ToolChaining:
10571
10674
  except Exception as e:
10572
10675
  logger.debug(f"Evil-WinRM chain check failed: {e}")
10573
10676
 
10574
- # === Hydra credentials found SSH shell access (Linux) ===
10575
- if credentials:
10576
- from souleyez.engine.background import enqueue_job
10577
- from souleyez.storage.hosts import HostManager
10578
- from souleyez.log_config import get_logger
10579
-
10580
- logger = get_logger(__name__)
10581
-
10582
- try:
10583
- host_manager = HostManager()
10584
- target_host = target.split(":")[0] if ":" in target else target
10585
-
10586
- host = host_manager.get_host_by_ip(engagement_id, target_host)
10587
- if host:
10588
- services = host_manager.get_host_services(host["id"])
10589
- ssh_svc = next(
10590
- (s for s in services if s.get("port") == 22), None
10591
- )
10592
-
10593
- if ssh_svc:
10594
- # SSH is available - create SSH command execution job
10595
- for cred in credentials: # Process all credentials
10596
- username = cred.get("username")
10597
- password = cred.get("password")
10598
-
10599
- if username and password:
10600
- # Check if SSH chain already ran for this user
10601
- from souleyez.storage.database import Database
10602
-
10603
- try:
10604
- db = Database()
10605
- existing = db.execute(
10606
- """SELECT id FROM jobs WHERE engagement_id = ?
10607
- AND tool = 'nxc' AND args LIKE '%ssh%'
10608
- AND args LIKE ? AND status != 'killed' LIMIT 1""",
10609
- (engagement_id, f'%-u", "{username}%'),
10610
- )
10611
- if existing:
10612
- continue
10613
- except Exception:
10614
- pass
10615
-
10616
- # Use nxc (netexec) to test SSH shell access
10617
- ssh_job_id = enqueue_job(
10618
- tool="nxc",
10619
- target=target_host,
10620
- args=[
10621
- "ssh",
10622
- target_host,
10623
- "-u",
10624
- username,
10625
- "-p",
10626
- password,
10627
- "-x",
10628
- "whoami && id && hostname",
10629
- ],
10630
- label="hydra",
10631
- engagement_id=engagement_id,
10632
- parent_id=job.get("id"),
10633
- reason=f"Auto-triggered by hydra: Testing SSH shell access with {username}",
10634
- rule_id=-28, # Smart chain: hydra → nxc ssh (shell)
10635
- )
10636
- job_ids.append(ssh_job_id)
10637
- logger.info(
10638
- f" nxc SSH job #{ssh_job_id} for {username}"
10639
- )
10640
- except Exception as e:
10641
- logger.debug(f"SSH shell chain check failed: {e}")
10677
+ # NOTE: SSH shell chain removed - spawn shell directly from Hydra job via [s] option
10642
10678
 
10643
10679
  return job_ids
10644
10680
 
souleyez/docs/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # SoulEyez Documentation
2
2
 
3
- **Version:** 2.43.34
4
- **Last Updated:** January 26, 2026
3
+ **Version:** 3.0.0
4
+ **Last Updated:** January 29, 2026
5
5
  **Organization:** CyberSoul Security
6
6
 
7
7
  Welcome to the SoulEyez documentation! This documentation covers architecture, development, user guides, and operational information for the SoulEyez penetration testing platform.
@@ -261,6 +261,7 @@ class HydraHandler(BaseToolHandler):
261
261
  "port": parsed.get("port"),
262
262
  "credentials_found": len(parsed.get("credentials", [])),
263
263
  "credentials_added": creds_added,
264
+ "credentials": parsed.get("credentials", []), # Include actual creds for smart chains
264
265
  "usernames_found": len(parsed.get("usernames", [])),
265
266
  "usernames": parsed.get("usernames", []),
266
267
  "usernames_added": usernames_added,
@@ -165,6 +165,7 @@ class MsfAuxiliaryHandler(BaseToolHandler):
165
165
  credential_summaries.append(
166
166
  {
167
167
  "username": cred.get("username", ""),
168
+ "password": cred.get("password", ""),
168
169
  "service": cred.get("service", "unknown"),
169
170
  "port": cred.get("port"),
170
171
  }
@@ -77,10 +77,51 @@ class NxcHandler(BaseToolHandler):
77
77
  hostname = banner_match.group(1)
78
78
  domain = banner_match.group(2)
79
79
 
80
- # Check for Pwn3d
80
+ # Check for Pwn3d (SMB admin access)
81
81
  if re.search(self.PWNED_PATTERN, log_content):
82
82
  is_pwned = True
83
83
 
84
+ # Check for SSH Shell access (Linux)
85
+ # Format: [+] leia_organa:help_me_obiwan Linux - Shell access!
86
+ has_shell_access = False
87
+ if "Shell access!" in log_content:
88
+ has_shell_access = True
89
+ # Parse SSH credentials
90
+ ssh_cred_pattern = r"\[\+\]\s+([^:\s]+):(\S+)\s+.*Shell access!"
91
+ for match in re.finditer(ssh_cred_pattern, log_content):
92
+ username = match.group(1)
93
+ password = match.group(2)
94
+ cred = {
95
+ "username": username,
96
+ "password": password,
97
+ "domain": "",
98
+ "service": "ssh",
99
+ "status": "valid",
100
+ }
101
+ credentials.append(cred)
102
+
103
+ # Store in database
104
+ if credentials_manager and host_manager:
105
+ try:
106
+ host = host_manager.get_host_by_ip(engagement_id, target)
107
+ if host:
108
+ credentials_manager.add_credential(
109
+ engagement_id=engagement_id,
110
+ host_id=host["id"],
111
+ username=username,
112
+ password=password,
113
+ service="ssh",
114
+ port=22,
115
+ credential_type="password",
116
+ tool="nxc",
117
+ status="valid",
118
+ )
119
+ logger.warning(
120
+ f"SSH SHELL ACCESS: {username}:{password} on {target}"
121
+ )
122
+ except Exception as e:
123
+ logger.debug(f"Could not store SSH credential: {e}")
124
+
84
125
  # Parse valid credentials
85
126
  # Format: [+] baby2.vl\Carl.Moore:Carl.Moore
86
127
  for match in re.finditer(self.VALID_CRED_PATTERN, log_content):
@@ -231,6 +272,8 @@ class NxcHandler(BaseToolHandler):
231
272
 
232
273
  if credentials:
233
274
  status = STATUS_DONE
275
+ elif has_shell_access:
276
+ status = STATUS_DONE # SSH shell access without parsed creds
234
277
  elif expired_credentials:
235
278
  status = STATUS_WARNING # Expired creds need attention
236
279
  elif shares:
@@ -255,6 +298,8 @@ class NxcHandler(BaseToolHandler):
255
298
  )
256
299
  if is_pwned:
257
300
  summary_parts.append("PWNED!")
301
+ if has_shell_access:
302
+ summary_parts.append("SHELL ACCESS!")
258
303
  if shares:
259
304
  summary_parts.append(
260
305
  f"{len(shares)} shares ({len(readable_shares)} readable, {len(writable_shares)} writable)"
@@ -273,6 +318,7 @@ class NxcHandler(BaseToolHandler):
273
318
  "credentials": credentials,
274
319
  "expired_credentials": expired_credentials,
275
320
  "is_pwned": is_pwned,
321
+ "has_shell_access": has_shell_access,
276
322
  "summary": summary,
277
323
  }
278
324
 
souleyez/main.py CHANGED
@@ -185,7 +185,7 @@ def _check_privileged_tools():
185
185
 
186
186
 
187
187
  @click.group()
188
- @click.version_option(version="2.43.34")
188
+ @click.version_option(version="3.0.0")
189
189
  def cli():
190
190
  """SoulEyez - AI-Powered Pentesting Platform by CyberSoul Security"""
191
191
  from souleyez.log_config import init_logging