Post

St3wart

A look at my Windows desktop and server vulnerability management tool, St3wart

St3wart

The source and compiled binary for this project can be found on my GitHub.

The Problem

Let’s say you are a newly hired IT administrator at a school. You’re responsible for managing hundreds, if not thousands, of devices. From student laptops to the desktop PCs in computer labs, it can quickly become overwhelming. To lighten the load, organizations often turn to centralized management systems such as Active Directory Domain Services (AD DS) for Windows or Jamf for Apple devices. These solutions are powerful, but they can be complex, platform-specific, and still leave gaps that require additional tools or expertise. In many environments, administrators struggle to achieve the balance between comprehensive management and practical day-to-day security.

When resources are stretched thin, this can lead to sloppy management. Too often — especially in smaller organizations or non-regulated sectors — cybersecurity is deprioritized until an incident forces attention. Budgets, competing priorities, and lack of staff expertise all contribute. This dynamic was one reason I chose not to pursue a career in cybersecurity.

I believe that if centralized cybersecurity management were simpler and more comprehensive, it would be easier for organizations to make it a priority. Unfortunately, the current mix of tools doesn’t always suffice.

Take the example of Active Directory. Policies are primarily enforced through Group Policy Objects (GPOs), which are extremely powerful for configuring standard Windows settings. However, GPOs don’t automatically cover every possible operating system or third-party application setting. To extend coverage, administrators often need to add vendor-supplied ADMX templates, craft custom Registry policies, or adopt additional tools like Intune, SCCM/ConfigMgr, or PowerShell Desired State Configuration. While administrators can remotely query and report on applied policies using tools like Group Policy Management Console (GPMC) or gpresult, it’s not always straightforward to manage every setting an organization cares about.

ADDS Diagram Diagram demonstrating Active Directory Domain Services policy management from here

Consider the case of application deployment. Enterprise tools exist to automate software rollout and configuration, but smaller IT teams — like those in many school districts — often lack the time, expertise, or vendor-supplied templates to reliably secure every new application. As a result, apps may be pushed out quickly to meet business demands, but their security configurations lag behind.

The conclusion is clear: we need something better. A way to easily manage policies and security configurations at scale, across diverse environments, without imposing overwhelming complexity. Enter St3wart.


Background

For most of my time in high school, I aspired to be a penetration tester. I thought that (and still think) the idea of being hired and paid to exploit a company’s network was extremely cool. One of the ways I pursued my study of cybersecurity was by competing in various cybersecurity competitions. During my time competing in these competitions, I became extremely well versed in Windows security. Several times a year I was tasked with securing Windows machines, and as such, I was constantly seeking out the best ways to manage the policies and security of Windows systems. While I found good solutions, I decided to create my own suite of Windows tools and scripts designed to secure machines quickly and effectively in accordance with these competition scenarios.

After becoming known online as “St3wart”, I decided to call my scripts and tools St3wart. St3wart’s capabilities ranged from custom complex malware scanners written in C that used preconfigured baselines to identify malware in NTFS to PowerShell scripts designed to properly configure the Windows Firewall and Defender policies on the system in accordance with CIS and United States DOD guidelines.

St3wart Scripts Original St3wart framework (private repo)

Following my high school graduation, I couldn’t help but wonder if St3wart could be generalized. I had spent so long creating this set of scripts specifically for competition, but what if I could create a way to generally secure Windows machines? This train of thought is what inspired this project.


St3wart

St3wart is a lightweight, configurable, general purpose Windows security auditing tool.

The concept is simple. Administrators define checks to perform on systems using JSON. Then, they can test if the checks pass on systems, employ security measures to patch them, generate vulnerability reports based on findings, schedule periodic checks and security measures, and more. All through a portable, easy to use CLI tool.

See Future Development for details on what the end vision for St3wart looks like.


Demonstration

Let’s take a look at how St3wart could be utilized to secure some basic vulnerabilities on a Windows machine.

When defining checks with St3wart, each check must have a “type”. This type dictates the mechanism in which St3wart will use to check for a finding. The three options are:

1
2
3
PowerShell
Registry
File

For this demonstration, we’ll use one of each type of check.

