Creating custom SCA policies
An SCA policy looks like the following:
# Security Configuration Assessment
# Audit for UNIX systems
# Copyright (C) 2015, Wazuh Inc.
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License (version 2) as published by the FSF - Free Software
# Foundation
policy:
id: "unix_audit"
file: "sca_unix_audit.yml"
name: "System audit for Unix based systems"
description: "Guidance for establishing a secure configuration for Unix based systems."
references:
- https://www.ssh.com/ssh/
requirements:
title: "Check that the SSH service and password-related files are present on the system"
description: "Requirements for running the SCA scan against the Unix based systems policy."
condition: any
rules:
- 'f:$sshd_file'
- 'f:/etc/passwd'
- 'f:/etc/shadow'
variables:
$sshd_file: /etc/ssh/sshd_config
$pam_d_files: /etc/pam.d/common-password,/etc/pam.d/password-auth,/etc/pam.d/system-auth,/etc/pam.d/system-auth-ac,/etc/pam.d/passwd
checks:
- id: 3000
title: "SSH Hardening: Port should not be 22"
description: "The ssh daemon should not be listening on port 22 (the default value) for incoming connections."
rationale: "Changing the default port you may reduce the number of successful attacks from zombie bots, an attacker or bot doing port-scanning can quickly identify your SSH port."
remediation: "Change the Port option value in the sshd_config file."
compliance:
- pci_dss: ["2.2.4"]
- nist_800_53: ["CM.1"]
condition: all
rules:
- 'f:$sshd_file -> !r:^# && r:Port && !r:\s*\t*22$'
- id: 3001
title: "SSH Hardening: Protocol should be set to 2"
...
As shown in this example, policy files are comprised of four sections, although not all of them are required, as detailed in the Policy file Sections table.
Section |
Required |
---|---|
policy |
Yes |
requirements |
No |
variables |
No |
checks |
Yes |
Note
If the requirements aren't satisfied for a specific policy file, the scan for that file won't start.
Each section has its fields as described in the tables Policy section, Requirements section, Variables section, Checks section.
Field |
Mandatory |
Type |
Allowed values |
---|---|---|---|
title |
Yes |
String |
Any string |
description |
Yes |
String |
Any string |
condition |
Yes |
String |
Any string |
rules |
Yes |
Array of strings |
Any string |
Field |
Mandatory |
Type |
Allowed values |
---|---|---|---|
variable_name |
Yes |
Array of strings |
Any string |
Note
Fields id from policy and checks must be unique across policy files.
Variables
Variables are set in the variables section. Their names are preceded by $
. For instance,
$list_of_files: /etc/ssh/sshd_config,/etc/sysctl.conf,/var/log/dmesg
$list_of_folders: /etc,/var,/tmp
$program_name: apache2
Variables can be placed anywhere in the left part of the rule. Therefore, regarding the variables above, the following rules could be built:
f:$list_of_files -> r:^Content to be found
c:systemctl is-enabled $program_name -> r:^enabled
There is no limit on the number of variables to add within a rule.
Checks
Checks are the core of an SCA policy, as they describe the checks to be performed in the system. Each check is comprised by several fields as described in the table Checks section.
Check evaluation is governed by its rule result aggregation strategy, as set in its condition
field, and the results of
the evaluation of its rules.
Condition
The condition field specifies how rule results are aggregated in order to calculate the final value of a check. There are three options:
all
: the check will be evaluated as Passed if all of its rules are satisfied and as Failed as soon as one rule is not satisfied,any
: the check will be evaluated as Passed as soon as any of its rules is satisfied,none
: the check will be evaluated as Passed if none of its rules are satisfied and as Failed as soon as one rule is satisfied.
Special mention deserves how rules evaluated as Not applicable are treated by the aforementioned aggregators.
all
: If any rule returns Not applicable, and no rule returns Failed, the result will be Not applicable.any
: The check will be evaluated as Not applicable if no rule is evaluated as Passed and any returns Not applicable.none
: The check will be evaluated as Not applicable if no rule is evaluated as Passed and any returns Not applicable.
Condition \ Rule evaluation |
Passed |
Failed |
Not applicable |
Result |
---|---|---|---|---|
|
yes |
no |
no |
Passed |
|
* |
no |
yes |
Not applicable |
|
* |
yes |
* |
Failed |
|
yes |
* |
* |
Passed |
|
no |
yes |
no |
Failed |
|
no |
* |
yes |
Not applicable |
|
yes |
* |
* |
Failed |
|
no |
* |
yes |
Not applicable |
|
no |
yes |
no |
Passed |
* This result does not affect the final result.
Rules
Rules can check for the existence of files, directories, registry keys and values, running processes, and recursively test for the existence of files inside directories. When it comes to content checking, they are able to check for file contents, recursively check for the contents of files inside directories, command output and registry value data.
Abstractly, rules start with a location (and a type of location), that will be the target of the test, followed by the actual test specification. Such tests fall into two categories: existence and content checks.
There are five main types of rules as described below:
Type |
Character |
---|---|
File |
|
Directory |
|
Process |
|
Commands |
|
Registry (Windows Only) |
|
The operators for content checking are:
A whole rule can be negated using the operator not
, which is placed at the beginning of the rule.
not RULE
Example: not f:/some_file -> some_text
will fail if some_text is found within the contents of some_file.
By combining the aforementioned rule types and operators, both existence and content checking can be performed.
Note
Process rules only allow existence checks.
Command rules only allow content (output) checks.
Existence checking rules
Existence checks are created by setting rules without a content operator, the general form is as follows:
RULE_TYPE:target
Examples of existence checks:
f:/etc/sshd_config
checks the existence of file /etc/sshd_configd:/etc
checks the existence of directory /etcnot p:sshd
will test the presence of processes called sshd and fail if one is found.r:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa
checks for the existence of that key.r:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa -> LimitBlankPasswordUse
checks for the existence of value LimitBlankPasswordUse in the key.
Content checking rules
The general form of a rule testing for contents is as follows:
RULE_TYPE:target -> CONTENT_OPERATOR:value
Warning
The context of a content check is limited to a line.
Content checks are case-sensitive.
It is mandatory to respect the spaces around the
->
andcompare
separators.If the target of a rule that checks for contents does not exist, the result will be
Not applicable
as it could not be checked.
Content check operator results can be negated by adding a !
before then, for example:
f:/etc/ssh_config -> !r:PermitRootLogin
Warning
Be careful when negating content operators as that will make them evaluate as Passed for anything that does not match with the check specified.
For example rule `f:/etc/ssh_config -> !r:PermitRootLogin`
will be evaluated as Passed if it finds any line that does not contain PermitRootLogin
.
Content check operators can be chained using the operator &&
(AND) as follows:
f:/etc/ssh_config -> !r:^# && r:Protocol && r:2
This rule reads as Pass if there's a line whose first character is not "#" and contains "Protocol" and "2".
Warning
It is mandatory to respect the spaces around the
&&
operator.There's no particular order of evaluation between tests chained using the
&&
operator.
Examples of content checks:
systemctl is-enabled cups -> r:^enabled
checks that the output of the command contains a line starting by enabled.
f:$sshd_file -> n:^\s*MaxAuthTries\s*\t*(\d+) compare <= 4
checks that MaxAuthTries is less or equal to 4.
r:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa -> LimitBlankPasswordUse -> 1
checks that value of LimitBlankPasswordUse is 1.
Examples
The following sections cover each rule type, illustrating them with several examples. It is also recommended to check the actual policies and, for minimalistic although complete examples, the SCA test suite policies.
Rule syntax for files
Check that a file exists:
f:/path/to/file
Check that a file does not exist:
not f:/path/to/file
Check file contains (whole line literal match):
f:/path/to/file -> content
Check file contents against regex:
f:/path/to/file -> r:REGEX
Check a numeric value:
f:/path/to/file -> n:REGEX(\d+) compare <= Number
Rule syntax for directories
Check if a directory exists:
d:/path/to/directory
Check if a directory contains a file:
d:/path/to/directory -> file
Check if a directory contains files that match a regex:
d:/path/to/directory -> r:^files
Check files matching
file_name
for content:d:/path/to/directory -> file_name -> content
Rule syntax for processes
Check if a process is running
p:process_name
Check if a process is not running
not p:process_name
Rule syntax for commands
Check the output of a command
c:command -> output
Check the output of a command using regex
c:command -> r:REGEX
Check a numeric value
c:command -> n:REGEX_WITH_A_CAPTURE_GROUP compare >= number
Rule syntax for Windows Registry
Check if a registry exists
r:path/to/registry
Check if a registry key exists
r:path/to/registry -> key
Check registry key contents
r:path/to/registry -> key -> content
Composite rules
Check if there is a line that does not begin with
#
and containsPort 22
f:/etc/ssh/sshd_config -> !r:^# && r:Port\.+22
Check if there is no line that does not begin with
#
and containsPort 22
not f:/etc/ssh/sshd_config -> !r:^# && r:Port\.+22
Other examples
Check for file contents, whole line match:
f:/proc/sys/net/ipv4/ip_forward -> 1
Check if a file exists:
f:/proc/sys/net/ipv4/ip_forward
Check if a process is running:
p:avahi-daemon
Check value of registry:
r:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Netlogon\Parameters -> MaximumPasswordAge -> 0
Check if a directory contains files:
d:/home -> ^.mysql_history$
Check if a directory exists:
d:/etc/mysql
Check the running configuration of sshd for the maximum authentication tries allowed:
c:sshd -T -> !r:^\s*maxauthtries\s+4\s*$
Check if root is the only account with UID 0:
f:/etc/passwd -> !r:^# && !r:^root: && r:^\w+:\w+:0: