New CIS Audit table (#11381)

This relates to #11244 

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [x] Manual QA for all new/changed functionality
- [x] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.

---------

Co-authored-by: Sharon Katz <121527325+sharon-fdm@users.noreply.github.com>
This commit is contained in:
Marcos Oviedo
2023-05-12 11:16:36 -03:00
committed by GitHub
co-authored by Sharon Katz
parent 2523ae39aa
commit 3ec04887e6
6 changed files with 855 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
* New table was added to support CIS audit process
@@ -0,0 +1,720 @@
//go:build windows
// +build windows
package cisaudit
import (
"bytes"
"context"
"encoding/csv"
"fmt"
"io/ioutil"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"sync"
"github.com/osquery/osquery-go/plugin/table"
"github.com/rs/zerolog/log"
"golang.org/x/sys/windows"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
"gopkg.in/ini.v1"
)
var (
// CIS items commands initialization
commandsInit sync.Once
// Map to store command handlers
commandHandlers map[string]CommandHandler
)
// CommandHandler is a function type that returns the value for a CIS item
type CommandHandler func() (string, error)
// Audit items stores data from auditpol utility
type AuditItem struct {
Subcategory string
GUID string
NoAuditing bool
Success bool
Failure bool
Raw string
}
// SeceditData stores data from secedit utility
type SeceditData struct {
Unicode struct {
Unicode bool
}
SystemAccess struct {
MinimumPasswordAge string
MaximumPasswordAge string
MinimumPasswordLength string
PasswordComplexity string
PasswordHistorySize string
LockoutBadCount string
ResetLockoutCount string
LockoutDuration string
RequireLogonToChangePassword string
ForceLogoffWhenHourExpire string
NewAdministratorName string
NewGuestName string
ClearTextPassword string
LSAAnonymousNameLookup string
EnableAdminAccount string
EnableGuestAccount string
}
EventAudit struct {
AuditSystemEvents string
AuditLogonEvents string
AuditObjectAccess string
AuditPrivilegeUse string
AuditPolicyChange string
AuditAccountManage string
AuditProcessTracking string
AuditDSAccess string
AuditAccountLogon string
}
PrivilegeRights struct {
SeNetworkLogonRight string
SeBackupPrivilege string
SeChangeNotifyPrivilege string
SeSystemtimePrivilege string
SeCreatePagefilePrivilege string
SeDebugPrivilege string
SeRemoteShutdownPrivilege string
SeAuditPrivilege string
SeIncreaseQuotaPrivilege string
SeIncreaseBasePriorityPrivilege string
SeLoadDriverPrivilege string
SeDenyBatchLogonRight string
SeDenyServiceLogonRight string
SeBatchLogonRight string
SeServiceLogonRight string
SeInteractiveLogonRight string
SeSecurityPrivilege string
SeSystemEnvironmentPrivilege string
SeProfileSingleProcessPrivilege string
SeSystemProfilePrivilege string
SeAssignPrimaryTokenPrivilege string
SeRestorePrivilege string
SeShutdownPrivilege string
SeTakeOwnershipPrivilege string
SeDenyNetworkLogonRight string
SeDenyInteractiveLogonRight string
SeUndockPrivilege string
SeManageVolumePrivilege string
SeRemoteInteractiveLogonRight string
SeImpersonatePrivilege string
SeCreateGlobalPrivilege string
SeIncreaseWorkingSetPrivilege string
SeTimeZonePrivilege string
SeCreateSymbolicLinkPrivilege string
SeDelegateSessionUserImpersonatePrivilege string
}
}
// Columns is the schema of the table
func Columns() []table.ColumnDefinition {
return []table.ColumnDefinition{
table.TextColumn("item"),
table.TextColumn("value"),
}
}
// Generate is called to return the results for the table at query time.
// Constraints for generating can be retrieved from the queryContext.
func Generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
// input item query constraint
var inputItem string
// item output value
var inputValue string
// error handling
var err error
// one-time commands handlers initialization
registerCommandsHandlers()
// checking if 'item' is in the where clause
if constraintList, present := queryContext.Constraints["item"]; present {
for _, constraint := range constraintList.Constraints {
if constraint.Operator == table.OperatorEquals {
inputItem = constraint.Expression // this input as to be kept as-is and returned on the same input column due to a sqlite requirement
log.Debug().Msgf("cis_audit input item requested: %s\n", inputItem)
}
}
}
// Getting the input value if supported
if len(inputItem) > 0 {
inputValue, err = getValueCisItem(inputItem)
if err != nil {
return nil, err
}
}
// returning item and its value
return []map[string]string{
{
"item": inputItem,
"value": inputValue,
},
}, nil
}
// getPreProcessedFileContent returns an UTF-16 byte array
// This is useful when reading data from MS-Windows systems that generate UTF-16BE files
func getPreProcessedFileContent(path string) ([]byte, error) {
// Read the file into a []byte:
raw, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
// make an tranformer that converts MS-Win default to UTF8
win16be := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
// make a transformer that is like win16be, but abides by BOM
utf16bom := unicode.BOMOverride(win16be.NewDecoder())
// make a Reader that uses utf16bom
unicodeReader := transform.NewReader(bytes.NewReader(raw), utf16bom)
// decode and return data
decoded, err := ioutil.ReadAll(unicodeReader)
if err != nil {
return []byte(""), err
}
// replace newlines with unix style
fileContent := strings.Replace(string(decoded), "\r\n", "\n", -1)
return []byte(fileContent), nil
}
// getSystem32Dir returns the path to the "system32" directory on Windows
func getSystem32Dir() (string, error) {
system32Path, err := windows.GetSystemDirectory()
if err != nil {
return "", err
}
return system32Path, nil
}
// getSeceditData returns data from the "secedit.exe" utility
func getSeceditData() (SeceditData, error) {
var data SeceditData
// Get the path to the system32 directory
system32Dir, err := getSystem32Dir()
if err != nil {
return data, fmt.Errorf("path to system32 could not be determined: %w", err)
}
// Build the fullpath to the "secedit.exe" executable
seceditPath := filepath.Join(system32Dir, "secedit.exe")
// Get temporary directory
cacheDir, err := os.UserCacheDir()
if err != nil {
return data, fmt.Errorf("get UserCacheDir failed: %s", err)
}
// Create temporary directory
tempDir, err := ioutil.TempDir(cacheDir, "secedit-")
if err != nil {
return data, fmt.Errorf("failed to create temporary directory: %w", err)
}
defer os.RemoveAll(tempDir)
// Execute "secedit.exe" to export the current security configuration
outputInfPath := filepath.Join(tempDir, "output.inf")
cmd := exec.Command(seceditPath, "/export", "/cfg", outputInfPath)
if err := cmd.Run(); err != nil {
return data, fmt.Errorf("failed to execute secedit.exe: %w", err)
}
// Read the exported file
fileContent, err := getPreProcessedFileContent(outputInfPath)
if err != nil {
return data, fmt.Errorf("failed to preprocess .inf file: %w", err)
}
// Load the .inf file content
cfg, err := ini.Load(fileContent)
if err != nil {
fmt.Printf("Error: %v\n", err)
return data, err
}
// Parse System Access section
if systemAccessSection := cfg.Section("System Access"); systemAccessSection != nil {
data.SystemAccess.MinimumPasswordAge = systemAccessSection.Key("MinimumPasswordAge").String()
data.SystemAccess.MaximumPasswordAge = systemAccessSection.Key("MaximumPasswordAge").String()
data.SystemAccess.MinimumPasswordLength = systemAccessSection.Key("MinimumPasswordLength").String()
data.SystemAccess.PasswordComplexity = systemAccessSection.Key("PasswordComplexity").String()
data.SystemAccess.PasswordHistorySize = systemAccessSection.Key("PasswordHistorySize").String()
data.SystemAccess.LockoutBadCount = systemAccessSection.Key("LockoutBadCount").String()
data.SystemAccess.ResetLockoutCount = systemAccessSection.Key("ResetLockoutCount").String()
data.SystemAccess.LockoutDuration = systemAccessSection.Key("LockoutDuration").String()
data.SystemAccess.RequireLogonToChangePassword = systemAccessSection.Key("RequireLogonToChangePassword").String()
data.SystemAccess.ForceLogoffWhenHourExpire = systemAccessSection.Key("ForceLogoffWhenHourExpire").String()
data.SystemAccess.NewAdministratorName = systemAccessSection.Key("NewAdministratorName").String()
data.SystemAccess.NewGuestName = systemAccessSection.Key("NewGuestName").String()
data.SystemAccess.ClearTextPassword = systemAccessSection.Key("ClearTextPassword").String()
data.SystemAccess.LSAAnonymousNameLookup = systemAccessSection.Key("LSAAnonymousNameLookup").String()
data.SystemAccess.EnableAdminAccount = systemAccessSection.Key("EnableAdminAccount").String()
data.SystemAccess.EnableGuestAccount = systemAccessSection.Key("EnableGuestAccount").String()
}
// Parse Event Audit section
if eventAuditSection := cfg.Section("Event Audit"); eventAuditSection != nil {
data.EventAudit.AuditSystemEvents = eventAuditSection.Key("AuditSystemEvents").String()
data.EventAudit.AuditLogonEvents = eventAuditSection.Key("AuditLogonEvents").String()
data.EventAudit.AuditObjectAccess = eventAuditSection.Key("AuditObjectAccess").String()
data.EventAudit.AuditPrivilegeUse = eventAuditSection.Key("AuditPrivilegeUse").String()
data.EventAudit.AuditPolicyChange = eventAuditSection.Key("AuditPolicyChange").String()
data.EventAudit.AuditAccountManage = eventAuditSection.Key("AuditAccountManage").String()
data.EventAudit.AuditProcessTracking = eventAuditSection.Key("AuditProcessTracking").String()
data.EventAudit.AuditDSAccess = eventAuditSection.Key("AuditDSAccess").String()
data.EventAudit.AuditAccountLogon = eventAuditSection.Key("AuditAccountLogon").String()
}
// Parse Privilege Rights section
if privilegeRightsSection := cfg.Section("Privilege Rights"); privilegeRightsSection != nil {
data.PrivilegeRights.SeNetworkLogonRight = getGroupNames(privilegeRightsSection.Key("SeNetworkLogonRight").String())
data.PrivilegeRights.SeBackupPrivilege = getGroupNames(privilegeRightsSection.Key("SeBackupPrivilege").String())
data.PrivilegeRights.SeChangeNotifyPrivilege = getGroupNames(privilegeRightsSection.Key("SeChangeNotifyPrivilege").String())
data.PrivilegeRights.SeSystemtimePrivilege = getGroupNames(privilegeRightsSection.Key("SeSystemtimePrivilege").String())
data.PrivilegeRights.SeCreatePagefilePrivilege = getGroupNames(privilegeRightsSection.Key("SeCreatePagefilePrivilege").String())
data.PrivilegeRights.SeDebugPrivilege = getGroupNames(privilegeRightsSection.Key("SeDebugPrivilege").String())
data.PrivilegeRights.SeRemoteShutdownPrivilege = getGroupNames(privilegeRightsSection.Key("SeRemoteShutdownPrivilege").String())
data.PrivilegeRights.SeAuditPrivilege = getGroupNames(privilegeRightsSection.Key("SeAuditPrivilege").String())
data.PrivilegeRights.SeIncreaseQuotaPrivilege = getGroupNames(privilegeRightsSection.Key("SeIncreaseQuotaPrivilege").String())
data.PrivilegeRights.SeIncreaseBasePriorityPrivilege = getGroupNames(privilegeRightsSection.Key("SeIncreaseBasePriorityPrivilege").String())
data.PrivilegeRights.SeLoadDriverPrivilege = getGroupNames(privilegeRightsSection.Key("SeLoadDriverPrivilege").String())
data.PrivilegeRights.SeDenyBatchLogonRight = getGroupNames(privilegeRightsSection.Key("SeDenyBatchLogonRight").String())
data.PrivilegeRights.SeDenyServiceLogonRight = getGroupNames(privilegeRightsSection.Key("SeDenyServiceLogonRight").String())
data.PrivilegeRights.SeBatchLogonRight = getGroupNames(privilegeRightsSection.Key("SeBatchLogonRight").String())
data.PrivilegeRights.SeServiceLogonRight = getGroupNames(privilegeRightsSection.Key("SeServiceLogonRight").String())
data.PrivilegeRights.SeInteractiveLogonRight = getGroupNames(privilegeRightsSection.Key("SeInteractiveLogonRight").String())
data.PrivilegeRights.SeSecurityPrivilege = getGroupNames(privilegeRightsSection.Key("SeSecurityPrivilege").String())
data.PrivilegeRights.SeSystemEnvironmentPrivilege = getGroupNames(privilegeRightsSection.Key("SeSystemEnvironmentPrivilege").String())
data.PrivilegeRights.SeProfileSingleProcessPrivilege = getGroupNames(privilegeRightsSection.Key("SeProfileSingleProcessPrivilege").String())
data.PrivilegeRights.SeSystemProfilePrivilege = getGroupNames(privilegeRightsSection.Key("SeSystemProfilePrivilege").String())
data.PrivilegeRights.SeAssignPrimaryTokenPrivilege = getGroupNames(privilegeRightsSection.Key("SeAssignPrimaryTokenPrivilege").String())
data.PrivilegeRights.SeRestorePrivilege = getGroupNames(privilegeRightsSection.Key("SeRestorePrivilege").String())
data.PrivilegeRights.SeShutdownPrivilege = getGroupNames(privilegeRightsSection.Key("SeShutdownPrivilege").String())
data.PrivilegeRights.SeTakeOwnershipPrivilege = getGroupNames(privilegeRightsSection.Key("SeTakeOwnershipPrivilege").String())
data.PrivilegeRights.SeDenyNetworkLogonRight = getGroupNames(privilegeRightsSection.Key("SeDenyNetworkLogonRight").String())
data.PrivilegeRights.SeDenyInteractiveLogonRight = getGroupNames(privilegeRightsSection.Key("SeDenyInteractiveLogonRight").String())
data.PrivilegeRights.SeUndockPrivilege = getGroupNames(privilegeRightsSection.Key("SeUndockPrivilege").String())
data.PrivilegeRights.SeManageVolumePrivilege = getGroupNames(privilegeRightsSection.Key("SeManageVolumePrivilege").String())
data.PrivilegeRights.SeRemoteInteractiveLogonRight = getGroupNames(privilegeRightsSection.Key("SeRemoteInteractiveLogonRight").String())
data.PrivilegeRights.SeImpersonatePrivilege = getGroupNames(privilegeRightsSection.Key("SeImpersonatePrivilege").String())
data.PrivilegeRights.SeCreateGlobalPrivilege = getGroupNames(privilegeRightsSection.Key("SeCreateGlobalPrivilege").String())
data.PrivilegeRights.SeIncreaseWorkingSetPrivilege = getGroupNames(privilegeRightsSection.Key("SeIncreaseWorkingSetPrivilege").String())
data.PrivilegeRights.SeTimeZonePrivilege = getGroupNames(privilegeRightsSection.Key("SeTimeZonePrivilege").String())
data.PrivilegeRights.SeCreateSymbolicLinkPrivilege = getGroupNames(privilegeRightsSection.Key("SeCreateSymbolicLinkPrivilege").String())
data.PrivilegeRights.SeDelegateSessionUserImpersonatePrivilege = getGroupNames(privilegeRightsSection.Key("SeDelegateSessionUserImpersonatePrivilege").String())
}
return data, nil
}
// Best effor helper to extract the group names from an input string
func getGroupNames(input string) string {
var output string
// remove global occurences of * character
input = strings.ReplaceAll(input, "*", "")
// split input by comma
groups := strings.Split(input, ",")
for _, group := range groups {
userGroup, _ := user.LookupGroupId(group)
if userGroup != nil && len(userGroup.Name) > 0 {
output += userGroup.Name + ","
} else {
output += group + ","
}
}
return output
}
// containsAny checks if any of the given substrings are present in the input string
// It returns true if at least one substring is found, otherwise it returns false
func containsAny(input string, substrings []string) bool {
for _, substring := range substrings {
if strings.Contains(input, substring) {
return true
}
}
return false
}
// contains checks if the given substring is present in the input string.
// It returns true if the substring is found, otherwise it returns false.
func contains(input string, substring string) bool {
return containsAny(input, []string{substring})
}
// ParseAuditOutput parses the output of the auditpol.exe command
func parseAuditOutput(input string) ([]AuditItem, error) {
// expected items per line
const expectedItemsPerLine = 6
// parse the CSV string into a slice of AuditItem structs
reader := csv.NewReader(strings.NewReader(input))
reader.FieldsPerRecord = expectedItemsPerLine
// read all lines
lines, err := reader.ReadAll()
if err != nil {
return nil, err
}
// parse the CSV lines into AuditItem structs
var auditItems []AuditItem
for i, line := range lines {
// Check if the line has the expected number of items
if len(line) < expectedItemsPerLine {
return nil, fmt.Errorf("invalid line at index %d", i)
}
// Skip header
if i == 0 {
continue
}
// Parse the line
item := AuditItem{
Subcategory: line[2],
GUID: line[3],
NoAuditing: contains(line[4], "No Auditing"),
Success: contains(line[4], "Success"),
Failure: contains(line[4], "Failure"),
Raw: line[4],
}
auditItems = append(auditItems, item)
}
return auditItems, nil
}
// getAuditItems returns a slice of AuditItem structs
func getAuditItems() ([]AuditItem, error) {
// Get the path to the "system32" directory
system32Dir, err := getSystem32Dir()
if err != nil {
return nil, fmt.Errorf("path to system32 could not be determined: %w", err)
}
// Build the fullpath to the "auditpol.exe" executable
auditpolPath := filepath.Join(system32Dir, "auditpol.exe")
cmd := exec.Command(auditpolPath, "/get", "/category:*", "/r")
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
// Execute the auditpol command
err = cmd.Run()
if err != nil {
return nil, fmt.Errorf("command execution failed: %v, %s", err, stderr.String())
}
// Parse the output
auditItems, err := parseAuditOutput(stdout.String())
if err != nil {
return nil, fmt.Errorf("parsing output failed: %v", err)
}
return auditItems, nil
}
// Register the CIS items command handlers
func registerCommandsHandlers() {
// initialize the commands handlers map
commandsInit.Do(func() {
commandHandlers = make(map[string]CommandHandler)
registerCommandHandler("1.2.1", handler_cis_1_2_1)
registerCommandHandler("1.2.2", handler_cis_1_2_2)
registerCommandHandler("1.2.3", handler_cis_1_2_3)
registerCommandHandler("2.2.4", handler_cis_2_2_4)
registerCommandHandler("2.2.6", handler_cis_2_2_6)
registerCommandHandler("2.2.9", handler_cis_2_2_9)
registerCommandHandler("2.2.17", handler_cis_2_2_17)
registerCommandHandler("2.2.18", handler_cis_2_2_18)
registerCommandHandler("2.2.28", handler_cis_2_2_28)
registerCommandHandler("2.2.29", handler_cis_2_2_29)
registerCommandHandler("2.2.33", handler_cis_2_2_33)
registerCommandHandler("2.2.35", handler_cis_2_2_35)
registerCommandHandler("2.2.36", handler_cis_2_2_36)
registerCommandHandler("2.2.38", handler_cis_2_2_38)
registerCommandHandler("2.3.10.1", handler_cis_2_3_10_1)
registerCommandHandler("2.3.11.6", handler_cis_2_3_11_6)
registerCommandHandler("17.5.1", handler_cis_17_5_1)
registerCommandHandler("17.5.2", handler_cis_17_5_2)
registerCommandHandler("17.5.3", handler_cis_17_5_3)
registerCommandHandler("17.5.4", handler_cis_17_5_4)
registerCommandHandler("17.5.5", handler_cis_17_5_5)
registerCommandHandler("17.5.6", handler_cis_17_5_6)
})
}
// registerCommandHandler registers a new command handler for the given command
func registerCommandHandler(command string, handler CommandHandler) {
commandHandlers[command] = handler
}
// Helper to access the command handlers map
func getValueCisItem(item string) (string, error) {
var output string
var err error
if handler, exists := commandHandlers[item]; exists {
output, err = handler()
if err != nil {
return "", fmt.Errorf("cis command handler err: %v", err)
}
}
return output, nil
}
// getAuditItem helps to access audit array
func getAuditItem(subcategory string) (string, error) {
var output string
// Getting audit items
items, err := getAuditItems()
if err != nil {
return "", err
}
// Find the item and save raw content if present
for _, item := range items {
if item.Subcategory == subcategory {
output = item.Raw
break
}
}
return output, nil
}
// Command handler for CIS item 1.2.1
func handler_cis_1_2_1() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.SystemAccess.LockoutDuration, nil
}
// Command handler for CIS item 1.2.2
func handler_cis_1_2_2() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.SystemAccess.LockoutBadCount, nil
}
// Command handler for CIS item 1.2.3
func handler_cis_1_2_3() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.SystemAccess.ResetLockoutCount, nil
}
// Command handler for CIS item 2.2.4
func handler_cis_2_2_4() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeIncreaseQuotaPrivilege, nil
}
// Command handler for CIS item 2.2.6
func handler_cis_2_2_6() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeRemoteInteractiveLogonRight, nil
}
// Command handler for CIS item 2.2.9
func handler_cis_2_2_9() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeTimeZonePrivilege, nil
}
// Command handler for CIS item 2.2.17
func handler_cis_2_2_17() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeDenyBatchLogonRight, nil
}
// Command handler for CIS item 2.2.18
func handler_cis_2_2_18() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeDenyServiceLogonRight, nil
}
// Command handler for CIS item 2.2.28
func handler_cis_2_2_28() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeBatchLogonRight, nil
}
// Command handler for CIS item 2.2.29
func handler_cis_2_2_29() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeServiceLogonRight, nil
}
// Command handler for CIS item 2.2.33
func handler_cis_2_2_33() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeManageVolumePrivilege, nil
}
// Command handler for CIS item 2.2.35
func handler_cis_2_2_35() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeSystemProfilePrivilege, nil
}
// Command handler for CIS item 2.2.36
func handler_cis_2_2_36() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeAssignPrimaryTokenPrivilege, nil
}
// Command handler for CIS item 2.2.38
func handler_cis_2_2_38() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.PrivilegeRights.SeShutdownPrivilege, nil
}
// Command handler for CIS item 2.3.10.1
func handler_cis_2_3_10_1() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.SystemAccess.LSAAnonymousNameLookup, nil
}
// Command handler for CIS item 2.3.11.6
func handler_cis_2_3_11_6() (string, error) {
data, err := getSeceditData()
if err != nil {
return "", err
}
return data.SystemAccess.ForceLogoffWhenHourExpire, nil
}
// Command handler for CIS item 17.5.1
func handler_cis_17_5_1() (string, error) {
output, err := getAuditItem("Account Lockout")
if err != nil {
return "", err
}
return output, nil
}
// Command handler for CIS item 17.5.2
func handler_cis_17_5_2() (string, error) {
output, err := getAuditItem("Group Membership")
if err != nil {
return "", err
}
return output, nil
}
// Command handler for CIS item 17.5.3
func handler_cis_17_5_3() (string, error) {
output, err := getAuditItem("Logoff")
if err != nil {
return "", err
}
return output, nil
}
// Command handler for CIS item 17.5.4
func handler_cis_17_5_4() (string, error) {
output, err := getAuditItem("Logon")
if err != nil {
return "", err
}
return output, nil
}
// Command handler for CIS item 17.5.5
func handler_cis_17_5_5() (string, error) {
output, err := getAuditItem("Other Logon/Logoff Events")
if err != nil {
return "", err
}
return output, nil
}
// Command handler for CIS item 17.5.6
func handler_cis_17_5_6() (string, error) {
output, err := getAuditItem("Special Logon")
if err != nil {
return "", err
}
return output, nil
}
@@ -0,0 +1,92 @@
//go:build windows
// +build windows
package cisaudit
import (
"runtime"
"testing"
"github.com/osquery/osquery-go/plugin/table"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
func TestGenerateItemNotPresent(t *testing.T) {
ctx := context.Background()
queryContext := table.QueryContext{
Constraints: make(map[string]table.ConstraintList),
}
result, err := Generate(ctx, queryContext)
assert.Nil(t, err)
assert.Equal(t, len(result), 1)
assert.Empty(t, result[0]["item"])
assert.Empty(t, result[0]["value"])
}
func TestGenerateItemConstrainIsPresentAndResponseMaintainsValue(t *testing.T) {
ctx := context.Background()
queryContext := table.QueryContext{
Constraints: map[string]table.ConstraintList{
"item": {
Constraints: []table.Constraint{
{
Operator: table.OperatorEquals,
Expression: "value",
},
},
},
},
}
result, err := Generate(ctx, queryContext)
assert.Nil(t, err)
assert.Equal(t, len(result), 1)
assert.Equal(t, result[0]["item"], "value")
}
func TestGenerateItemInvalidInput(t *testing.T) {
ctx := context.Background()
queryContext := table.QueryContext{
Constraints: map[string]table.ConstraintList{
"item": {
Constraints: []table.Constraint{
{
Operator: table.OperatorEquals,
Expression: "9.9.9.9.9.9",
},
},
},
},
}
result, err := Generate(ctx, queryContext)
assert.Nil(t, err)
assert.Equal(t, len(result), 1)
assert.Empty(t, result[0]["value"])
}
func TestGenerateItemValid(t *testing.T) {
ctx := context.Background()
queryContext := table.QueryContext{
Constraints: map[string]table.ConstraintList{
"item": {
Constraints: []table.Constraint{
{
Operator: table.OperatorEquals,
Expression: "1.2.1",
},
},
},
},
}
_, err := Generate(ctx, queryContext)
if runtime.GOOS == "windows" {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
}
}
+2
View File
@@ -3,6 +3,7 @@
package table
import (
"github.com/fleetdm/fleet/v4/orbit/pkg/table/cis_audit"
"github.com/fleetdm/fleet/v4/orbit/pkg/table/mdm"
"github.com/osquery/osquery-go"
"github.com/osquery/osquery-go/plugin/table"
@@ -12,5 +13,6 @@ func PlatformTables() []osquery.OsqueryPlugin {
return []osquery.OsqueryPlugin{
// Fleet tables
table.NewPlugin("mdm_bridge", mdmbridge.Columns(), mdmbridge.Generate),
table.NewPlugin("cis_audit", cisaudit.Columns(), cisaudit.Generate),
}
}
+25
View File
@@ -28064,6 +28064,31 @@
"url": "https://fleetdm.com/tables/mdm_bridge",
"fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/mdm_bridge.yml"
},
{
"name": "cis_audit",
"platforms": [
"windows"
],
"description": "Enables querying CIS item values.",
"columns": [
{
"name": "item",
"type": "text",
"required": false,
"description": "Contains the input CIS item to query. If empty, no CIS item is queried."
},
{
"name": "value",
"type": "text",
"required": false,
"description": "Contains the value for the queried CIS item."
}
],
"notes": "This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer).",
"evented": false,
"url": "https://fleetdm.com/tables/cis_audit",
"fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/cis_audit.yml"
},
{
"name": "munki_installs",
"notes": "This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer).<p> Code based on work by [Kolide](https://github.com/kolide/launcher).",
+15
View File
@@ -0,0 +1,15 @@
name: cis_audit
platforms:
- windows
description: Enables querying CIS items values.
columns:
- name: item
type: text
required: false
description: Contains the input CIS item to query. If empty, no CIS item is queried.
- name: value
type: text
required: false
description: Contains the value for the queried CIS item.
notes: This table is not a core osquery table. It is included as part of [Fleetd](https://fleetdm.com/docs/using-fleet/orbit), the osquery manager from Fleet. Fleetd can be built with [fleetctl](https://fleetdm.com/docs/using-fleet/adding-hosts#osquery-installer).
evented: false