FIND_CMD

This policy item is used to build an equivalent find command to send to the target. Building the command in this method allows for discovery of partitions, iteration of user homes, and control over timeouts.

The advantage of using FIND_CMD over a CMD_EXEC is to use the Compliance Find Command Timeout feature, use uploaded inclusion/exclusions paths, and uploaded exclusion file system types.

Although there are many supported tags, the usage is practical and straight forward.

Usage

<custom_item>

type : FIND_CMD

description : ["description"]

find_name : ["filename"] || ["filename"]

not_find_name : ["filename"] || ["filename"]

target : ["path"] || ["path"]

find_type : ["type"] || ["type"]

exec : ["command to pass to exec"]

regex : ["expression to reduce results"]

expect : ["expression to evaluate against"]

not_expect : ["expression to evaluate against"]

find_context : ["selinux context"]

not_find_context : ["selinux context"]

ctime : ["integer"]

not_ctime : ["integer"]

gid : ["integer"] || ["integer"]

not_gid : ["integer"] || ["integer"]

group : ["groupname"] || ["groupname"]

not_group : ["groupname"] || ["groupname"]

match_all : [YES|NO]

match_case : [YES|NO]

maxdepth : ["integer"]

mindepth : ["integer"]

mtime : ["integer"]

not_mtime : ["integer"]

nogroup : [YES|NO]

nouser : [YES|NO]

perm : ["permission flags"]

not_perm : ["permission flags"]

print : [YES|NO]

size : ["integer"]

not_size : ["integer"]

uid : ["integer"] || ["integer"]

not_uid : ["integer"] || ["integer"]

use_valid_shells : [YES|NO]

user : ["username"] || ["username"]

not_user : "root" || "adm"

verbose : [YES|NO]

</custom_item>

Building a find command

The listed tags relate to named predicates that find supports. Not all predicates are supported on all operating systems, therefore the predicates are tested on the target to verify if they are supported before running. Review the manual pages for find on the target operating system for more information about supported predicates.

The tags are used to build a find command in a series of AND logic.

For example, the following audit check:

<custom_item>

type : FIND_CMD

description : "Find all items owned by root, group root, and mask /133"

user : "root"

group : "root"

perm : "/133"

target : "/testpath"

</custom_item>

Produces the following command:

find /testpath \( -user root \) \( -group root \) \( -perm /133 \)

Each separate section within an escaped parenthesis is evaluated as an AND. In this example the find command is attempting to identify all files that are user root, AND, group root, AND, matches the permission mask of /133.

Many of the listed tags support more than one entry with the double pipe || syntax. Taking the example above, if the user tag is expanded like the following:

<custom_item>

type : FIND_CMD

description : "Find all items owned by root or auditor, group root, and mask /133"

user : "root" || "auditor"

group : "root"

perm : "/133"

target : "/testpath"

</custom_item>

This then produces the following command:

find /testpath \( -user root -o -user auditor \) \( -group root \) \( -perm /133 \)

Tags with "not_"

Any tag starting with not_ adds an exclamation point before the predicate. This indicates that find should list results that are "not" what is specified.

For example the following audit check:

<custom_item>

type : FIND_CMD

description : "Find all items not owned by root:root and mask /133"

not_user : "root"

not_group : "root"

target : "/root"

</custom_item>

Produces the following command:

find /root \( ! -user root \) \( ! -group root \)

Tags

The following table describes the optional tags available for use with FIND_CMD.

Name Description Example
target

This tag specifies the target path or paths to search within. If this tag is not specified, the root path of / is searched by default. Multiple options can be specified with the double pipe ||.

If a tilde ~ is specified, all user home paths are searched. The tilde ~ can be combined with use_valid_shells. See that section for more details.

If no target is specified, or only / is present, FIND_CMD returns the output of the targets mount command. All mount points that have /dev are searched, and the results are compiled into a single output.

  • target : "/"

  • target : "~"

  • target : "/home/user1" || "/home/user2"

find_name and not_find_name These tags add the -name predicate. Multiple options can be specified with the double pipe ||.
  • find_name : ".rhosts"

  • find-name : "testfile1" || "testfile2"

find_type

This tag adds the -type predicate.

If this tag is not present, the returned results are the default behavior of find for that target operating system.

This tag is validated against ^[bcdpflsD]$.

Note: Review the target operating systems manual pages for more information on the type predicate options and the supported values.

Multiple options can be specified with the double pipe ||.

  • find_type : "f"

  • find_type : "f" || "d"

  • find_type : "f" || "d" || "s"

exec

This tag adds the -exec predicate.

This tag is always added to the end of the built command.

If this tag is not present, the returned results are the default behavior of find for that target operating system.

This tag always requires '{}' \\; at the end in this exact format.

This tag is deliberately limited in scope to prevent nefarious commands.

This tag supports the following commands and is validated against the regex shown:

  • ls -[a-zA-Z]+ '{}' \\;

  • file '{}' \\;

    Note: Optionally, the --mime argument may be added after file (for example, file --mime '{}' \\;).

  • grep -il "\/([A-Za-z0-9_\-]+\/?)+" '{}' \\;

  • stat -c "[%a-zA-Z ]+" '{}' \\;

  • runat '{}' ls -[a-zA-Z]+ \\;

    Note: This is used in Solaris operating systems.

Review the target operating systems manual pages for each utility for additional information on the supported arguments.

 
regex

This tag is used to reduce, select, or tailor the output to a smaller subset of results for evaluation.

This tag is not sent to the target and is only used to modify returned output.

This tag can be modified with the match_case option. See that section for more details.

  • regex : "^root"

  • regex : "^644"

expect and not_expect

Only one tag is supported at a time and they are mutually exclusive.

expect is a positive evaluation.

All returned results must match the contents.

not_expect is a negative evaluation.

All returned results must not match the contents.

The evaluation can be modified using the match_case and match_all options. See those sections for more details.

 
ctime and not_ctime

This tag adds the -ctime predicate.

This tag is validated against ^[-+]?\d+$.

  • ctime : "+1"

  • ctime : "-5"

  • not_ctime : "+1"

mtime and not_mtime

This tag adds the -mtime predicate.

This tag is validated against ^[-+]?\d+$.

  • mtime : "+1"

  • mtime : "-5"

  • not_mtime : "+1"

context and not_context

This tag adds the -context predicate.

This tag is only supported on target operating systems actively running SELinux.

This tag is validated against ^[:a-z0-9_\*]+$.

  • find_context : "*:unlabeled_t:*"

  • find_context : "*:device_t:*"

nogroup and nouser

These tags add one of:

  • -nouser

  • -nogroup

These are used to find files that have no known user and/or group

These tags are mutually exclusive with user, group, uid, or gid.

When both tags are specified, the predicates are combined: \( -nouser -o -nogroup \)

  • nouser : YES

  • nogroup : YES

gid and not_gid

This tag adds the -gid predicate.

Multiple options can be specified with the double pipe ||.

This tag is validated against ^[-+]?\d+$.

  • gid : "0"

  • not_gid : "1000"

  • gid : "1000" || "1002"

uid and not_uid

This tag adds the -uid predicate.

Multiple options can be specified with the double pipe ||.

This tag is validated against ^[-+]?\d+$.

  • uid : "0"

  • not_uid : "1000"

  • uid : "1000" || "1002"

group and not_group

This tag adds the -group predicate.

Multiple options can be specified with the double pipe ||.

This tag is validated against ^[\w.][\w.-]{0,30}[\w.$-]?$.

  • group : "root"

  • not_group : "auditor"

  • group : "root" || "adm"

user and not_user

This tag adds the -user predicate.

Multiple options can be specified with the double pipe ||.

This tag is validated against ^[\w.][\w.-]{0,30}[\w.$-]?$.

  • user : "root"

  • not_user : "auditor"

  • user : "root" || "adm"

perm and not_perm

This tag adds the -perm predicate.

Multiple options can be specified with the double pipe ||.

This tag is validated against ^[-+\/]?[0-9A-Za-z+=,]+$.

  • perm : "/133"

  • not_perm : "+644"

print

This tag adds the -print predicate.

This tag is useful when combined with other options that may not return the file name by default, such as xattr and runat.

If this tag is combined with exec and ls, duplicate results are likely.

If this tag is missing, the default is NO.

  • print : YES

  • print : NO

size and not_size

This tag adds the -size predicate.

