Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: NTLM Implementation #177

Open
wants to merge 3 commits into
base: v4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/CommonLib/Enums/CollectionMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ public enum CollectionMethod
CARegistry = 1 << 16,
DCRegistry = 1 << 17,
CertServices = 1 << 18,
LdapServices = 1 << 19,
WebClientService = 1 << 21,
SmbInfo = 1 << 22,
EventLogs = 1 << 23,
LocalGroups = DCOM | RDP | LocalAdmin | PSRemote,
ComputerOnly = LocalGroups | Session | UserRights | CARegistry | DCRegistry,
DCOnly = ACL | Container | Group | ObjectProps | Trusts | GPOLocalGroup | CertServices,
Default = Group | Session | Trusts | ACL | ObjectProps | LocalGroups | SPNTargets | Container | CertServices,
All = Default | LoggedOn | GPOLocalGroup | UserRights | CARegistry | DCRegistry
ComputerOnly = LocalGroups | Session | UserRights | CARegistry | DCRegistry | WebClientService | SmbInfo | EventLogs,
DCOnly = ACL | Container | Group | ObjectProps | Trusts | GPOLocalGroup | CertServices | LdapServices | SmbInfo,
Default = Group | Session | Trusts | ACL | ObjectProps | LocalGroups | SPNTargets | Container | CertServices | LdapServices | SmbInfo,
All = Default | LoggedOn | GPOLocalGroup | UserRights | CARegistry | DCRegistry | WebClientService | LdapServices | EventLogs
}
}
4 changes: 3 additions & 1 deletion src/CommonLib/Enums/LdapErrorCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
public enum LdapErrorCodes : int
{
Success = 0,
StrongAuthRequired = 8,
SaslBindInProgress = 14,
InvalidCredentials = 49,
Busy = 51,
ServerDown = 81,
LocalError = 82,
KerberosAuthType = 83
KerberosAuthType = 83,
}
}
14 changes: 14 additions & 0 deletions src/CommonLib/Enums/LdapOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace SharpHoundCommonLib.Enums
{
public enum LdapOption : int
{
Ssl = 0x0A,
ProtocolVersion = 0x11,
ResultCode = 0x31,
ServerError = 0x33,
ServerCertificate = 0x81,
Sign = 0x95,
Encrypt = 0x96,
Timeout = 0x5002,
}
}
15 changes: 15 additions & 0 deletions src/CommonLib/Enums/LdapOptionValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SharpHoundCommonLib.Enums
{
public enum LdapOptionValue : int
{
Off = 0,
On = 1,
Version3 = 3,
};
}
16 changes: 16 additions & 0 deletions src/CommonLib/Enums/LdapSupportedSaslMechansims.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SharpHoundCommonLib.Enums
{
public static class LdapSupportedSaslMechansims
{
public const string GSSAPI = "GSSAPI";
public const string GSS_SPNEGO = "GSS-SPNEGO";
public const string EXTERNAL = "EXTERNAL";
public const string DIGEST_MD5 = "DIGEST_MD5";
}
}
2 changes: 1 addition & 1 deletion src/CommonLib/LdapConnectionPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace SharpHoundCommonLib {
internal class LdapConnectionPool : IDisposable{
private readonly ConcurrentBag<LdapConnectionWrapper> _connections;
private readonly ConcurrentBag<LdapConnectionWrapper> _globalCatalogConnection;
private readonly SemaphoreSlim _semaphore;
private readonly SemaphoreSlim _semaphore = null;
private readonly string _identifier;
private readonly string _poolIdentifier;
private readonly LdapConfig _ldapConfig;
Expand Down
4 changes: 2 additions & 2 deletions src/CommonLib/LdapProducerQueryGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static GeneratedLdapParameters GenerateDefaultPartitionParameters(Collect
if (methods.HasFlag(CollectionMethod.SPNTargets))
properties.AddRange(CommonProperties.SPNTargetProps);

if (methods.HasFlag(CollectionMethod.DCRegistry))
if (methods.HasFlag(CollectionMethod.DCRegistry) || methods.HasFlag(CollectionMethod.LdapServices))
rvazarkar marked this conversation as resolved.
Show resolved Hide resolved
properties.AddRange(CommonProperties.ComputerMethodProps);

if (methods.HasFlag(CollectionMethod.SPNTargets)) {
Expand Down Expand Up @@ -79,7 +79,7 @@ public static GeneratedLdapParameters GenerateDefaultPartitionParameters(Collect
properties.AddRange(CommonProperties.GPOLocalGroupProps);
}

if (methods.HasFlag(CollectionMethod.DCRegistry)) {
if (methods.HasFlag(CollectionMethod.DCRegistry) || methods.HasFlag(CollectionMethod.LdapServices)) {
filter = filter.AddComputers(CommonFilters.DomainControllers);
properties.AddRange(CommonProperties.ComputerMethodProps);
}
Expand Down
68 changes: 39 additions & 29 deletions src/CommonLib/LdapUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public IAsyncEnumerable<LdapResult<IDirectoryObject>> PagedQuery(LdapQueryParame
} catch {
//pass
}


return (false, Label.Base);
}
Expand Down Expand Up @@ -227,7 +227,7 @@ public IAsyncEnumerable<LdapResult<IDirectoryObject>> PagedQuery(LdapQueryParame
} catch {
//pass
}


return (false, Label.Base);
}
Expand Down Expand Up @@ -361,7 +361,7 @@ public IAsyncEnumerable<LdapResult<IDirectoryObject>> PagedQuery(LdapQueryParame
} catch {
//pass
}


return (false, string.Empty);
}
Expand Down Expand Up @@ -904,7 +904,6 @@ public async Task<bool> IsDomainController(string computerObjectId, string domai
_unresolvablePrincipals.Add(distinguishedName);
return (false, default);
}

}

public async Task<(bool Success, string DSHeuristics)> GetDSHueristics(string domain, string dn) {
Expand Down Expand Up @@ -961,7 +960,7 @@ public async IAsyncEnumerable<OutputBase> GetWellKnownPrincipalOutput() {
yield return entdc;
}
}

private async IAsyncEnumerable<Group> GetEnterpriseDCGroups() {
var grouped = new ConcurrentDictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
var forestSidToName = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
Expand All @@ -985,6 +984,7 @@ await GetDomainSidFromDomainName(forestName) is (true, var forestDomainSid)) {
if (!forestSidToName.TryGetValue(f.Key, out var forestName)) {
continue;
}

var group = new Group { ObjectIdentifier = $"{forestName}-S-1-5-9" };
group.Properties.Add("name", $"ENTERPRISE DOMAIN CONTROLLERS@{forestName}".ToUpper());
group.Properties.Add("domainsid", f.Key);
Expand Down Expand Up @@ -1203,6 +1203,15 @@ await utils.GetDomainNameFromSid(objectIdentifier) is (true, var domainName)) {
return (true, res);
}

res.ObjectType = await ComputeLabel(directoryObject, objectIdentifier, domain, utils);

directoryObject.TryGetProperty(LDAPProperties.SAMAccountName, out var samAccountName);
res.DisplayName = ComputeDisplayName(directoryObject, domain, res.ObjectType, samAccountName);
return (true, res);
}

private static async Task<Label> ComputeLabel(IDirectoryObject directoryObject, string objectIdentifier,
string domain, ILdapUtils utils) {
if (!directoryObject.GetLabel(out var label)) {
if (await utils.ResolveIDAndType(objectIdentifier, domain) is (true, var typedPrincipal)) {
label = typedPrincipal.ObjectType;
Expand All @@ -1213,68 +1222,70 @@ await utils.GetDomainNameFromSid(objectIdentifier) is (true, var domainName)) {
label = Label.User;
}

res.ObjectType = label;

directoryObject.TryGetProperty(LDAPProperties.SAMAccountName, out var samAccountName);
return label;
}

private static string ComputeDisplayName(IDirectoryObject directoryObject, string domain, Label label,
string samAccountName) {
string displayName;
switch (label) {
case Label.User:
case Label.Group:
case Label.Base:
res.DisplayName = $"{samAccountName}@{domain}";
displayName = $"{samAccountName}@{domain}";
break;
case Label.Computer: {
var shortName = samAccountName?.TrimEnd('$');
if (directoryObject.TryGetProperty(LDAPProperties.DNSHostName, out var dns)) {
res.DisplayName = dns;
displayName = dns;
} else if (!string.IsNullOrWhiteSpace(shortName)) {
res.DisplayName = $"{shortName}.{domain}";
displayName = $"{shortName}.{domain}";
} else if (directoryObject.TryGetProperty(LDAPProperties.CanonicalName,
out var canonicalName)) {
res.DisplayName = $"{canonicalName}.{domain}";
displayName = $"{canonicalName}.{domain}";
} else if (directoryObject.TryGetProperty(LDAPProperties.Name, out var name)) {
res.DisplayName = $"{name}.{domain}";
displayName = $"{name}.{domain}";
} else {
res.DisplayName = $"UNKNOWN.{domain}";
displayName = $"UNKNOWN.{domain}";
}

break;
}
case Label.GPO:
case Label.IssuancePolicy: {
if (directoryObject.TryGetProperty(LDAPProperties.DisplayName, out var displayName)) {
res.DisplayName = $"{displayName}@{domain}";
if (directoryObject.TryGetProperty(LDAPProperties.DisplayName, out var ldapDisplayName)) {
displayName = $"{ldapDisplayName}@{domain}";
} else if (directoryObject.TryGetProperty(LDAPProperties.CanonicalName,
out var canonicalName)) {
res.DisplayName = $"{canonicalName}@{domain}";
displayName = $"{canonicalName}@{domain}";
} else {
res.DisplayName = $"UNKNOWN@{domain}";
displayName = $"UNKNOWN@{domain}";
}

break;
}
case Label.Domain:
res.DisplayName = domain;
displayName = domain;
break;
case Label.OU: {
if (directoryObject.TryGetProperty(LDAPProperties.Name, out var name)) {
res.DisplayName = $"{name}@{domain}";
displayName = $"{name}@{domain}";
} else if (directoryObject.TryGetProperty(LDAPProperties.OU, out var ou)) {
res.DisplayName = $"{ou}@{domain}";
displayName = $"{ou}@{domain}";
} else {
res.DisplayName = $"UNKNOWN@{domain}";
displayName = $"UNKNOWN@{domain}";
}

break;
}
case Label.Container: {
if (directoryObject.TryGetProperty(LDAPProperties.Name, out var name)) {
res.DisplayName = $"{name}@{domain}";
displayName = $"{name}@{domain}";
} else if (directoryObject.TryGetProperty(LDAPProperties.CanonicalName,
out var canonicalName)) {
res.DisplayName = $"{canonicalName}@{domain}";
displayName = $"{canonicalName}@{domain}";
} else {
res.DisplayName = $"UNKNOWN@{domain}";
displayName = $"UNKNOWN@{domain}";
}

break;
Expand All @@ -1286,9 +1297,9 @@ await utils.GetDomainNameFromSid(objectIdentifier) is (true, var domainName)) {
case Label.EnterpriseCA:
case Label.CertTemplate: {
if (directoryObject.TryGetProperty(LDAPProperties.Name, out var name)) {
res.DisplayName = $"{name}@{domain}";
displayName = $"{name}@{domain}";
} else {
res.DisplayName = $"UNKNOWN@{domain}";
displayName = $"UNKNOWN@{domain}";
}

break;
Expand All @@ -1297,8 +1308,7 @@ await utils.GetDomainNameFromSid(objectIdentifier) is (true, var domainName)) {
throw new ArgumentOutOfRangeException();
}

res.DisplayName = res.DisplayName.ToUpper();
return (true, res);
return displayName.ToUpper();
}
}
}
45 changes: 45 additions & 0 deletions src/CommonLib/Ntlm/HttpClientFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Net;
using System.Net.Http;

namespace SharpHoundCommonLib.Ntlm;

public interface IHttpClientFactory
{
HttpClient CreateUnauthenticatedClient();
HttpClient CreateAuthenticatedHttpClient(Uri Url, string authPackage = "Kerberos");
}

public class HttpClientFactory : IHttpClientFactory
{
public HttpClient CreateUnauthenticatedClient()
{
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true,
UseDefaultCredentials = false
};

return new HttpClient(handler);
}

public HttpClient CreateAuthenticatedHttpClient(Uri Url, string authPackage = "Kerberos")
{
var handler = new HttpClientHandler
{
Credentials = new CredentialCache()
{
{ Url, authPackage, CredentialCache.DefaultNetworkCredentials }
},

PreAuthenticate = true,
ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) =>
{
return true;
},
};

return new HttpClient(handler);
}
}
Loading
Loading