PowerShell Check - Log Size of Firewall

In the Windows Defender Firewall, users have the option to specify the maximum size of the log file for each profile. CIS recommends that the domain firewall profile log size be set to at least 16,384 KB (see CIS benchmarks and controls 8.3).

wf.msc Microsoft Management Console snap-in wf.msc

We can retrieve this particular policy with the following PowerShell command:

1
Get-NetFirewallProfile -Profile Domain | Select-Object -ExpandProperty LogMaxSizeKilobytes

With that, we can craft the JSON object for this check.

1
2
3
4
5
6
7
8
9
10
{
	"ID": "TEST-001",
	"Description": "Windows Firewall Domain Profile Log Size Configured",

	"CheckType": "PowerShell",
	"CheckCommand": "Get-NetFireWallProfile -Profile Domain | Select-Object -ExpandProperty LogMaxSizeKilobytes",
	"FindData": "16384",
	"Operator": "LessThan",
	"SecureCommand": "Set-NetFirewallProfile -Profile Domain -LogMaxSizeKilobytes 16384"
}

Let’s break this down line by line:

  • ID: A unique string used to easily reference this check.
  • Description: A short description used to label the check.
  • CheckType: The type of the check, one of the three options given above.
  • CheckCommand: A PowerShell check specific component representing the PowerShell command used to check for the presence of the vulnerability.
  • FindData & Operator: The operator compares the output of the command with this FindData to check for a finding. In this scenario, if the output of the command is less than the FindData, 16,384, the check fails and a finding has occurred.
  • SecureCommand: The PowerShell command used to remediate the vulnerability.

Essentially, the JSON object defines a command to run in PowerShell to check for the presence of a vulnerability. It then also defines what to compare the output of that command with (FindData) and how to compare it (Operator). In this check specifically, we can see that we are retrieving the domain profile log size limit using the command given earlier. We then compare its output with the recommended amount, 16,384, and if the output, which represents our system log size, is less than this number, it is marked as a finding. We then define a similar command to correct this finding.

And that’s it! The check is ready to go and St3wart can now check for the vulnerability, automatically secure it, etc.

Registry Check - Startup Type of Remote Registry Service

In addition to being able to run checks using PowerShell, St3wart can also query the Registry. The process for defining a Registry check is similar to the above PowerShell check.

For this vulnerability, we’ll configure the startup type of a service. The Windows Service Manager references a particular Registry key that stores information such as the names, descriptions, paths to executables, and startup types of each service. We’ll use this key to check for the startup type of the service “Remote Registry”, which allows for remote calls to the Registry. Due to this service increasing the attack surface of the machine, it is recommended to set the startup type of this service to disabled, meaning the service can not be loaded (see CIS benchmarks and controls 4.8).

Remote Registry Remote Registry service in services.msc

Using this information, we can define the check.

1
2
3
4
5
6
7
8
9
10
11
{
	"ID": "TEST-002",
	"Description": "Windows Remote Registry Service Disabled",

	"CheckType": "Registry",
	"Key": "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\RemoteRegistry",
	"Value": "Start",
	"SecureValue": 4,
	"FindData": "4",
	"Operator": "NotEqualTo"
}

The above check works similar to the PowerShell command, but with 2 key differences:

  • We have to define a Key and Value here, indicating the key and value to query within the Registry.
  • Instead of defining a command to run to secure the vulnerability, we define a SecureValue. This indicates what to set the indicated value in the Registry to in order to remediate the finding.

In this check, we query the startup type within the Remote Registry service key. According to Microsoft Docs, a startup type of 4 indicates the disabled setting. As such, any value not 4 indicates that the service is not disabled, leading to a finding.

File Check - SSH Allows Empty Passwords

Open SSH can be configured on Windows machines (see Windows Optional Features).

In order to properly secure the Open SSH Server on your machine, there are a few basic best practices to follow.

One of the more important ones is disallowing empty passwords. This is configured in the sshd_config file.

As such, instead of using PowerShell or the Registry to check for this vulnerability, we can perform the check by querying the config file.

