diff --git a/src/CommonLib/Enums/LdapErrorCodes.cs b/src/CommonLib/Enums/LdapErrorCodes.cs index 942b4546..31e61890 100644 --- a/src/CommonLib/Enums/LdapErrorCodes.cs +++ b/src/CommonLib/Enums/LdapErrorCodes.cs @@ -4,5 +4,6 @@ public enum LdapErrorCodes : int { Success = 0, Busy = 51, + ServerDown = 81 } } \ No newline at end of file diff --git a/src/CommonLib/LDAPUtils.cs b/src/CommonLib/LDAPUtils.cs index b56dce32..2b539f86 100644 --- a/src/CommonLib/LDAPUtils.cs +++ b/src/CommonLib/LDAPUtils.cs @@ -866,9 +866,23 @@ public IEnumerable QueryLDAP(string ldapFilter, SearchScope if (response != null) pageResponse = (PageResultResponseControl)response.Controls .Where(x => x is PageResultResponseControl).DefaultIfEmpty(null).FirstOrDefault(); - } - catch (LdapException le) when (le.ErrorCode == (int)LdapErrorCodes.Busy && retryCount < MaxRetries) + }catch (LdapException le) when (le.ErrorCode == (int)LdapErrorCodes.ServerDown && + retryCount < MaxRetries) { + retryCount++; + Thread.Sleep(backoffDelay); + backoffDelay = TimeSpan.FromSeconds(Math.Min( + backoffDelay.TotalSeconds * BackoffDelayMultiplier.TotalSeconds, MaxBackoffDelay.TotalSeconds)); + conn = CreateNewConnection(domainName, globalCatalog, skipCache); + if (conn == null) + { + _log.LogError("Unable to create replacement ldap connection for ServerDown exception. Breaking loop"); + yield break; + } + + _log.LogInformation("Created new LDAP connection after receiving ServerDown from server"); + continue; + }catch (LdapException le) when (le.ErrorCode == (int)LdapErrorCodes.Busy && retryCount < MaxRetries) { retryCount++; Thread.Sleep(backoffDelay); backoffDelay = TimeSpan.FromSeconds(Math.Min( @@ -920,6 +934,22 @@ public IEnumerable QueryLDAP(string ldapFilter, SearchScope } } + private LdapConnection CreateNewConnection(string domainName = null, bool globalCatalog = false, bool skipCache = false) + { + var task = globalCatalog + ? Task.Run(() => CreateGlobalCatalogConnection(domainName, _ldapConfig.AuthType)) + : Task.Run(() => CreateLDAPConnection(domainName, skipCache, _ldapConfig.AuthType)); + + try + { + return task.ConfigureAwait(false).GetAwaiter().GetResult(); + } + catch + { + return null; + } + } + /// /// Performs an LDAP query using the parameters specified by the user. /// @@ -983,6 +1013,23 @@ public virtual IEnumerable QueryLDAP(string ldapFilter, Sear backoffDelay.TotalSeconds * BackoffDelayMultiplier.TotalSeconds, MaxBackoffDelay.TotalSeconds)); continue; } + catch (LdapException le) when (le.ErrorCode == (int)LdapErrorCodes.ServerDown && + retryCount < MaxRetries) + { + retryCount++; + Thread.Sleep(backoffDelay); + backoffDelay = TimeSpan.FromSeconds(Math.Min( + backoffDelay.TotalSeconds * BackoffDelayMultiplier.TotalSeconds, MaxBackoffDelay.TotalSeconds)); + conn = CreateNewConnection(domainName, globalCatalog, skipCache); + if (conn == null) + { + _log.LogError("Unable to create replacement ldap connection for ServerDown exception. Breaking loop"); + yield break; + } + + _log.LogInformation("Created new LDAP connection after receiving ServerDown from server"); + continue; + } catch (LdapException le) { if (le.ErrorCode != 82) diff --git a/src/CommonLib/SharpHoundCommonLib.csproj b/src/CommonLib/SharpHoundCommonLib.csproj index 864a183a..ebe26b0f 100644 --- a/src/CommonLib/SharpHoundCommonLib.csproj +++ b/src/CommonLib/SharpHoundCommonLib.csproj @@ -9,7 +9,7 @@ Common library for C# BloodHound enumeration tasks GPL-3.0-only https://github.com/BloodHoundAD/SharpHoundCommon - 3.0.8 + 3.0.9 SharpHoundCommonLib SharpHoundCommonLib diff --git a/test/unit/LDAPPropertyTests.cs b/test/unit/LDAPPropertyTests.cs index 0a6c06df..ab71cf83 100644 --- a/test/unit/LDAPPropertyTests.cs +++ b/test/unit/LDAPPropertyTests.cs @@ -641,8 +641,7 @@ public void LDAPPropertyProcessor_ReadAIACAProperties() {"name", "DUMPSTER-DC01-CA@DUMPSTER.FIRE"}, {"domainsid", "S-1-5-21-2697957641-2271029196-387917394"}, {"whencreated", 1683986131}, - {"crosscertificatepair", new[] - {"AQIDBAUGBwg="}} + {"hascrosscertificatepair", true}, }, "2F9F3630-F46A-49BF-B186-6629994EBCF9", Label.AIACA); var test = LDAPPropertyProcessor.ReadAIACAProperties(mock);