Skip to content

Commit

Permalink
Merge pull request #376 from HotCakeX/Harden-Windows-Security-v0.6.8
Browse files Browse the repository at this point in the history
Harden Windows Security v0.6.8
  • Loading branch information
HotCakeX authored Oct 29, 2024
2 parents f64de48 + 148c5ca commit 2b8bec7
Show file tree
Hide file tree
Showing 109 changed files with 1,345 additions and 1,319 deletions.
3 changes: 3 additions & 0 deletions Harden-Windows-Security Module/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,6 @@ dotnet_diagnostic.CA2265.severity = error

# CA1507: Use nameof to express symbol names
dotnet_diagnostic.CA1507.severity = error

# IDE0001: Simplify name
dotnet_diagnostic.IDE0001.severity = error
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
System
System.IO
System.Xml
System.Net
System.Data
WindowsBase
System.Xaml
System.Linq
System.Memory
System.Console
System.Windows
System.CodeDom
System.Runtime
System.Net.Http
System.Xml.Linq
Expand All @@ -30,11 +28,9 @@ System.Threading.Tasks
System.Linq.Expressions
System.Threading.Thread
System.Xml.ReaderWriter
WindowsFormsIntegration
System.DirectoryServices
Microsoft.Win32.Registry
System.Security.Principal
System.Reflection.Metadata
System.Diagnostics.Process
Microsoft.Win32.Primitives
System.Diagnostics.EventLog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ public static void AddTpmAndStartupKeyProtector(string DriveLetter, string Start
{
// If the key wasn't saved successfully, remove the protector as a safety measure

var deleteKeyProtectorArgs = VolumeInfo.GetMethodParameters("DeleteKeyProtector");
ManagementBaseObject deleteKeyProtectorArgs = VolumeInfo.GetMethodParameters("DeleteKeyProtector");
deleteKeyProtectorArgs["VolumeKeyProtectorID"] = ProtectKeyWithTPMAndStartupKeyMethodInvocationResult["VolumeKeyProtectorID"];
_ = VolumeInfo.InvokeMethod("DeleteKeyProtector", deleteKeyProtectorArgs, null);

Expand Down Expand Up @@ -619,7 +619,7 @@ public static void AddSidProtector(string DriveLetter, string SID, bool ServiceA
SecurityIdentifier SIDResult = new(SID);

// Prepare the method with arguments
var protectWithSidArgs = VolumeInfo.GetMethodParameters("ProtectKeyWithAdSid");
ManagementBaseObject protectWithSidArgs = VolumeInfo.GetMethodParameters("ProtectKeyWithAdSid");
protectWithSidArgs["FriendlyName"] = null;
protectWithSidArgs["SidString"] = SIDResult.Value;
protectWithSidArgs["Flags"] = flags;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public partial class BitLocker
{

// Class that stores the information about each key protector
public class KeyProtector
public sealed class KeyProtector
{
public KeyProtectorType? KeyProtectorType { get; set; }
public string? KeyProtectorID { get; set; }
Expand All @@ -33,7 +33,7 @@ public class KeyProtector
}

// class that stores the information about BitLocker protected volumes
public class BitLockerVolume
public sealed class BitLockerVolume
{
public string? MountPoint { get; set; }
public EncryptionMethod? EncryptionMethod { get; set; }
Expand Down Expand Up @@ -224,7 +224,7 @@ public static BitLockerVolume GetEncryptedVolumeInfo(string targetVolume)
try
{
// Try to use the GetEncryptionMethod method to get the EncryptionMethod and EncryptionMethodFlags properties
var currentEncryptionMethod = InvokeCimMethod(volume, "GetEncryptionMethod", null);
ManagementBaseObject? currentEncryptionMethod = InvokeCimMethod(volume, "GetEncryptionMethod", null);
if (currentEncryptionMethod is not null && Convert.ToUInt32(currentEncryptionMethod["ReturnValue"], CultureInfo.InvariantCulture) == 0)
{
uint EncryptionMethodNum = Convert.ToUInt32(currentEncryptionMethod["EncryptionMethod"], CultureInfo.InvariantCulture);
Expand Down Expand Up @@ -470,21 +470,15 @@ public static List<BitLockerVolume> GetAllEncryptedVolumeInfo(bool OnlyNonOSDriv
BitLockerVolume volume = GetEncryptedVolumeInfo(driveLetter + ":");

// If only Non-OS Drives are requested, skip any other drive types
if (OnlyNonOSDrives)
if (OnlyNonOSDrives && volume.VolumeType is not VolumeType.FixedDisk)
{
if (volume.VolumeType is not VolumeType.FixedDisk)
{
continue;
}
continue;
}

// If only Removable Drives are requested, skip any other drive types
if (OnlyRemovableDrives)
if (OnlyRemovableDrives && volume.VolumeType is not VolumeType.Removable)
{
if (volume.VolumeType is not VolumeType.Removable)
{
continue;
}
continue;
}

volumes.Add(volume);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,18 @@ internal static void Disable(string DriveLetter)
if (HasErrorsOccurred) { return; }


if (VolumeInfoExtended.ConversionStatus is BitLocker.ConversionStatus.FullyDecrypted)
if (VolumeInfoExtended.ConversionStatus is ConversionStatus.FullyDecrypted)
{
Logger.LogMessage($"The drive {DriveLetter} is already decrypted", LogTypeIntel.InformationInteractionRequired);
return;
}

if (VolumeInfoExtended.ConversionStatus is ConversionStatus.DecryptionInProgress)
{
Logger.LogMessage($"The drive {DriveLetter} is being decrypted, please wait.", LogTypeIntel.InformationInteractionRequired);
return;
}


if (VolumeInfoExtended.VolumeType is VolumeType.OperationSystem)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ internal static void Enable(string DriveLetter, OSEncryptionType OSEncryptionTyp
if (HasErrorsOccurred) { return; }

// If the drive is fully encrypted, check its key protectors
if (VolumeInfoExtended.ConversionStatus is BitLocker.ConversionStatus.FullyEncrypted)
if (VolumeInfoExtended.ConversionStatus is ConversionStatus.FullyEncrypted)
{
Logger.LogMessage($"The OS drive is fully encrypted, will check if it conforms to the selected {OSEncryptionType} level.", LogTypeIntel.Information);

Expand All @@ -74,7 +74,7 @@ internal static void Enable(string DriveLetter, OSEncryptionType OSEncryptionTyp


// Get the key protectors of the OS Drive after making sure it is fully encrypted
List<BitLocker.KeyProtectorType?> KeyProtectors = VolumeInfoExtended.KeyProtector!
List<KeyProtectorType?> KeyProtectors = VolumeInfoExtended.KeyProtector!
.Select(kp => kp.KeyProtectorType).ToList();

if (KeyProtectors is null || KeyProtectors.Count == 0)
Expand All @@ -85,7 +85,7 @@ internal static void Enable(string DriveLetter, OSEncryptionType OSEncryptionTyp
}

// If Normal security level was selected
if (OSEncryptionType is BitLocker.OSEncryptionType.Normal)
if (OSEncryptionType is OSEncryptionType.Normal)
{
// If all the required key protectors for Normal security level are present, then return from the method
if (KeyProtectors.Contains(BitLocker.KeyProtectorType.RecoveryPassword) && KeyProtectors.Contains(BitLocker.KeyProtectorType.TpmPin))
Expand Down Expand Up @@ -188,7 +188,7 @@ internal static void Enable(string DriveLetter, OSEncryptionType OSEncryptionTyp
}

// Continue with full encryption if the drive is fully decrypted
else if (VolumeInfoExtended.ConversionStatus is BitLocker.ConversionStatus.FullyDecrypted)
else if (VolumeInfoExtended.ConversionStatus is ConversionStatus.FullyDecrypted)
{

// Prepare the method with arguments
Expand Down Expand Up @@ -348,7 +348,7 @@ internal static void Enable(string DriveLetter, bool FreePlusUsedSpace)


// Make sure the OS Drive is encrypted first, or else we would add recovery password key protector and then get error about the same problem during auto-unlock key protector enablement
BitLockerVolume OSDriveVolumeInfo = HardenWindowsSecurity.BitLocker.GetEncryptedVolumeInfo(Environment.GetEnvironmentVariable("SystemDrive") ?? "C:\\");
BitLockerVolume OSDriveVolumeInfo = BitLocker.GetEncryptedVolumeInfo(Environment.GetEnvironmentVariable("SystemDrive") ?? "C:\\");
if (OSDriveVolumeInfo.ProtectionStatus is not BitLocker.ProtectionStatus.Protected)
{
Logger.LogMessage($"Operation System drive must be encrypted first before encrypting Non-OS drives.", LogTypeIntel.ErrorInteractionRequired);
Expand All @@ -357,7 +357,7 @@ internal static void Enable(string DriveLetter, bool FreePlusUsedSpace)
}

// If the drive is already fully encrypted, check its key protectors
if (VolumeInfoExtended.ConversionStatus is BitLocker.ConversionStatus.FullyEncrypted)
if (VolumeInfoExtended.ConversionStatus is ConversionStatus.FullyEncrypted)
{

Logger.LogMessage($"The drive {DriveLetter} is fully encrypted, will check its key protectors.", LogTypeIntel.Information);
Expand All @@ -369,7 +369,7 @@ internal static void Enable(string DriveLetter, bool FreePlusUsedSpace)
}

// Get the key protectors of the Drive after making sure it is fully encrypted
List<BitLocker.KeyProtectorType?> KeyProtectors = VolumeInfoExtended.KeyProtector!
List<KeyProtectorType?> KeyProtectors = VolumeInfoExtended.KeyProtector!
.Select(kp => kp.KeyProtectorType).ToList();

if (KeyProtectors is null || KeyProtectors.Count == 0)
Expand All @@ -385,7 +385,7 @@ internal static void Enable(string DriveLetter, bool FreePlusUsedSpace)

#region
// Delete any possible old leftover ExternalKey key protectors
List<BitLocker.KeyProtector> ExternalKeys = VolumeInfoExtended.KeyProtector!.Where(kp => kp.KeyProtectorType is KeyProtectorType.ExternalKey).ToList();
List<KeyProtector> ExternalKeys = VolumeInfoExtended.KeyProtector!.Where(kp => kp.KeyProtectorType is KeyProtectorType.ExternalKey).ToList();

// This step ensures any leftover or unbound external key key protectors will be removed and a working one will be added
// If the current one is working and bound, it won't be removed and will be gracefully skipped over.
Expand Down Expand Up @@ -430,11 +430,11 @@ internal static void Enable(string DriveLetter, bool FreePlusUsedSpace)
#region
// Check for presence of multiple recovery password key protectors

List<BitLocker.KeyProtector> PasswordProtectors = VolumeInfoExtended.KeyProtector!.Where(kp => kp.KeyProtectorType is KeyProtectorType.RecoveryPassword).ToList();
List<KeyProtector> PasswordProtectors = VolumeInfoExtended.KeyProtector!.Where(kp => kp.KeyProtectorType is KeyProtectorType.RecoveryPassword).ToList();

if (PasswordProtectors.Count > 1)
{
Logger.LogMessage($"drive {DriveLetter} has {PasswordProtectors} recovery password key protectors. Usually only one is enough.", LogTypeIntel.Information);
Logger.LogMessage($"drive {DriveLetter} has {PasswordProtectors.Count} recovery password key protectors. Usually only one is enough.", LogTypeIntel.Information);
}
#endregion

Expand Down Expand Up @@ -468,7 +468,7 @@ internal static void Enable(string DriveLetter, bool FreePlusUsedSpace)
}

// If the drive is fully decrypted, begin full drive encryption
else if (VolumeInfoExtended.ConversionStatus is BitLocker.ConversionStatus.FullyDecrypted)
else if (VolumeInfoExtended.ConversionStatus is ConversionStatus.FullyDecrypted)
{

// Prepare the method with arguments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ public static void RemoveKeyProtector(string DriveLetter, string KeyProtectorID,
}


if (DetectedKeyProtector.KeyProtectorType is BitLocker.KeyProtectorType.TpmNetworkKey)
if (DetectedKeyProtector.KeyProtectorType is KeyProtectorType.TpmNetworkKey)
{
Logger.LogMessage($"The detected Key Protector type is TpmNetworkKey, it must be disabled and removed using group policies.", LogTypeIntel.Warning);
return;
}


if (DetectedKeyProtector.KeyProtectorType is BitLocker.KeyProtectorType.PublicKey)
if (DetectedKeyProtector.KeyProtectorType is KeyProtectorType.PublicKey)
{
Logger.LogMessage($"Removal of PublicKey type key protector not supported yet.", LogTypeIntel.Warning);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static dynamic GetMpComputerStatus()
// Make sure the results isn't empty
if (results.Count > 0)
{
var result = results.Cast<ManagementBaseObject>().FirstOrDefault();
ManagementBaseObject? result = results.Cast<ManagementBaseObject>().FirstOrDefault();

if (result is not null)
{
Expand All @@ -41,13 +41,13 @@ public static dynamic GetMpComputerStatus()
}
else
{
throw new HardenWindowsSecurity.PowerShellExecutionException("WMI query for 'MSFT_MpComputerStatus' failed");
throw new PowerShellExecutionException("WMI query for 'MSFT_MpComputerStatus' failed");
}
}
catch (ManagementException ex)
{
string errorMessage = $"WMI query for 'MSFT_MpComputerStatus' failed: {ex.Message}";
throw new HardenWindowsSecurity.PowerShellExecutionException(errorMessage, ex);
throw new PowerShellExecutionException(errorMessage, ex);
}
}

Expand All @@ -56,9 +56,10 @@ private static dynamic ConvertToDynamic(ManagementBaseObject managementObject)
{
// Creating a dynamic object to store the properties of the ManagementBaseObject
dynamic expandoObject = new ExpandoObject();
var dictionary = (IDictionary<string, object>)expandoObject;

foreach (var property in managementObject.Properties)
IDictionary<string, object> dictionary = expandoObject;

foreach (PropertyData property in managementObject.Properties)
{
if (property.Type == CimType.DateTime && property.Value is string dmtfTime)
{
Expand Down Expand Up @@ -102,14 +103,14 @@ public static void ManageMpPreference<T>(string preferenceName, T preferenceValu
try
{
// Connect to the WMI namespace
var scope = new ManagementScope(@"\\.\ROOT\Microsoft\Windows\Defender");
ManagementScope scope = new(@"\\.\ROOT\Microsoft\Windows\Defender");
scope.Connect();

// Create an instance of the MSFT_MpPreference class
using var mpPreferenceClass = new ManagementClass(scope, new ManagementPath("MSFT_MpPreference"), null);
using ManagementClass mpPreferenceClass = new(scope, new ManagementPath("MSFT_MpPreference"), null);

// Get the available methods for the class
var methodParams = mpPreferenceClass.GetMethodParameters(MethodName);
ManagementBaseObject methodParams = mpPreferenceClass.GetMethodParameters(MethodName);

if (preferenceValue is null)
{
Expand Down Expand Up @@ -157,11 +158,11 @@ public static void ManageMpPreference<T>(string preferenceName, T preferenceValu
// Invoke the method to apply the settings
_ = mpPreferenceClass.InvokeMethod(MethodName, methodParams, null);

HardenWindowsSecurity.Logger.LogMessage($"{preferenceName} set to {preferenceValue} (Type: {typeof(T).Name}) successfully.", LogTypeIntel.Information);
Logger.LogMessage($"{preferenceName} set to {preferenceValue} (Type: {typeof(T).Name}) successfully.", LogTypeIntel.Information);
}
catch (Exception ex)
{
HardenWindowsSecurity.Logger.LogMessage($"Error setting {preferenceName}: {ex.Message}- You might need to update your OS first.", LogTypeIntel.Warning);
Logger.LogMessage($"Error setting {preferenceName}: {ex.Message}- You might need to update your OS first.", LogTypeIntel.Warning);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ public static List<ManagementObject> GetFirewallRules(string ruleGroup, ushort d
// catch exceptions specific to WMI
catch (ManagementException mex)
{
HardenWindowsSecurity.Logger.LogMessage($"WMI ManagementException: {mex.Message}", LogTypeIntel.Error);
Logger.LogMessage($"WMI ManagementException: {mex.Message}", LogTypeIntel.Error);
}
// Catch block for unauthorized access exceptions
catch (UnauthorizedAccessException uex)
{
HardenWindowsSecurity.Logger.LogMessage($"UnauthorizedAccessException: {uex.Message}", LogTypeIntel.Error);
Logger.LogMessage($"UnauthorizedAccessException: {uex.Message}", LogTypeIntel.Error);
}
// General catch block for any other exceptions
catch (Exception ex)
{
HardenWindowsSecurity.Logger.LogMessage($"An error occurred: {ex.Message}", LogTypeIntel.Error);
Logger.LogMessage($"An error occurred: {ex.Message}", LogTypeIntel.Error);
}

return results;
Expand Down Expand Up @@ -254,10 +254,10 @@ void DeleteFirewallRules(CimSession cimSession, string ruleName, string policySt
options.SetCustomOption("PolicyStore", policyStore, mustComply: true);

// Check for existing rules with the same name and delete them
var existingRules = cimSession.EnumerateInstances("root/StandardCimv2", "MSFT_NetFirewallRule", options)
IEnumerable<CimInstance> existingRules = cimSession.EnumerateInstances("root/StandardCimv2", "MSFT_NetFirewallRule", options)
.Where(instance => instance.CimInstanceProperties["ElementName"].Value.ToString() == ruleName);

foreach (var rule in existingRules)
foreach (CimInstance rule in existingRules)
{
cimSession.DeleteInstance("root/StandardCimv2", rule, options);
Logger.LogMessage($"Deleted existing firewall rule: {ruleName}", LogTypeIntel.Information);
Expand Down
Loading

0 comments on commit 2b8bec7

Please sign in to comment.