1
2
3
4
5
6
7
8
9
10
{
	"ID": "TEST-003",
	"Description": "SSH Server Config File Disallows Empty Passwords",

	"CheckType": "File",
	"Path": "C:/ProgramData/ssh/sshd_config",
	"SecureText": "PermitEmptyPasswords no",
	"FindData": "PermitEmptyPasswords yes",
	"Operator": "Contains"
}

This defines a check of type “File”. This is rather similar to how the Registry checks operate. In this check, St3wart checks if the specified config file contains the FindData (the insecure configuration) and if so, replaces it with the “SecureText” (the secure configuration). If the setting is not set in the file or if the setting is commented, the server defaults to no.

Ready to Go!

These three checks yield the following JSON:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[
	{
		"ID": "TEST-001",
		"Description": "Windows Firewall Domain Profile Log Size Configured",

		"CheckType": "PowerShell",
		"CheckCommand": "Get-NetFireWallProfile -Profile Domain | Select-Object -ExpandProperty LogMaxSizeKilobytes",
		"FindData": "16384",
		"Operator": "LessThan",
		"SecureCommand": "Set-NetFirewallProfile -Profile Domain -LogMaxSizeKilobytes 16384"
	},

	{
		"ID": "TEST-002",
		"Description": "Windows Remote Registry Service Disabled",

		"CheckType": "Registry",
		"Key": "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\RemoteRegistry",
		"Value": "Start",
		"SecureValue": 4,
		"FindData": "4",
		"Operator": "NotEqualTo"
	},

	{
		"ID": "TEST-003",
		"Description": "SSH Server Config File Disallows Empty Passwords",

		"CheckType": "File",
		"Path": "C:/ProgramData/ssh/sshd_config",
		"SecureText": "PermitEmptyPasswords no",
		"FindData": "PermitEmptyPasswords yes",
		"Operator": "Contains"
	}
]

JSON Parameters

Here are some things to know for crafting these JSON files:

  • There are (currently) only 3 types of checks: PowerShell, Registry, File.
  • The ID and Descriptions attributes can be anything but the ID should be easy to look up and identify. Use the description attribute to provide greater detail on the check.
  • The following are valid Operator attributes for PowerShell checks: GreaterThan, LessThan, EqualTo, Contains, NotEqualTo, NotContains.
  • The following are valid Operator attributes for Registry and File checks: GreaterThan, LessThan, EqualTo, Contains, NotEqualTo, NotContains, Exists, NotExists.
  • For NotExists Registry and File checks, the engine will remediate the check by attempting to create the Registry value or file and then populating it with the “SecureValue” or “SecureText” attributes.
  • For Exists Registry and File checks, the engine will remediate the check by attempting to delete the Registry value or file. The “SecureValue” / “SecureText” attributes are still required in order for the JSON to be properly parsed, but their actual value has no effect on the check’s behavior.
  • For GreaterThan, LessThan, and Contains File checks, the engine will remediate findings by removing the text within the file where the “FindData” occurs, and replace all instances with the “SecureText”.
  • For NotContains File checks, the engine will simply append the “SecureText” to the file.

Documentation

For documenting each command, I will be using the checks crafted in the section above. I’ve saved the checks file as “test.json” in the root directory of my drive, along with the St3wart binary (which is available here).

Check

The check command is used to check for the presence of designated vulns on the machine.

The command utilizes the user crafted JSON to check for each vuln, and then outputs and logs the findings with an action GUID.

Syntax:

1
St3wart.exe check [OPTIONS] <JSON BANK PATH>

Examples:

1
St3wart.exe check C:/test.json

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\> ./St3wart.exe check C:/test.json
ID: TEST-001
Description: Windows Firewall Domain Profile Log Size Configured
Check Pass: False
----------------------------------------------
ID: TEST-002
Description: Windows Remote Registry Service Disabled
Check Pass: True
----------------------------------------------
ID: TEST-003
Description: SSH Server Config File Disallows Empty Passwords
Check Pass: True
----------------------------------------------
Completed check ID e12072d2ae5b458eb04df7d2806d619a

Exempt

The exempt command is used to exempt a command from being checked by St3wart.