Multiple options can be specified with the double pipe ||.

This tag is validated against ^\d+[ckMGTP]?$.

  • size : "0"

  • not_size : "100M"

match_all

This tag is used for evaluation purposes only.

If this tag is missing, the default is YES.

Enabling this tag requires all returned output to match the expect/not_expect evaluation.

  • match_all : YES

  • match_all : NO

match_case

This tag is used for evaluation purposes only.

If this tag is missing, the default is YES.

Enabling this tag requires case sensitivity for both the regex and expect/not_expect tags.

  • match_case : YES

  • match_case : NO

maxdepth

This tag adds the -maxdepth predicate.

This tag is validated against ^\d+$.

  • maxdepth : "1"

  • maxdepth : "5"

mindepth

This tag adds the -mindepth predicate.

This tag is validated against ^\d+$.

  • mindepth : "1"

  • mindepth : "5"

use_valid_shells

This tag is used in conjunction with a target tag value of ~.

If this tag is missing, the default is YES.

Enabling this tag causes the following:

  • Read in the values from /etc/shells.

  • Use those values to filter any user that does not have a valid login shell assigned to them.

This tag is used to control whether all users or only login-enabled user homes should be scanned.

This is useful for filtering out service accounts, or any account that may be assigned a shell variant of nologin or false.

target : "~"

use_valid_shells : NO

verbose

This tag is used to control how much output is returned in scan results

If this tag is missing, the default is NO.

Enabling verbose shows all passing results along with failing results.

A missing tag, or value of NO, only shows failing results.

Note: Enabling this tag should be used with caution as very large result sets may be returned.

  • verbose : YES

  • verbose : NO

xattr

This tag adds the -xattr predicate.

This tag is used with Solaris audits.

When combined with an exec of runat '{}' ls -al \\;, this shows results of the extended attributes found within the file.

If this tag is missing, the default is NO.

  • xattr : YES

  • xattr : NO

find_acl

This tag adds the -acl predicate.

This tag is used with Solaris audits.

If this tag is missing, the default is NO.

  • find_acl : YES

  • find_acl : NO

Caveats

Custom FIND_CMD checks should be created carefully and based on target testing. The potential to create a check that returns large output is possible. Best practice guidance would be to create a check that has the most number of options possible to narrow the scope.

Space separated values are not permitted. Any tag that supports multiple items must use the double pipe || syntax.

The resulting find command that is sent to the target has all errors redirected to /dev/null. This has the potential to produce false positive results if careful consideration of custom commands is not considered.

Example

1. On a target, the use of the -group predicate with an invalid group name returns an error:

# find / -group custom_group_name

find: cannot find -group name

2. If an audit check is created that uses the group or _not_group tag as such:

group : "custom_group_name"

3. The audit would return a PASSING result since no results can be produced.

Audit examples and their command equivalents

<custom_item>

type : FIND_CMD

description : "No .rhosts files found - user homes"

not_expect : ".+"

find_name : ".rhosts"

target : "~"

</custom_item>

The use of ~ for the target enumerates all user home paths. This check would produce the following example find commands, assuming the homes are /root and /home/audituser:

  • find /root -name .rhosts

  • find /home/audituser -name .rhosts

<custom_item>

type : FIND_CMD

description : "nogroup and nouser - full system"

not_expect : ".+"

exec : "ls -ld '{}' \\;"

nogroup : YES

nouser : YES

</custom_item>

With the target tag missing, all mounted /dev points are enumerated for scanning. This check would produce the following example find commands, assuming the mounted paths are /, /opt, and /var:

  • find / \( -nouser -o -nogroup \) -exec ls -ld '{}' \;

  • find /opt \( -nouser -o -nogroup \) -exec ls -ld '{}' \;

  • find /var \( -nouser -o -nogroup \) -exec ls -ld '{}' \;

<custom_item>

type : FIND_CMD

description : "everything in /root must be owned by user root"

not_expect : ".+"

exec : "stat -c \"%U %n\" '{}' \\;"

find_type : "f" || "d" || "s"

not_user : "root"

target : "/root"

</custom_item>

This would produce the following example find command:

  • find /root \( -type f -o -type d -type s \) ! -user root -exec stat -c "%U %n" '{}' \;