Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create HVCI table for Windows Device Guard #5426

Merged
merged 17 commits into from
Jan 24, 2020

Conversation

finnstra
Copy link
Contributor

@finnstra finnstra commented Feb 8, 2019

I created a new table to track HVCI settings on a machine, utilizing the WMI class Win32_DeviceGuard. This is useful in determining if a host is configured to use Device Guard or running, and displays various code integrity settings enforced on the machine. In addition, this table returns the machine's unique Instance Identifier.

Currently, this table does not return the available or required security properties of a machine.

This is my first pull request for osquery (and GitHub!) so please let me know if there is a better way to implement this.

Context:

@facebook-github-bot facebook-github-bot added the cla signed Automated label: Pull Request author has signed the osquery CLA label Feb 8, 2019
Copy link
Contributor

@muffins muffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An initial pass. Some of our styling may have changed since I've been reviewing actively, so we'll wanna make sure we get someone else from the core team to also stamp this before we get too far along :)

Column("version", TEXT, "The version number of the Device Guard build."),
Column("instance_identifier", TEXT, "The instance ID of Device Guard."),
Column("vbs_status", TEXT, "The status of the virtualization based security settings."),
Column("code_integirty_policy_enforcement_status", TEXT, "The status of the code integirty policy enforcement settings."),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/integirty/integrity/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

(BSTR)L"ROOT\\MICROSOFT\\WINDOWS\\DEVICEGUARD");
const std::vector<WmiResultItem>& wmiResults = wmiSystemReq.results();
if (wmiResults.empty()) {
LOG(WARNING) << "Error retreiving information from WMI.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for data loss we prefer LOG(ERROR)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

umcipolicymethod);

std::string vbsmethod_str;
std::map<long, std::string> vbsmethods;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to be a std::map? Perhaps this could just be a statically defined array or vector of std::string, so:

std::vector<std::string> vbs_methods {
    "VBS_NOT_ENABLED",
    "VBS_ENABLED_AND_NOT_RUNNING",
    "VBS_ENABLED_AND_RUNNING",
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also move this up outside of the for loop, so that we're not redefining it every pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was mapping this because the WMI output returned a number, that correlated to a particular setting status (0 = this, 1 = that), but they seem to be in order from 0 to n, so this seems far simpler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

osquery/tables/system/windows/hvci_status.cpp Outdated Show resolved Hide resolved
std::map<long, std::string> vbsmethods;

std::string codepolicystatusmethod_str;
std::map<long, std::string> codepolicystatusmethods;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same note as vbs_methods for both this and the umcipolicymethods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

return results;
}
for (const auto& data : wmiResults) {
long vbsmethod;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and throughout, feel free to use snake_case for variable names to break things up a bit, so vbs_method, code_policy_status_method, umci_policy_method. Also, considering method is repeated maybe just omit it for brevity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

}
for (const auto& data : wmiResults) {
long vbsmethod;
long codepolicystatusmethod;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and throughout, the variable declaration is pretty far away from it's use. It can be a little clearer if the variable is right next to it's use point, so for something like this you could do:

long code_policy_status;
data.GetLong("CodeIntegrityPolicyEnforcementStatus", code_policy_status);
// do something with code_policy_status

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

umcipolicymethods[1] = "AUDIT_MODE";
umcipolicymethods[2] = "ENFORCED_MODE";

if (vbsmethods.find(vbsmethod) != vbsmethods.end()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's save this value so that you don't grab it twice. In fact you could just make this a ternary:

auto vbs_meth = vbsmethods.find(vbsmethod);
r["vbs_status"] = vbs_meth != vbs_methods.end() ? vbs_meth->second : "UNKNOWN";

Here and throughout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

r["code_integirty_policy_enforcement_status"] = codepolicystatusmethod_str;
r["umci_policy_status"] = umcipolicymethod_str;

// stuff goes before here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Fix typo with one of the umcipolicy, remove VectorOfStrings call.

Fix"integrity" spelling issues.

Remove map calls to use arrays.

Switch to vectors instead of array methods.

Add conditional statements to use "unknown" for out-of-range values.

Remove extraneous comment.
Fix typo with one of the umcipolicy, remove VectorOfStrings call.

Fix"integrity" spelling issues.

Remove map calls to use arrays.

Switch to vectors instead of array methods.

Add conditional statements to use "unknown" for out-of-range values.

Remove extraneous comment.
fmanco
fmanco previously requested changes Mar 19, 2019
Copy link
Contributor

@fmanco fmanco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to target experimental which will change a but the generation function. Whenever problem provide the valid values on the column description mentioning that UNKNOWN is returned on error. Finally please add tests similar to the other tests we have on the tests/integration directory.

namespace osquery {
namespace tables {

std::vector<std::string> vbs_methods = {"VBS_NOT_ENABLED",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to split this into enabled and running and make them booleans?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this case we need to track the three distinctive states. It may be possible for VBS to be running, but the configurations may be incorrect, and thus not run. It's also part of the expected status from the Microsoft docs: https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-exploit-guard/enable-virtualization-based-protection-of-code-integrity#virtualizationbasedsecuritystatus

"OFF", "AUDIT_MODE", "ENFORCED_MODE"};

QueryData genHVCIStatus(QueryContext& context) {
Row r;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move inside the loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're suggesting that I move 13-20 to line 30, I received previous feedback move most of my code outside the loop, since I would be recreating variables in each loop iteration.

#5426 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think I'm gunna say my bad on this one and let's go with @fmanco's suggestion here :) Thinking through this, my concern would be if logic ever changes and we're not updating the values for every column in the row we might get stale data being set. That's my bad, sorry about that!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, I just moved them back into the loop.

@finnstra finnstra changed the base branch from master to experimental March 22, 2019 00:33
@finnstra
Copy link
Contributor Author

I've correct my PR to be compatible with Buck, and also included an integration test for the new table.

@finnstra
Copy link
Contributor Author

finnstra commented Jul 2, 2019

Friendly bump on this PR :) Thanks!

@finnstra finnstra closed this Nov 21, 2019
@finnstra finnstra reopened this Nov 21, 2019
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Nov 21, 2019

CLA Check
The committers are authorized under a signed CLA.

@finnstra
Copy link
Contributor Author

Hello, could someone please take a look at my PR? Thank you :)

@Smjert Smjert changed the base branch from experimental to master January 23, 2020 18:58
Copy link
Contributor

@muffins muffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lgtm, @nullbrx would you mind fixing the one comment to move the Row r back inside the loop? And we should be good to merge assuming CI goes through ok. Thanks for your patience with us!

"OFF", "AUDIT_MODE", "ENFORCED_MODE"};

QueryData genHVCIStatus(QueryContext& context) {
Row r;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think I'm gunna say my bad on this one and let's go with @fmanco's suggestion here :) Thinking through this, my concern would be if logic ever changes and we're not updating the values for every column in the row we might get stale data being set. That's my bad, sorry about that!

Copy link
Contributor

@muffins muffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for the CI build failures and also for the not about moving Row r back into the loop :)

specs/BUCK Outdated
@@ -745,7 +745,11 @@ osquery_gentable_cxx_library(
"windows",
),
(
"windows/pipes.table",
_SPECS_LOCATION + "/windows/hvci_status.table",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the _SPECS_LOCATION bit here, make it look like the ones above, I think CI is failing over this.

specs/BUCK Outdated
"windows",
),
(
_SPECS_LOCATION + "/windows/pipes.table",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm betting you might need to remove this _SPECS_LOCATION as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed!

@finnstra
Copy link
Contributor Author

@muffins I'm affecting the Linux build somehow - let me know if I need to sync some things up :)

Addressing `clang-format` nits, moving the `Row r` into the main loop.
@muffins
Copy link
Contributor

muffins commented Jan 23, 2020

@nullbrx I made the changes, looks like it was a clang-format issue, we'll see if they went through alright? If the build passes we should be good to merge :)

@muffins muffins merged commit 0b2aa61 into osquery:master Jan 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla signed Automated label: Pull Request author has signed the osquery CLA virtual tables Windows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants