Skip to content

Commit

Permalink
Harden Windows Security Module v0.4.6 (#289)
Browse files Browse the repository at this point in the history
Harden Windows Security Module v0.4.6
  • Loading branch information
HotCakeX authored Jul 7, 2024
1 parent b5cb2e3 commit 5960633
Show file tree
Hide file tree
Showing 77 changed files with 5,151 additions and 4,418 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"Lanman",
"LGPO",
"LICENSEURI",
"Linq",
"LLMNR",
"LMHOSTS",
"lockstatus",
Expand Down
220 changes: 220 additions & 0 deletions Harden-Windows-Security Module/Main files/C#/AsyncDownloader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using System.IO.Compression;

namespace HardeningModule
{
public class FileDownloader
{
// HttpClient instance to be used and re-used for downloading files
private static readonly HttpClient _httpClient = new HttpClient();

// Dictionary to map URLs to their local file paths
private static readonly Dictionary<string, string> fileDictionary = new Dictionary<string, string>
{
{
"https://download.microsoft.com/download/8/5/C/85C25433-A1B0-4FFA-9429-7E023E7DA8D8/Windows%2011%20v23H2%20Security%20Baseline.zip",
"MicrosoftSecurityBaseline.zip"
},
{
"https://download.microsoft.com/download/8/5/C/85C25433-A1B0-4FFA-9429-7E023E7DA8D8/Microsoft%20365%20Apps%20for%20Enterprise%202306.zip",
"Microsoft365SecurityBaseline.zip"
},
{
"https://download.microsoft.com/download/8/5/C/85C25433-A1B0-4FFA-9429-7E023E7DA8D8/LGPO.zip",
"LGPO.zip"
}
};

/// <summary>
/// Synchronously checks if all URLs are valid and accessible.
/// </summary>
/// <exception cref="Exception">Thrown if any URL is invalid or inaccessible.</exception>
private static void CheckUrls()
{
foreach (var url in fileDictionary.Keys)
{
try
{
var response = _httpClient.GetAsync(url).Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception($"URL check failed for {url} with status code {response.StatusCode}");
}
}
catch (Exception ex)
{
throw new Exception($"Failed to access {url}: {ex.Message}", ex);
}
}
}

/// <summary>
/// Asynchronously starts the file download process for multiple files.
/// </summary>
/// <param name="workingDir">The directory where files will be downloaded.</param>
/// <exception cref="DirectoryNotFoundException">Thrown if the specified directory does not exist.</exception>
private static async Task StartFileDownloadAsync(string workingDir)
{
// Check if the working directory exists; throw an exception if it does not
if (!Directory.Exists(workingDir))
{
throw new DirectoryNotFoundException($"The directory '{workingDir}' does not exist.");
}

// Check if all URLs are valid and accessible
// Not necessary as the async method outputs proper errors and data to verify success/failure
// CheckUrls();

List<Task> tasks = new List<Task>();

// Start asynchronous download for each file
foreach (var kvp in fileDictionary)
{
string url = kvp.Key;
string fileName = kvp.Value;
string filePath = Path.Combine(workingDir, fileName);
tasks.Add(DownloadFileAsync(url, filePath));
}

try
{
// Wait for all download tasks to complete
await Task.WhenAll(tasks);
}
catch (Exception)
{
// Re-throw the exception to propagate it further if needed
throw;
}
}

/// <summary>
/// Asynchronously downloads a file from the specified URL to the specified file path.
/// </summary>
/// <param name="url">The URL of the file to download.</param>
/// <param name="filePath">The local file path where the downloaded file will be saved.</param>
/// <exception cref="Exception">Thrown if downloading or saving the file fails.</exception>
private static async Task DownloadFileAsync(string url, string filePath)
{
try
{
// Send GET request to download the file
using (var response = await _httpClient.GetAsync(url))
{
// Ensure the response indicates success
response.EnsureSuccessStatusCode();

// Open file stream to save the downloaded content
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
// Copy the content from the HTTP response to the file stream
await response.Content.CopyToAsync(fs);
}
}
}
catch (Exception ex)
{
// Throw a new exception with a meaningful message indicating the failure
throw new Exception($"Failed to download {url}: {ex.Message}", ex);
}
}

// To get the Security baselines directories that have dynamic names by getting the first directory after the base path
// e.g., inputting this in Get-ChildItem "C:\Users\HotCakeX\AppData\Local\Temp\HardeningXStuff\MicrosoftSecurityBaseline\*\"
// would show these "C:\Users\HotCakeX\AppData\Local\Temp\HardeningXStuff\MicrosoftSecurityBaseline\Windows 11 v23H2 Security Baseline"
// This method does the same thing but in C#
private static string GetSubDirectoryName(string basePath)
{
if (Directory.Exists(basePath))
{
string[] subDirectories = Directory.GetDirectories(basePath);
if (subDirectories.Length > 0)
{
// Assuming you want the first subdirectory found
string fullPath = subDirectories[0];
return fullPath;
}
}
return string.Empty;
}

// The main method of this class, called from the Protected-WindowsSecurity cmdlet
// First checks if the module is running in offline mode, if not, it starts the download process asynchronously
// Then extracts the downloaded files to the working directory
// If the module is running in offline mode, it copies the files from the user provided paths to the working directory
// Finally, it extracts the downloaded zip files to the working directory
// It also copies the LGPO.exe to the Microsoft Security Baseline and Microsoft 365 Security Baseline folders
// so that it can be used by the PowerShell script
public static void PrepDownloadedFiles(bool GUI = false, string LGPOPath = null, string MSFTSecurityBaselinesPath = null, string MSFT365AppsSecurityBaselinesPath = null)
{
// Only download if offline is not used
if (!HardeningModule.GlobalVars.Offline)
{

// Start the download process asynchronously
Task DownloadsTask = HardeningModule.FileDownloader.StartFileDownloadAsync(workingDir: HardeningModule.GlobalVars.WorkingDir);

while (!DownloadsTask.IsCompleted)
{
// Wait for 500 milliseconds before checking again
System.Threading.Thread.Sleep(50);
}

if (DownloadsTask.IsFaulted)
{
throw new Exception(DownloadsTask.Exception.Message);
}
else if (DownloadsTask.IsCompletedSuccessfully)
{
// Console.WriteLine("Download completed successfully");
}

}


if (HardeningModule.GlobalVars.Offline)
{
// 'Offline Mode; Copying the Microsoft Security Baselines, Microsoft 365 Apps for Enterprise Security Baselines and LGPO files from the user provided paths to the working directory'

System.IO.File.Copy(LGPOPath, Path.Combine(HardeningModule.GlobalVars.WorkingDir, "LGPO.zip"), true);

System.IO.File.Copy(MSFTSecurityBaselinesPath, Path.Combine(HardeningModule.GlobalVars.WorkingDir, "MicrosoftSecurityBaseline.zip"), true);

System.IO.File.Copy(MSFT365AppsSecurityBaselinesPath, Path.Combine(HardeningModule.GlobalVars.WorkingDir, "Microsoft365SecurityBaseline.zip"), true);
}


// Extract MicrosoftSecurityBaseline.zip
System.IO.Compression.ZipFile.ExtractToDirectory(Path.Combine(HardeningModule.GlobalVars.WorkingDir, "MicrosoftSecurityBaseline.zip"), Path.Combine(HardeningModule.GlobalVars.WorkingDir, "MicrosoftSecurityBaseline"));

// Extract Microsoft365SecurityBaseline.zip
System.IO.Compression.ZipFile.ExtractToDirectory(Path.Combine(HardeningModule.GlobalVars.WorkingDir, "Microsoft365SecurityBaseline.zip"), Path.Combine(HardeningModule.GlobalVars.WorkingDir, "Microsoft365SecurityBaseline"));

// Extract LGPO.zip
System.IO.Compression.ZipFile.ExtractToDirectory(Path.Combine(HardeningModule.GlobalVars.WorkingDir, "LGPO.zip"), HardeningModule.GlobalVars.WorkingDir);


// capturing the Microsoft Security Baselines extracted path in a variable using GetSubDirectoryName method and storing it in a variable so that we won't need to change anything in the code other than the download link when they are updated
HardeningModule.GlobalVars.MicrosoftSecurityBaselinePath = GetSubDirectoryName(basePath: Path.Combine(HardeningModule.GlobalVars.WorkingDir, "MicrosoftSecurityBaseline"));

// capturing the Microsoft 365 Security Baselines extracted path in a variable using GetSubDirectoryName method and storing it in a variable so that we won't need to change anything in the code other than the download link when they are updated
HardeningModule.GlobalVars.Microsoft365SecurityBaselinePath = GetSubDirectoryName(basePath: Path.Combine(HardeningModule.GlobalVars.WorkingDir, "Microsoft365SecurityBaseline"));

// Storing the LGPO.exe path in a variable
HardeningModule.GlobalVars.LGPOExe = (Path.Combine(HardeningModule.GlobalVars.WorkingDir, "LGPO_30", "LGPO.exe"));

// Copying LGPO.exe from its folder to Microsoft Security Baseline folder in order to get it ready to be used by PowerShell script
System.IO.File.Copy(HardeningModule.GlobalVars.LGPOExe, Path.Combine(HardeningModule.GlobalVars.MicrosoftSecurityBaselinePath, "Scripts", "Tools", "LGPO.exe"), true);

// Copying LGPO.exe from its folder to Microsoft Office 365 Apps for Enterprise Security Baseline folder in order to get it ready to be used by PowerShell script
System.IO.File.Copy(HardeningModule.GlobalVars.LGPOExe, Path.Combine(HardeningModule.GlobalVars.Microsoft365SecurityBaselinePath, "Scripts", "Tools", "LGPO.exe"), true);

}
}
}
66 changes: 66 additions & 0 deletions Harden-Windows-Security Module/Main files/C#/CSVImporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.IO;

