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

windows/certificates: Fix enumeration bugs, add columns #5631

Merged

Conversation

offlinemark
Copy link
Contributor

@offlinemark offlinemark commented Jul 8, 2019

This PR addresses a few bugs in the certificates table on Windows (which was somewhat broken), and also implements some additional functionality (new columns). This is the first of two PRs, the second of which improves the table's completeness with respect to the Personal certificate store.

Bugs Fixed

  1. The store enumeration API (certEnumSystemStoreCallback) returned False during error handling, but this prematurely ends the enumeration of cert store locations and cert stores. The callback has been adjusted to always return true, which ensures complete enumeration. (https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nc-wincrypt-pfn_cert_enum_system_store#return-value)
  2. The CertOpenSystemStore API (https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-certopensystemstorea) was used for opening system stores, however this is not the correct API, since it only opens stores in the Current User store location. The CertOpenStore API is now used, which is the correct functionality for opening arbitrary stores in arbitrary locations.
  3. The table previously included deduplication functionality which actually resulted in certificates being hidden. If a certificate was found in multiple users' stores, it would only appear as one entry due to this deduplication. Deduplication has been removed.

Improvements Made

  • Fixing bug 1. reveals bugs in the handling of the const void* systemStore parameter that certEnumSystemStoreCallback receives. Specifically, that systemStore parameter could also contain information about the service or user store location that the store was in (previously, the code expected something like My, and now that parameter could contain something like S-1-5-18\My). Handling this required adding more parsing logic (parseSystemStoreString) and refactoring to use the additional provided information.
  • Various new columns have been added to the table to make using it more ergonomic for Windows. Previously, a variety of data was concatenated and put in the path column. That functionality has been preserved, but that information (username, SID, store location, store) has been broken out into individual columns so it can be queried for.

Testing Notes

  • Verbose debug logging has been added to show the certificate store enumeration process
  • select common_name, path, sid, username, store_location from certificates; is a good starting query
  • Unit tests have been added for one small string parsing routine, however, much of the table is untested. I took a look at restructuring the code to create a layer between the table and the system APIs and it was very difficult due to the way the system APIs use (nested) callback functions extensively.

Questions

  • The table currently does an internal SQL query to map a service's name to its SID. It caches these lookups, however the cache assumes that services will not restart under a different user while osquery is running. Is this too dangerous of an assumption?
  • Some files contain comments at the top with a Facebook copyright notice, since they were copied from other files. Should that be removed, and what should go in place of them, if anything?

@@ -34,6 +34,10 @@ function(osqueryTablesSystemTestsMain)
generateOsqueryTablesSystemTestsStartupitemstestsTest()
generateOsqueryTablesSystemTestsSignaturetestsTest()
endif()

if(DEFINED PLATFORM_WINDOWS)
generateOsqueryTablesSystemTestsCertificatestestsTest()
Copy link
Member

Choose a reason for hiding this comment

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

This name collides with an already existing function in the macOS case.
You might want to rename both and add the platform, like generateOsqueryTablesSystemTestsWindowsCertificatestestsTest and generateOsqueryTablesSystemTestsmacOSCertificatestestsTest

@Smjert
Copy link
Member

Smjert commented Jul 8, 2019

Formatting the code is required (as shown by the Linux CI job), if you add the clang-format root folder in the PATH, something like C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\VC\vcpackages depending on what Visual Studio version have you installed, then you can run it.
Then you have to format each commit, as separate changes (unless you want to do the format at the end), but you need to have the files in the git stage area, or in the working tree, but not committed, by building the format target with cmake --build . --config <config> --target format.

return Status(-1, "Invalid SID");
}

Status getCurrentUserInfo(PTOKEN_USER& info) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The getCurrentUserInfo() and isUserCurrentUser() are only used by tables. I wish there were separate libraries for table support routines like this, as it makes the core harder and harder to separate and whittle down. It appears that both of these routines are not used outside the certificates table, so perhaps it can be contained in there for now?

Copy link
Member

Choose a reason for hiding this comment

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

I somewhat disagree.

I think there's value in having some core table helper functions about. These sound like great candidates to me. (Certainly we have equivalents in our go tables)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@packetzero I see what you're saying- In this particular case, I feel like although the certificates table is the only caller, these functions are general enough that it makes sense to keep them in a general library of routines available to windows tables, including future ones, so I tend to agree with @directionless here.

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, not a huge deal. I'm all for common routines, we need to start thinking about the monolith that is osquery and how to separate core from tables at some point. When builds have stabilized, we can think about moving table-specific helpers into libs separate from core.

Fixes a bug where certificates for services that correspond to Local Service or
Network Service may not have their sids appear correctly. This is because the
services table is inconsistent with its user_account column.
@offlinemark offlinemark force-pushed the mark/feature/win-certs-1-initial branch from 5bd26a6 to fca2e16 Compare July 9, 2019 22:16
packetzero
packetzero previously approved these changes Jul 10, 2019
Previously, the service name cache existed for the lifetime of the
osquery process, which made it susceptible to stale reads if a service
restarted under a different user during osquery's lifetime. Now the
cache is created for each query. Also refactor it to directly map to the
sid, rather than the account name, which removes the need to translate
from account name to sid every row.
Previously, getCurrentUserInfo took a reference to data from a local
vector, whose data is free'd after the function. This refactors the code
to use a unique_ptr (similar to how getSidFromUsername) does it.
@alessandrogario
Copy link
Member

Thanks for the review @packetzero! 😄

@alessandrogario alessandrogario merged commit a60b940 into osquery:master Jul 16, 2019
@offlinemark offlinemark deleted the mark/feature/win-certs-1-initial branch July 23, 2019 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants