Cybersecurity Project: Windows Defender Firewall STIG Script

Having focused a lot on networking and AI implications for security lately, I've decided to pivot into something a little different, but still connected: Compliance.

The Defense Information Systems Agency (DISA) provides unclassified Security Technical Implementation Guides (STIGs) for hardening specific systems. For those who don't want to go on a government monitored system, DISA STIG Viewer is a good option to check these guides out.

I decided to make a script that would do two main things:

  1. Check the status of the system regarding the controls/settings.
  2.  
  3. Update the settings/implement the controls.

Table of Contents

Picking a STIG

Given the number of STIGs, one might be overwhelmed and get into decision paralysis.

However, I used a simple metric to cutdown the number of choices: Did I have the software/hardware in use on a system I used? This was because I wanted to implement the STIGs across all the systems I used and maintained.

This reduced the options to 4:

  1. Windows 10
  2. Windows 11
  3. Firefox
  4. Windows Defender Firewall

From there, I then picked the one with the smallest number of controls to implement: Windows Defender Firewall.

Making the Script

The next step was to make the script.

I chose to utilize AI, namely ChatGPT, for this, for a few reasons:

  1. I wanted to save time in creating the script.
  2. It allows for faster iteration.
  3. If the process worked, I could then apply it to other STIGs.

To do this, I examined the STIG's CSV file, where all the controls and their requirements are enumerated. What I discovered for the Windows Firewall STIG was not a well ordered spreadsheet. Instead, it seemed more like a standard page, laid out in numerous rows, in a single column format.

Cross checking with the CSVs for some of the other STIGs revealed this to be simply this specific STIG's format.

That led me to write a prompt that laid out the specifics of what I wanted done, instead of uploading the CSV and then referring to it in the prompt. That prompt is available here, on a GitHub repo that contains the prompts and the generated scripts.

The process took a few hours, mostly because I would get confused at times, due to how similar and repetitive many elements of the tasks were. I implemented all but one of the STIG controls, with the final control being omitted due to the lack of a remote management connection.

The First Iteration

