The audit system already collects login information for all users
and root. If the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following lines to a file with suffix .rules in the
directory /etc/audit/rules.d in order to watch for attempted manual
edits of files involved in storing logon events:
-w /var/log/faillog -p wa -k logins
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following lines to
/etc/audit/audit.rules file in order to watch for unattempted manual
edits of files involved in storing logon events:
-w /var/log/faillog -p wa -k logins
Rationale
Manual editing of these files may indicate nefarious activity, such
as an attacker attempting to remove evidence of an intrusion.
- name: Gather the package facts
package_facts:
manager: auto
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /var/log/faillog already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/var/log/faillog\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when:
- '"audit" in ansible_facts.packages'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key logins
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)logins$
patterns: '*.rules'
register: find_watch_key
when:
- '"audit" in ansible_facts.packages'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use /etc/audit/rules.d/logins.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/logins.rules
when:
- '"audit" in ansible_facts.packages'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- '"audit" in ansible_facts.packages'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/log/faillog in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /var/log/faillog -p wa -k logins
create: true
mode: '0640'
when:
- '"audit" in ansible_facts.packages'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /var/log/faillog already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/var/log/faillog\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when:
- '"audit" in ansible_facts.packages'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/log/faillog in /etc/audit/audit.rules
lineinfile:
line: -w /var/log/faillog -p wa -k logins
state: present
dest: /etc/audit/audit.rules
create: true
mode: '0640'
when:
- '"audit" in ansible_facts.packages'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
tags:
- CCE-92576-8
- audit_rules_login_events_faillog
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
Remediation - Shell Script
# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ] && rpm --quiet -q audit; then
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
files_to_inspect=()
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
files_to_inspect+=('/etc/audit/audit.rules')
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillog" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Define BRE whitespace class shortcut
sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "wa" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s#\($sp*-w$sp\+/var/log/faillog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w /var/log/faillog -p wa -k logins" >> "$audit_rules_file"
fi
done
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
files_to_inspect=()
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection.
readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/faillog" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/logins.rules' into list of files for inspection
key_rule_file="/etc/audit/rules.d/logins.rules"
# If the logins.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "^[\s]*-w[\s]+/var/log/faillog" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Define BRE whitespace class shortcut
sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/faillog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "wa" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s#\($sp*-w$sp\+/var/log/faillog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w /var/log/faillog -p wa -k logins" >> "$audit_rules_file"
fi
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi