PyADRecon 0.12.2__tar.gz → 0.12.3__tar.gz

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.
Files changed (27) hide show
  1. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/CHANGELOG.md +7 -7
  2. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/PKG-INFO +1 -1
  3. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/PyADRecon.egg-info/PKG-INFO +1 -1
  4. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/dashboard_generator.py +204 -8
  5. pyadrecon-0.12.3/package.json +3 -0
  6. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/pyadrecon.py +1 -1
  7. pyadrecon-0.12.2/package.json +0 -3
  8. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.dockerignore +0 -0
  9. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.github/FUNDING.yml +0 -0
  10. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  11. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  12. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.github/pyadrecon.png +0 -0
  13. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.github/workflows/conventional-commits.yml +0 -0
  14. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.github/workflows/pypi.yml +0 -0
  15. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/.gitignore +0 -0
  16. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/Dockerfile +0 -0
  17. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/LICENSE +0 -0
  18. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/PyADRecon.egg-info/SOURCES.txt +0 -0
  19. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/PyADRecon.egg-info/dependency_links.txt +0 -0
  20. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/PyADRecon.egg-info/entry_points.txt +0 -0
  21. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/PyADRecon.egg-info/requires.txt +0 -0
  22. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/PyADRecon.egg-info/top_level.txt +0 -0
  23. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/README.md +0 -0
  24. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/pyproject.toml +0 -0
  25. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/requirements.txt +0 -0
  26. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/setup.cfg +0 -0
  27. {pyadrecon-0.12.2 → pyadrecon-0.12.3}/windows/README.md +0 -0
@@ -1,3 +1,10 @@
1
+ ## [0.12.3](https://github.com/l4rm4nd/PyADRecon/compare/v0.12.2...v0.12.3) (2026-02-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add krbtgt rotation and protected users group to dashboard ([03e3046](https://github.com/l4rm4nd/PyADRecon/commit/03e30466f1f1f9033e0aa3341dba7521f855a62e))
7
+
1
8
  ## [0.12.2](https://github.com/l4rm4nd/PyADRecon/compare/v0.12.1...v0.12.2) (2026-02-21)
2
9
 
3
10
 
@@ -26,10 +33,3 @@
26
33
 