After the hard work of crafting the prompt was done, the first iteration of the script was generated (code here). Since downloading the Windows image was taking a while (more on that later), this iteration was tested on a live Windows 10 system. (Said system had nothing of real value, and could've really used the excuse to be upgraded.)

The script ran its functions properly, but the CSV output generated revealed I had made an understandable oversight. There were no descriptions to explain what the actual controls were doing, just ID numbers.

Iterations 2 & 3

Curious to see how the script would be changed, I gave ChatGPT this follow up prompt. This generated the second iteration, which had the following code added to the existing script:

# Mapping of Findings to their Descriptions
$descriptions = @{
    "V-241989" = "Windows Defender Firewall must be enabled when connected to a domain."
    "V-241990" = "Windows Defender Firewall must be enabled when connected to a private network."
    "V-241991" = "Windows Defender Firewall must be enabled when connected to a public network."
    "V-241992" = "Windows Defender Firewall must block unsolicited inbound connections when connected to a domain."
    "V-241993" = "Windows Defender Firewall must allow outbound connections, unless explicitly blocked by rule."
    "V-241994" = "Windows Defender Firewall log size must be configured for domain connections."
    "V-241995" = "Windows Defender Firewall must log dropped packets when connected to a domain."
    "V-241996" = "Windows Defender Firewall must log successful connections when connected to a domain."
    "V-241997" = "Windows Defender Firewall must block unsolicited inbound connections when connected to a private network."
    "V-241998" = "Windows Defender Firewall must allow outbound connections on a private network, unless explicitly blocked by rule."
    "V-241999" = "Windows Defender Firewall log size must be configured for private network connections."
    "V-242000" = "Windows Defender Firewall must log dropped packets when connected to a private network."
    "V-242001" = "Windows Defender Firewall must log successful connections when connected to a private network."
    "V-242002" = "Windows Defender Firewall must block unsolicited inbound connections when connected to a public network."
    "V-242003" = "Windows Defender Firewall must allow outbound connections on a public network, unless explicitly blocked by rule."
    "V-242004" = "Windows Defender Firewall public network connections must not merge local firewall rules with Group policy settings."
    "V-242005" = "Windows Defender Firewall public network connections must not merge local connection rules with Group policy settings."
    "V-242006" = "Windows Defender Firewall log size must be configured for public connections."
    "V-242007" = "Windows Defender Firewall must log dropped packets when connected to a public network."
    "V-242008" = "Windows Defender Firewall must log successful connections when connected to a public network."
}

Based on my admittedly shoddy understanding of programming/coding best practices, this is basically creating an array that stores the variables associated with the actual descriptions.

This is presumably more efficient than just doing a brute force, manual addition of a field called Description to each row, and then putting in the string of text.

This didn't address any issues with the log identifying things by row numbers instead of the finding numbers, but that was easily dealt with in the third iteration. All it took was editing the code in VS Code.

Testing in Windows 11 Enterprise IOT LTSC

The choice of Windows 11 Enterprise IOT LTSC came about after I decided to do some verification on the Windows 10 test system after generating the second iteration.

It turned out that even settings like the firewall log size are so locked down on Windows Home editions that the commands have no actual effect. Inputting the specific commands into an administrator command prompt yields an "Access is denied." response.

(The reader is free to speculate as to the reasoning for this.)

Actually downloading the image took an annoying amount of time, as the website hosting the non-trial images does not work well with browser download managers. A separate download manager is required, and if you don't use Edge, it can be hard to get the download link for the ISO.

Once the virtual machine was ready, I made a snapshot of the initial configuration, and checked the defaults:

Default Windows Defender Firewall settings on Windows 11 Enterprise IOT LTSC: Firewall on in all domains, inbound connections blocked by default, outbound connections allowed by default.

To verify that Enterprise IOT LTSC was not as locked down as the Home version, I checked if I could change the logging settings:

Verifying Windows 11 Enterprise IOT LTSC firewall setting freedom by changing Log Dropped Packages to "Yes" from "No (Default)".

With that confirmed, I ran the third iteration of the script:

For whatever reason, attempting to change the rules merging settings failed. So I decided to look at the registry:

Looking for the HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\WindowsFirewall\PublicProfile\ entry in the Windows 11 Enterprise IOT LTSC registry.

V-242004 of the Windows Firewall STIG says that the following should be present:

Registry Hive: HKEY_LOCAL_MACHINE
Registry Path: \SOFTWARE\Policies\Microsoft\WindowsFirewall\PublicProfile\

It was not, and it's not entirely clear why. Since the Windows Defender Firewall STIG was last updated in August 2023, it's entirely possible that entire setting has been depreciated by Microsoft, or made useless without an actual Group Policy.

The CurrentControlSet registry entries for the various profiles did exist, which was why those settings could be correctly updated.

Iteration 4 & 5

To add an extra level of confidence to the results of the script, I decided to add a validation pass with the following prompt:

Update script to do the following:
-Log if command has been granted permissions to execute
-Validate that the settings have been properly updated
-If settings have not been properly updated, set corrected value to False, and log correction failure

I then ran this iteration of the script on a modified snapshot of initial install, with the full Guest Additions package installed. This was the result:

Log output of WinFirewall-STIG-tester-v4 with false negative readings for the connection logging findings.

Oddly enough, all the validation checks for the connection logging settings failed. So I examined the actual settings through the GUI:

Since the system reported that the settings had been configured correctly, that pointed to an error in the script.

Opening up the Registry Editor and searching for the name LogDroppedPackets revealed that I had made the following error in the original prompt:

Incorrect: "HKLM:\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile",
Incorrect: "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\DomainProfile"

Correct: "HKLM:\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile\Logging",
Correct: "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\DomainProfile\Logging"

As a result, the script was checking the wrong area in the registry, finding no registry entry for LogDroppedPackets or LogSuccessfulConnections, and declaring that the correction had failed. In essence, the quintessential false negative.

This was easily corrected in the fifth iteration by amending the paths.

Takeaways

  • DISA STIG Viewer is a great resource for anyone looking to understand DOD compliance or looking for a guide on how to harden their systems.
  • STIG Viewer's CSVs for a particular STIG may not be correctly formatted.
  • For smaller STIGs, handcrafting prompts instead of uploading a CSV for AI code generation is possible, but runs the risk of errors in the code.
  • Including a validation test is a good way to spot errors in output.
  • Using reasoning LLMs to iteratively update code is quite effective if the user knows how to articulate what their objective is.
  • Windows 11 Home gives the user far less ability to fine tune security controls with its implementation of Windows Defender Firewall.