Skip to content

Commit 8d44ab4

Browse files
committed
Add more durability for badPasswordTime formats and force resolved DC's to be used the whole time to prevent mismatches in badPwdCount causing lockouts
1 parent 124b3ca commit 8d44ab4

2 files changed

Lines changed: 65 additions & 7 deletions

File tree

TheSprayer/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"profiles": {
33
"TheSprayer": {
44
"commandName": "Project",
5-
"commandLineArgs": "-p vagrant -U vagrant -P vagrant -d windomain.local -s 192.168.38.102"
5+
"commandLineArgs": "-p vagrant -U vagrant -P vagrant -d sevenkingdoms.local -s 192.168.56.10"
66
}
77
}
88
}

TheSprayer/Services/ActiveDirectoryService.cs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Net;
32
using System.DirectoryServices.Protocols;
43
using TheSprayer.Models;
54
using System.Collections.Generic;
@@ -12,6 +11,8 @@
1211
using System.Text;
1312
using System.Collections.Concurrent;
1413
using System.IO;
14+
using System.Net;
15+
using System.Net.Sockets;
1516

1617
namespace TheSprayer.Services
1718
{
@@ -23,13 +24,17 @@ public class ActiveDirectoryService
2324
private readonly string _domainUserPass;
2425
private readonly string _domainController;
2526
private readonly string _distinguishedName;
27+
private readonly string _resolvedDomainController;
28+
private readonly LdapDirectoryIdentifier _ldapIdentifier;
2629

2730
public ActiveDirectoryService(string domain, string domainUser, string domainUserPass, string domainController)
2831
{
2932
_domain = domain;
3033
_domainUser = domainUser;
3134
_domainUserPass = domainUserPass;
3235
_domainController = domainController;
36+
_resolvedDomainController = ResolveDomainController(domainController);
37+
_ldapIdentifier = new LdapDirectoryIdentifier(_resolvedDomainController, 389, false, false);
3338

3439
var splitDomain = _domain.Split('.');
3540
_distinguishedName = "";
@@ -206,12 +211,25 @@ public List<ActiveDirectoryUser> GetAllDomainUsers(IEnumerable<string> userSamAc
206211
// Loop through every response entry
207212
foreach (SearchResultEntry entry in searchResponse.Entries)
208213
{
209-
var sid = entry.Attributes.Contains("objectSid") && entry.Attributes["objectSid"].Count > 0
210-
? new SecurityIdentifier((byte[])entry.Attributes["objectSid"][0], 0).Value
211-
: null;
214+
string sid = null;
215+
if (entry.Attributes.Contains("objectSid") && entry.Attributes["objectSid"].Count > 0)
216+
{
217+
if (OperatingSystem.IsWindows())
218+
{
219+
sid = new SecurityIdentifier((byte[])entry.Attributes["objectSid"][0], 0).Value;
220+
}
221+
}
212222

213223
var badPwdCount = entry.Attributes.GetIfExists<int?>("badPwdCount");
214224
var lastBadPwdTime = entry.Attributes.GetIfExists<DateTime?>("badPasswordTime");
225+
226+
// Sometimes badPasswordTime is just "0" which means never
227+
if(lastBadPwdTime == null)
228+
{
229+
var intEntry = entry.Attributes.GetIfExists<int?>("badPasswordTime");
230+
lastBadPwdTime = intEntry == 0 ? entry.Attributes.GetIfExists<DateTime?>("createTimeStamp") : null;
231+
}
232+
215233
if (badPwdCount == null || lastBadPwdTime == null)
216234
{
217235
var sam = entry.Attributes.GetIfExists("sAMAccountName");
@@ -580,7 +598,8 @@ public static bool ShouldSprayUser(ActiveDirectoryUser user, PasswordPolicy defa
580598

581599
public bool TryValidateCredentials(string username, string password)
582600
{
583-
using var connection = new LdapConnection(new LdapDirectoryIdentifier(_domainController, 389, false, false));
601+
using var connection = new LdapConnection(_ldapIdentifier);
602+
connection.SessionOptions.ReferralChasing = ReferralChasingOptions.All;
584603
connection.Credential = new NetworkCredential(username, password, _domain);
585604

586605
try
@@ -596,12 +615,51 @@ public bool TryValidateCredentials(string username, string password)
596615

597616
private LdapConnection CreateLdapConnection()
598617
{
599-
var connection = new LdapConnection(new LdapDirectoryIdentifier(_domainController, 389, false, false));
618+
var connection = new LdapConnection(_ldapIdentifier);
619+
connection.SessionOptions.ReferralChasing = ReferralChasingOptions.All;
600620
if (!string.IsNullOrWhiteSpace(_domainUser) && !string.IsNullOrWhiteSpace(_domainUserPass))
601621
{
602622
connection.Credential = new NetworkCredential(_domainUser, _domainUserPass, _domain);
603623
}
604624
return connection;
605625
}
626+
627+
private static string ResolveDomainController(string domainController)
628+
{
629+
if (string.IsNullOrWhiteSpace(domainController))
630+
{
631+
throw new ArgumentException("Domain controller must be provided", nameof(domainController));
632+
}
633+
634+
if (IPAddress.TryParse(domainController, out _))
635+
{
636+
return domainController;
637+
}
638+
639+
try
640+
{
641+
var addresses = Dns.GetHostAddresses(domainController)
642+
.Where(a => a.AddressFamily == AddressFamily.InterNetwork || a.AddressFamily == AddressFamily.InterNetworkV6)
643+
.ToArray();
644+
645+
if (!addresses.Any())
646+
{
647+
ColorConsole.WriteLine($"Warning: Unable to resolve domain controller {domainController}, falling back to original value.", ConsoleColor.Yellow);
648+
return domainController;
649+
}
650+
651+
if (addresses.Length > 1)
652+
{
653+
ColorConsole.WriteLine($"Warning: Domain controller {domainController} resolves to multiple addresses; using {addresses[0]} for all operations.", ConsoleColor.Yellow);
654+
}
655+
656+
return addresses[0].ToString();
657+
}
658+
catch (Exception ex)
659+
{
660+
ColorConsole.WriteLine($"Warning: Failed to resolve domain controller {domainController}: {ex.Message}. Using supplied value.", ConsoleColor.Yellow);
661+
return domainController;
662+
}
663+
}
606664
}
607665
}

0 commit comments

Comments
 (0)