When dealing with hundreds of vulnerabilities and checking for all of them across multiple machines, there may be slight variance between machines in which policies need to be enforced. To account for this, the engine can be configured to “exempt” certain checks. The same JSON file can be used, but St3wart will ignore those specific vulnerabilities.

Syntax:

1
St3wart.exe exempt [OPTIONS] <ADD/REMOVE> <VULN ID>

Examples:

1
2
St3wart.exe exempt add TEST-001
St3wart.exe exempt remove TEST-001

Help

The help command displays the St3wart help menu.

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\> ./St3wart.exe help
St3wart - Simplifying Systems Management
A general purpose Windows security auditing tool

Usage: St3wart.exe <COMMAND>

Commands:
 check
 exempt
 help
 report
 schedule
 secure
 vuln

Report

The report command generates a pdf report of actions taken by St3wart.

The user can specify the GUID of an action taken by the engine and the engine will generate a pdf report of the action.

Syntax:

1
St3wart.exe report [OPTIONS] <ACTION TYPE> <ACTION GUID>

Examples:

1
St3wart.exe report secure abcdefghijklmnopqrstuvwxyz012345

Schedule

The schedule command allows for the periodic running of St3wart.

Users can schedule St3wart to take actions periodically by specifying the command and how often to run it.

Syntax:

1
St3wart.exe schedule [OPTIONS] <COMMAND> <PERIOD TIME IN DAYS>

Examples:

1
St3wart.exe schedule \"check C:/test.json\" 1

Secure

The secure command automates the correction of any findings.

Users must first run a check to gather which vulnerabilities require remediation. Then, users can specify the JSON file with the checks and the GUID of the corresponding check to automatically run remediation attempts on the machine. The results are then outputted and logged under the same GUID as the check.

Syntax:

1
St3wart.exe secure [OPTIONS] <JSON BANK PATH> <CHECK GUID>

Examples:

1
St3wart.exe secure C:/test.json abcdefghijklmnopqrstuvwxyz012345

Output:

1
2
3
4
5
6
7
8
9
10
11
PS C:\> ./St3wart.exe secure C:/test.json ac79654a6d814d9186a3be0ffe262e21
Vuln: TEST-001
Check Type: PowerShell
Description: Windows Firewall Domain Profile Log Size Configured
Check Command: Get-NetFireWallProfile -Profile Domain | Select-Object -ExpandProperty LogMaxSizeKilobytes
Secure Command: Set-NetFirewallProfile -Profile Domain -LogMaxSizeKilobytes 16384
Find Data: 16384
Operator: LessThan
Remediation Success: True
----------------------------------------------
Secured check ID ac79654a6d814d9186a3be0ffe262e21

Vuln

The vuln command fetches information on a vulnerability from a JSON database.

As the size of a JSON St3wart check file increases, it can become difficult to navigate the file if an IT Administrator needs to examine the details of a singular check. This command allows for administrators to view details on a vuln in a JSON file by specifying the ID of the check.

Syntax:

1
St3wart.exe vuln [OPTIONS] <JSON BANK PATH> <VULN ID>

Examples:

1
St3wart.exe vuln C:/vulns.json TEST-001

Output:

1
2
3
4
5
6
7
8
PS C:\> ./St3wart.exe vuln C:/test.json TEST-001
Vuln: TEST-001
Check Type: PowerShell
Description: Windows Firewall Domain Profile Log Size Configured
Check Command: Get-NetFireWallProfile -Profile Domain | Select-Object -ExpandProperty LogMaxSizeKilobytes
Secure Command: Set-NetFirewallProfile -Profile Domain -LogMaxSizeKilobytes 16384
Find Data: 16384
Operator: LessThan

Future Development

The deployed version on GitHub and the version corresponding to the documentation on this page is version 0.1. Subsequent versions will include any identified bug fixes, better report generation, configurable logs, and more.

Long-term changes include remote scanning, the ability to manage multiple systems at once, and support beyond only Windows machines. To stay tuned, follow along here and on my other socials!

Conclusion

Overall, I am extremely happy I pursued this project. To me, it is most likely the end of my cybersecurity journey, but was a way for me to put to use the large amount of work that I invested in the original St3wart framework. Thanks for reading!

This post is licensed under CC BY 4.0 by the author.