namespace HardeningModule
{
public class HardeningRegistryKeys
{
// Define a public class to store the structure of the new CSV data
public class CsvRecord
{
public string Category { get; set; } // Column for category
public string Path { get; set; } // Column for registry path
public string Key { get; set; } // Column for registry key
public string Value { get; set; } // Column for the expected value
public string Type { get; set; } // Column for the type of the registry value
public string Action { get; set; } // Column for the action to be taken
public string Comment { get; set; } // Column for comments
}

// Define a public method to parse the CSV file and save the records to RegistryCSVItems
public static void ReadCsv()
{
// Define the path to the CSV file
string path = Path.Combine(GlobalVars.path, "Resources", "Registry.csv");

// Open the file and read the contents
using (StreamReader reader = new StreamReader(path))
{
// Read the header line
var header = reader.ReadLine();

// Return if the header is null
if (header == null) return;

// Read the rest of the file line by line
while (!reader.EndOfStream)
{
var line = reader.ReadLine();

// Skip if the line is null
if (line == null) continue;

// Split the line by commas to get the values, that's the CSV's delimiter
var values = line.Split(',');

// Check if the number of values is 7
if (values.Length == 7)
{
// Add a new CsvRecord to the list
HardeningModule.GlobalVars.RegistryCSVItems.Add(new CsvRecord
{
Category = values[0],
Path = values[1],
Key = values[2],
Value = values[3],
Type = values[4],
Action = values[5],
Comment = values[6]
});
}
}
}
}
}
}
Loading

0 comments on commit 5960633

Please sign in to comment.