27
34
  * initialize sid mappings earlier to support individual --collect adcs runs ([b036a8f](https://github.com/l4rm4nd/PyADRecon/commit/b036a8fde34eae88cb14e570f9feb92a4a0e0fb3))
28
35
 
29
- ## [0.11.13](https://github.com/l4rm4nd/PyADRecon/compare/v0.11.12...v0.11.13) (2026-02-19)
30
-
31
-
32
- ### Bug Fixes
33
-
34
- * forest-child enumeration and sid resolving ([10637ff](https://github.com/l4rm4nd/PyADRecon/commit/10637ff68929c00b06df058b3720e26a20975774))
35
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyADRecon
3
- Version: 0.12.2
3
+ Version: 0.12.3
4
4
  Summary: Python Active Directory Reconnaissance Tool (ADRecon port) with NTLM and Kerberos support.
5
5
  Author: LRVT
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyADRecon
3
- Version: 0.12.2
3
+ Version: 0.12.3
4
4
  Summary: Python Active Directory Reconnaissance Tool (ADRecon port) with NTLM and Kerberos support.
5
5
  Author: LRVT
6
6
  License-Expression: MIT
@@ -286,6 +286,18 @@ class DashboardGenerator:
286
286
  .loading-message.show {{
287
287
  display: block;
288
288
  }}
289
+
290
+ /* Inline tool links - inherit color, no underline, subtle hover */
291
+ .tool-link {{
292
+ color: inherit;
293
+ text-decoration: none;
294
+ border-bottom: 1px dotted currentColor;
295
+ transition: opacity 0.2s;
296
+ }}
297
+
298
+ .tool-link:hover {{
299
+ opacity: 0.7;
300
+ }}
289
301
  </style>
290
302
  </head>
291
303
  <body class="bg-gray-900 dark">
@@ -467,6 +479,40 @@ class DashboardGenerator:
467
479
  </div>
468
480
  </div>
469
481
  </div>
482
+
483
+ <div v-if="krbtgtOldPassword.length > 0" @click="navigateToSection('findings', 'krbtgt-section')" class="stat-card bg-white dark:bg-gray-800 rounded-lg shadow p-6 cursor-pointer hover:shadow-lg transition-shadow">
484
+ <div class="flex items-center">
485
+ <div class="flex-shrink-0 bg-red-100 dark:bg-red-900/30 rounded-md p-3">
486
+ <i class="fas fa-crown text-red-600 text-2xl"></i>
487
+ </div>
488
+ <div class="ml-5 w-0 flex-1">
489
+ <dl>
490
+ <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">KRBTGT Password Age</dt>
491
+ <dd class="text-3xl font-semibold text-gray-900 dark:text-white">{{{{ krbtgtOldPassword.length }}}}</dd>
492
+ <dd class="text-xs text-gray-500 dark:text-gray-400 mt-1">
493
+ Golden Ticket risk
494
+ </dd>
495
+ </dl>
496
+ </div>
497
+ </div>
498
+ </div>
499
+
500
+ <div v-if="unprotectedPrivilegedUsers.length > 0" @click="navigateToSection('findings', 'protected-users-section')" class="stat-card bg-white dark:bg-gray-800 rounded-lg shadow p-6 cursor-pointer hover:shadow-lg transition-shadow">
501
+ <div class="flex items-center">
502
+ <div class="flex-shrink-0 bg-teal-100 dark:bg-teal-900/30 rounded-md p-3">
503
+ <i class="fas fa-shield-alt text-teal-600 text-2xl"></i>
504
+ </div>
505
+ <div class="ml-5 w-0 flex-1">
506
+ <dl>
507
+ <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Privileged Users</dt>
508
+ <dd class="text-3xl font-semibold text-gray-900 dark:text-white">{{{{ unprotectedPrivilegedUsers.length }}}}</dd>
509
+ <dd class="text-xs text-gray-500 dark:text-gray-400 mt-1">
510
+ Not in Protected Users
511
+ </dd>
512
+ </dl>
513
+ </div>
514
+ </div>
515
+ </div>
470
516
  </div>
471
517
  </div>
472
518
 
@@ -774,8 +820,12 @@ class DashboardGenerator:
774
820
  Attackers can request rogue certificates for domain administrators or other high-privilege accounts, achieving persistent domain compromise without account credentials.
775
821
  </p>
776
822
  <h3 class="font-semibold text-orange-800 dark:text-orange-200 mb-2">🔓 Exploitation Method</h3>
823
+ <p class="text-orange-700 dark:text-orange-300 text-sm mb-3">
824
+ Tools like <a href="https://github.com/ly4k/Certipy" target="_blank" class="tool-link">Certipy</a> or <a href="https://github.com/GhostPack/Certify" target="_blank" class="tool-link">Certify</a> identify vulnerable templates. Attackers request certificates with elevated permissions, then authenticate using the certificate to obtain TGTs or NTLM hashes.
825
+ </p>
826
+ <h3 class="font-semibold text-orange-800 dark:text-orange-200 mb-2">💡 Remediation</h3>
777
827
  <p class="text-orange-700 dark:text-orange-300 text-sm">
778
- Tools like Certipy or Certify identify vulnerable templates. Attackers request certificates with elevated permissions, then authenticate using the certificate to obtain TGTs or NTLM hashes.
828
+ <strong>Secure template permissions:</strong> Remove enrollment rights from Domain Users/Authenticated Users. Enable Manager Approval for high-risk templates. Set "This number of authorized signatures" to ≥1 for ESC1. Disable "Enrollee Supplies Subject" or require manager approval. Regularly audit certificate templates against ESC vulnerabilities using <a href="https://github.com/GhostPack/Certify" target="_blank" class="tool-link">Certify</a>, <a href="https://github.com/ly4k/Certipy" target="_blank" class="tool-link">Certipy</a> or <a href="https://github.com/TrimarcJake/Locksmith" target="_blank" class="tool-link">Locksmith</a>.
779
829
  </p>
780
830
  </div>
781
831
  <div class="table-container">
@@ -848,9 +898,13 @@ class DashboardGenerator:
848
898
  Any domain user can request TGS tickets for these accounts and perform offline password cracking without raising alerts. Weak or old passwords are particularly vulnerable.
849
899
  </p>
850
900
  <h3 class="font-semibold text-purple-800 dark:text-purple-200 mb-2">🔓 Exploitation Method</h3>
851
- <p class="text-purple-700 dark:text-purple-300 text-sm">
901
+ <p class="text-purple-700 dark:text-purple-300 text-sm mb-3">
852
902
  Tools: Rubeus, Invoke-Kerberoast, GetUserSPNs.py. Request TGS tickets, extract to disk, crack with Hashcat/John. Success = service account compromise.
853
903
  </p>
904
+ <h3 class="font-semibold text-purple-800 dark:text-purple-200 mb-2">💡 Remediation</h3>
905
+ <p class="text-purple-700 dark:text-purple-300 text-sm">
906
+ <strong>Use strong passwords (25+ characters) or better gMSA:</strong> Service accounts should use Group Managed Service Accounts (gMSA) which auto-rotate passwords. For standard accounts, enforce minimum 25-character passwords, rotate every 90 days, and implement least privilege. Remove unnecessary SPNs from user accounts.
907
+ </p>
854
908
  </div>
855
909
  <div class="table-container">
856
910
  <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
@@ -929,9 +983,13 @@ class DashboardGenerator:
929
983
  No authentication required - attackers can request AS-REP messages for these accounts without valid credentials and perform offline brute-force attacks.
930
984
  </p>
931
985
  <h3 class="font-semibold text-pink-800 dark:text-pink-200 mb-2">🔓 Exploitation Method</h3>
932
- <p class="text-pink-700 dark:text-pink-300 text-sm">
986
+ <p class="text-pink-700 dark:text-pink-300 text-sm mb-3">
933
987
  Tools: Rubeus, GetNPUsers.py. Request AS-REP for target accounts, extract encrypted portion, crack offline with Hashcat format 18200 or John.
934
988
  </p>
989
+ <h3 class="font-semibold text-pink-800 dark:text-pink-200 mb-2">💡 Remediation</h3>
990
+ <p class="text-pink-700 dark:text-pink-300 text-sm">
991
+ <strong>Enable Kerberos pre-authentication:</strong> Remove "Do not require Kerberos preauthentication" flag from all user accounts unless absolutely necessary for legacy applications. For required exceptions, enforce strong passwords (25+ characters), monitor for AS-REP requests, and consider isolating these accounts to restricted network segments.
992
+ </p>
935
993
  </div>
936
994
  <div class="table-container">
937
995
  <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
@@ -1010,9 +1068,13 @@ class DashboardGenerator:
1010
1068
  Instant account compromise without cracking. Attackers simply read user attributes to obtain valid credentials, enabling lateral movement and privilege escalation.
1011
1069
  </p>
1012
1070
  <h3 class="font-semibold text-red-800 dark:text-red-200 mb-2">🔓 Exploitation Method</h3>
1013
- <p class="text-red-700 dark:text-red-300 text-sm">
1071
+ <p class="text-red-700 dark:text-red-300 text-sm mb-3">
1014
1072
  LDAP queries or tools like BloodHound/PowerView enumerate user descriptions containing password patterns. Direct login with discovered credentials.
1015
1073
  </p>
1074
+ <h3 class="font-semibold text-red-800 dark:text-red-200 mb-2">💡 Remediation</h3>
1075
+ <p class="text-red-700 dark:text-red-300 text-sm">
1076
+ <strong>Immediately remove all passwords from user attributes:</strong> Clear 'description' and 'info' fields of all password-like strings. Never store secrets in LDAP attributes again. Force password resets for affected accounts. Implement security awareness training about proper credential storage. Regularly audit user attributes using scripts to detect and prevent future occurrences.
1077
+ </p>
1016
1078
  </div>
1017
1079
  <div class="table-container">
1018
1080
  <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
@@ -1064,9 +1126,13 @@ class DashboardGenerator:
1064
1126
  Compromised systems with readable LAPS passwords grant local administrator access, enabling credential dumping, persistence mechanisms, and lateral movement across the environment.
1065
1127
  </p>
1066
1128
  <h3 class="font-semibold text-indigo-800 dark:text-indigo-200 mb-2">🔓 Exploitation Method</h3>
1067
- <p class="text-indigo-700 dark:text-indigo-300 text-sm">
1129
+ <p class="text-indigo-700 dark:text-indigo-300 text-sm mb-3">
1068
1130
  Query AD attributes ms-Mcs-AdmPwd and ms-Mcs-AdmPwdExpirationTime. Tools: LAPSToolkit, PowerView. Use retrieved passwords for PSExec/WMI/RDP access.
1069
1131
  </p>
1132
+ <h3 class="font-semibold text-indigo-800 dark:text-indigo-200 mb-2">💡 Remediation</h3>
1133
+ <p class="text-indigo-700 dark:text-indigo-300 text-sm">
1134
+ <strong>Restrict LAPS password read permissions:</strong> Only designated IT admin groups should have read access to ms-Mcs-AdmPwd attribute. Remove "All Extended Rights" and grant only specific LAPS read permissions to authorized security groups. Regularly audit permissions using AccessChk or custom scripts. Consider implementing LAPS password rotation to limit exposure window.
1135
+ </p>
1070
1136
  </div>
1071
1137
  <div class="table-container">
1072
1138
  <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
@@ -1147,6 +1213,109 @@ class DashboardGenerator:
1147
1213
  </div>
1148
1214
  </div>
1149
1215
 
1216
+ <!-- KRBTGT Password Rotation -->
1217
+ <div id="krbtgt-section" v-if="krbtgtOldPassword.length > 0" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
1218
+ <h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
1219
+ <i class="fas fa-crown text-red-600"></i> KRBTGT Password Rotation ({{{{ krbtgtOldPassword.length }}}})
1220
+ </h2>
1221
+ <div class="bg-red-50 dark:bg-red-900/20 border-l-4 border-red-500 p-4 mb-6">
1222
+ <h3 class="font-semibold text-red-800 dark:text-red-200 mb-2">🎯 Issue & Impact</h3>
1223
+ <p class="text-red-700 dark:text-red-300 text-sm mb-3">
1224
+ The KRBTGT account password has not been rotated in over 365 days. This account is used to encrypt and sign all Kerberos tickets in the domain. A compromised KRBTGT password hash enables Golden Ticket attacks, providing persistent, undetectable domain dominance.
1225
+ </p>
1226
+ <h3 class="font-semibold text-red-800 dark:text-red-200 mb-2">⚔️ Attacker Benefit</h3>
1227
+ <p class="text-red-700 dark:text-red-300 text-sm mb-3">
1228
+ With the KRBTGT hash, attackers can forge Kerberos Ticket Granting Tickets (TGTs) for any user, including Domain Admins. These Golden Tickets remain valid even after password resets, providing persistent backdoor access. Detection is nearly impossible as forged tickets appear legitimate to all domain systems.
1229
+ </p>
1230
+ <h3 class="font-semibold text-red-800 dark:text-red-200 mb-2">🔓 Exploitation Method</h3>
1231
+ <p class="text-red-700 dark:text-red-300 text-sm mb-3">
1232
+ Tools: Mimikatz, Rubeus, Impacket's ticketer.py. Once KRBTGT hash is obtained (via DCSync or DC compromise), attackers craft Golden Tickets with arbitrary privileges. Tickets work across all domain resources without re-authentication. Old KRBTGT passwords that haven't been rotated remain valid indefinitely.
1233
+ </p>
1234
+ <h3 class="font-semibold text-red-800 dark:text-red-200 mb-2">💡 Remediation</h3>
1235
+ <p class="text-red-700 dark:text-red-300 text-sm">
1236
+ <strong>Rotate KRBTGT password twice</strong> with a 10-hour wait between rotations to invalidate all existing tickets. Use Microsoft's official script or KRBTGT Reset Toolkit. Establish a regular rotation schedule every 180 days and rotate immediately twice after any suspected compromise.
1237
+ </p>
1238
+ </div>
1239
+ <div class="table-container">
1240
+ <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
1241
+ <thead>
1242
+ <tr>
1243
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Account Name</th>
1244
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Password Last Set</th>
1245
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Password Age (days)</th>
1246
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Risk</th>
1247
+ </tr>
1248
+ </thead>
1249
+ <tbody class="divide-y divide-gray-200 dark:divide-gray-700">
1250
+ <tr v-for="account in krbtgtOldPassword" :key="account['SAM Account Name'] || account.SAMAccountName || account.UserName || account.User">
1251
+ <td class="px-6 py-4 whitespace-nowrap font-medium">{{{{ account['SAM Account Name'] || account.SAMAccountName || account.UserName || account.User || 'krbtgt' }}}}</td>
1252
+ <td class="px-6 py-4">{{{{ account['Password Last Set'] || account.PasswordLastSet || 'N/A' }}}}</td>
1253
+ <td class="px-6 py-4">
1254
+ <span :class="parseInt(account['Password Age (days)']) > 730 ? 'badge badge-critical' : 'badge badge-high'">
1255
+ {{{{ account['Password Age (days)'] }}}} days
1256
+ </span>
1257
+ </td>
1258
+ <td class="px-6 py-4">
1259
+ <span class="badge badge-critical">GOLDEN TICKET RISK</span>
1260
+ </td>
1261
+ </tr>
1262
+ </tbody>
1263
+ </table>
1264
+ </div>
1265
+ </div>
1266
+
1267
+ <!-- Unprotected Privileged Users -->
1268
+ <div id="protected-users-section" v-if="unprotectedPrivilegedUsers.length > 0" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
1269
+ <h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
1270
+ <i class="fas fa-shield-alt text-teal-600"></i> Unprotected Privileged Users ({{{{ unprotectedPrivilegedUsers.length }}}})
1271
+ </h2>
1272
+ <div class="bg-teal-50 dark:bg-teal-900/20 border-l-4 border-teal-500 p-4 mb-6">
1273
+ <h3 class="font-semibold text-teal-800 dark:text-teal-200 mb-2">🎯 Issue & Impact</h3>
1274
+ <p class="text-teal-700 dark:text-teal-300 text-sm mb-3">
1275
+ Privileged accounts (Domain Admins, Enterprise Admins, Administrators) not in the Protected Users security group lack critical protections against credential theft attacks. This group enforces non-delegable credentials, NTLMv2-only authentication, Kerberos AES encryption, and restrictions on credential caching.
1276
+ </p>
1277
+ <h3 class="font-semibold text-teal-800 dark:text-teal-200 mb-2">⚔️ Attacker Benefit</h3>
1278
+ <p class="text-teal-700 dark:text-teal-300 text-sm mb-3">
1279
+ Without Protected Users protections, privileged accounts are vulnerable to Pass-the-Hash, Pass-the-Ticket, credential delegation abuse, and offline attacks on weak/legacy encryption. Compromising these accounts grants domain-wide control.
1280
+ </p>
1281
+ <h3 class="font-semibold text-teal-800 dark:text-teal-200 mb-2">🔓 Exploitation Method</h3>
1282
+ <p class="text-teal-700 dark:text-teal-300 text-sm mb-3">
1283
+ Tools: Mimikatz (sekurlsa::logonpasswords, sekurlsa::tickets), Rubeus, Impacket. Extract credentials from LSASS memory, perform Pass-the-Hash/Pass-the-Ticket attacks. Unconstrained/constrained delegation allows ticket theft. RC4 encryption susceptible to offline cracking.
1284
+ </p>
1285
+ <h3 class="font-semibold text-teal-800 dark:text-teal-200 mb-2">💡 Remediation</h3>
1286
+ <p class="text-teal-700 dark:text-teal-300 text-sm">
1287
+ <strong>Add privileged accounts to Protected Users group:</strong> For Windows Server 2012 R2+ domains, add Domain Admins, Enterprise Admins, and other Tier 0 accounts to the "Protected Users" group. This enforces: no NTLM authentication (Kerberos only), no DES/RC4 encryption, no credential delegation, no password caching. Test compatibility before deployment as some legacy applications may break.
1288
+ </p>
1289
+ </div>
1290
+ <div class="table-container">
1291
+ <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
1292
+ <thead>
1293
+ <tr>
1294
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">SAM Account Name</th>
1295
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Name</th>
1296
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Status</th>
1297
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Last Logon</th>
1298
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Risk Level</th>
1299
+ </tr>
1300
+ </thead>
1301
+ <tbody class="divide-y divide-gray-200 dark:divide-gray-700">
1302
+ <tr v-for="user in unprotectedPrivilegedUsers" :key="user['SAM Account Name']">
1303
+ <td class="px-6 py-4 whitespace-nowrap font-medium">{{{{ user['SAM Account Name'] }}}}</td>
1304
+ <td class="px-6 py-4">{{{{ user.Name || 'N/A' }}}}</td>
1305
+ <td class="px-6 py-4 text-sm">{{{{ user.Status || 'N/A' }}}}</td>
1306
+ <td class="px-6 py-4 text-sm">{{{{ user['Last Logon'] || 'Never' }}}}</td>
1307
+ <td class="px-6 py-4">
1308
+ <span v-if="user['Risk Level'] === 'CRITICAL' || user['Risk Level'] === 'Critical'" class="badge badge-critical">CRITICAL</span>
1309
+ <span v-else-if="user['Risk Level'] === 'HIGH' || user['Risk Level'] === 'High'" class="badge badge-high">HIGH</span>
1310
+ <span v-else-if="user['Risk Level'] === 'MEDIUM' || user['Risk Level'] === 'Medium'" class="badge badge-medium">MEDIUM</span>
1311
+ <span v-else class="badge badge-low">{{{{ user['Risk Level'] || 'LOW' }}}}</span>
1312
+ </td>
1313
+ </tr>
1314
+ </tbody>
1315
+ </table>
1316
+ </div>
1317
+ </div>
1318
+
1150
1319
  <!-- Password Policy Analysis -->
1151
1320
  <div id="password-policy-section" v-if="passwordPolicy.length > 0" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
1152
1321
  <h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
@@ -1162,9 +1331,13 @@ class DashboardGenerator:
1162
1331
  Weak policies enable password spraying attacks, where attackers try common passwords against many accounts. Long password ages mean compromised passwords remain valid indefinitely. Weak lockout thresholds allow unlimited brute-force attempts.
1163
1332
  </p>
1164
1333
  <h3 class="font-semibold text-orange-800 dark:text-orange-200 mb-2">🔓 Exploitation Method</h3>
1165
- <p class="text-orange-700 dark:text-orange-300 text-sm">
1334
+ <p class="text-orange-700 dark:text-orange-300 text-sm mb-3">
1166
1335
  Tools: Hydra, Medusa, CrackMapExec. Attackers enumerate users, spray common passwords, escalate with compromised credentials. No complexity = simple passwords work.
1167
1336
  </p>
1337
+ <h3 class="font-semibold text-orange-800 dark:text-orange-200 mb-2">💡 Remediation</h3>
1338
+ <p class="text-orange-700 dark:text-orange-300 text-sm">
1339
+ <strong>Implement CIS-compliant password policy:</strong> Minimum 14 characters, maximum age 365 days (90 for privileged accounts), password history 24, lockout threshold 5 attempts, lockout duration 15 minutes. Enable password complexity requirements. Consider passphrase policy over traditional complexity. Implement Fine-Grained Password Policies (FGPP) for privileged accounts with stricter requirements.
1340
+ </p>
1168
1341
  </div>
1169
1342
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
1170
1343
  <div v-for="policy in passwordPolicy" :key="policy.Policy"
@@ -1722,6 +1895,12 @@ class DashboardGenerator:
1722
1895
  forest() {{
1723
1896
  return csvData.Forest || [];
1724
1897
  }},
1898
+ krbtgt() {{
1899
+ return csvData.krbtgt || [];
1900
+ }},
1901
+ protectedGroups() {{
1902
+ return csvData.ProtectedGroups || [];
1903
+ }},
1725
1904
 
1726
1905
  aboutInfo() {{
1727
1906
  const about = csvData.AboutPyADRecon || [];
@@ -1850,6 +2029,24 @@ class DashboardGenerator:
1850
2029
  return csvData.LAPS ? csvData.LAPS.filter(l => l.Readable === 'True' || l.Readable === 'TRUE') : [];
1851
2030
  }},
1852
2031
 
2032
+ krbtgtOldPassword() {{
2033
+ return this.krbtgt.filter(k => {{
2034
+ const pwdAge = parseInt(k['Password Age (days)']);
2035
+ return !isNaN(pwdAge) && pwdAge > 180;
2036
+ }});
2037
+ }},
2038
+
2039
+ unprotectedPrivilegedUsers() {{
2040
+ return this.protectedGroups.filter(u => {{
2041
+ // Users not in Protected Users group with Risk Level > Low
2042
+ const notInProtectedUsers = u['In Protected Users Group'] === 'No' || u['In Protected Users Group'] === 'NO';
2043
+ const isUser = u.Type === 'User' || u.Type === 'USER';
2044
+ const riskLevel = (u['Risk Level'] || '').toUpperCase();
2045
+ const highRisk = riskLevel === 'MEDIUM' || riskLevel === 'HIGH' || riskLevel === 'CRITICAL';
2046
+ return isUser && notInProtectedUsers && highRisk;
2047
+ }});
2048
+ }},
2049
+
1853
2050
  lapsEnabled() {{
1854
2051
  return this.laps.filter(l => l['LAPS Enabled'] === 'True' || l['LAPS Enabled'] === 'TRUE');
1855
2052
  }},
@@ -2782,8 +2979,7 @@ class DashboardGenerator:
2782
2979
  with open(self.output_file, 'w', encoding='utf-8') as f:
2783
2980
  f.write(html_content)
2784
2981
 
2785
- print(f"[+] Dashboard generated: {self.output_file}")
2786
- print(f" Open it in your browser to view the interactive report")
2982
+ print(f"[INFO] [+] Dashboard generated: {self.output_file}")
2787
2983
  return True
2788
2984
 
2789
2985
 
@@ -0,0 +1,3 @@
1
+ {
2
+ "version": "0.12.3"
3
+ }
@@ -103,7 +103,7 @@ if not KERBEROS_AVAILABLE:
103
103
 
104
104
 
105
105
  # Constants
106
- VERSION = "v0.12.2" # Automatically updated by CI/CD pipeline during release
106
+ VERSION = "v0.12.3" # Automatically updated by CI/CD pipeline during release
107
107
  BANNER = f"""
108
108
  ╔═════════════════════════════════════════════════════════
109
109
  ║ PyADRecon {VERSION} - Python AD Reconnaissance Tool
@@ -1,3 +0,0 @@
1
- {
2
- "version": "0.12.2"
3
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes